Patchwork [RfC,08/11] spice: add qxl device

login
register
mail settings
Submitter Gerd Hoffmann
Date April 14, 2010, 9:55 a.m.
Message ID <1271238922-10008-9-git-send-email-kraxel@redhat.com>
Download mbox | patch
Permalink /patch/50143/
State New
Headers show

Comments

Gerd Hoffmann - April 14, 2010, 9:55 a.m.
---
 Makefile.target |    2 +-
 hw/hw.h         |   14 +
 hw/pc.c         |    8 +
 hw/qxl.c        | 1035 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/vga_int.h    |    2 +-
 qemu-spice.h    |    2 +
 sysemu.h        |    3 +-
 vl.c            |    4 +-
 8 files changed, 1066 insertions(+), 4 deletions(-)
 create mode 100644 hw/qxl.c
Blue Swirl - April 14, 2010, 4:52 p.m.
On 4/14/10, Gerd Hoffmann <kraxel@redhat.com> wrote:
>  +static inline void atomic_or(uint32_t *var, uint32_t add)
>  +{
>  +   __asm__ __volatile__ ("lock; orl %1, %0" : "+m" (*var) : "r" (add) : "memory");
>  +}

This will break on non-x86 hosts.

>  +static QXLInterface qxl_interface = {
>  +    .base.type               = SPICE_INTERFACE_QXL,
>  +    .base.description        = "qxl gpu",
>  +    .base.major_version      = SPICE_INTERFACE_QXL_MAJOR,
>  +    .base.minor_version      = SPICE_INTERFACE_QXL_MINOR,
>  +
>  +    .pci_vendor              = REDHAT_PCI_VENDOR_ID,
>  +    .pci_id                  = QXL_DEVICE_ID,
>  +    .pci_revision            = QXL_REVISION,
>  +
>  +    .attache_worker          = interface_attach_worker,
>  +    .set_compression_level   = interface_set_compression_level,
>  +    .set_mm_time             = interface_set_mm_time,
>  +
>  +    .get_init_info           = interface_get_init_info,
>  +    .get_command             = interface_get_command,
>  +    .req_cmd_notification    = interface_req_cmd_notification,
>  +    .has_command             = interface_has_command,
>  +    .release_resource        = interface_release_resource,
>  +    .get_cursor_command      = interface_get_cursor_command,
>  +    .req_cursor_notification = interface_req_cursor_notification,
>  +    .get_update_area         = interface_get_update_area,
>  +    .notify_update           = interface_notify_update,
>  +    .set_save_data           = interface_set_save_data,
>  +    .get_save_data           = interface_get_save_data,
>  +    .flush_resources         = interface_flush_resources,
>  +};

Could this (and other similar cases) be const?
Alexander Graf - April 14, 2010, 10:21 p.m.
On 14.04.2010, at 11:55, Gerd Hoffmann wrote:

> 
> +static inline void atomic_or(uint32_t *var, uint32_t add)
> +{
> +   __asm__ __volatile__ ("lock; orl %1, %0" : "+m" (*var) : "r" (add) : "memory");

I hope this is going away? I don't see why QXL should be coupled to x86 host and guest only.


Alex
Paolo Bonzini - April 14, 2010, 11:08 p.m.
On 04/14/2010 06:52 PM, Blue Swirl wrote:
> On 4/14/10, Gerd Hoffmann<kraxel@redhat.com>  wrote:
>>   +static inline void atomic_or(uint32_t *var, uint32_t add)
>>   +{
>>   +   __asm__ __volatile__ ("lock; orl %1, %0" : "+m" (*var) : "r" (add) : "memory");
>>   +}
>
> This will break on non-x86 hosts.

I'd just use __sync_fetch_and_or here.

Paolo
Blue Swirl - April 15, 2010, 4:47 p.m.
On 4/15/10, Paolo Bonzini <pbonzini@redhat.com> wrote:
> On 04/14/2010 06:52 PM, Blue Swirl wrote:
>
> > On 4/14/10, Gerd Hoffmann<kraxel@redhat.com>  wrote:
> >
> > >  +static inline void atomic_or(uint32_t *var, uint32_t add)
> > >  +{
> > >  +   __asm__ __volatile__ ("lock; orl %1, %0" : "+m" (*var) : "r" (add)
> : "memory");
> > >  +}
> > >
> >
> > This will break on non-x86 hosts.
> >
>
>  I'd just use __sync_fetch_and_or here.

And on environments without __sync_fetch_and_or? Where is that available?
Richard Henderson - April 15, 2010, 7:27 p.m.
On 04/15/2010 11:47 AM, Blue Swirl wrote:
> On 4/15/10, Paolo Bonzini<pbonzini@redhat.com>  wrote:
>> On 04/14/2010 06:52 PM, Blue Swirl wrote:
>>
>>> On 4/14/10, Gerd Hoffmann<kraxel@redhat.com>   wrote:
>>>
>>>>   +static inline void atomic_or(uint32_t *var, uint32_t add)
>>>>   +{
>>>>   +   __asm__ __volatile__ ("lock; orl %1, %0" : "+m" (*var) : "r" (add)
>> : "memory");
>>>>   +}
>>>>
>>>
>>> This will break on non-x86 hosts.
>>>
>>
>>   I'd just use __sync_fetch_and_or here.
>
> And on environments without __sync_fetch_and_or? Where is that available?

GCC will provide it for capable cpus.  So sparcv9 has it via CAS.

For less capable cpus, there may be cooperation with the system in
some way.  For instance, ARM, SH, and HPPA Linux kernels all provide
various mechanisms to implement atomic sequences.  The result continues
to be provided by GCC in the form of entry points in libgcc.so.

For less capable cpus with no system support... well, the program
itself needs to figure out what an appropriate response should be.
I haven't gone back to look at the context from which this snippet
was taken to know if a locally defined mutex would be adequate.

In either case, some configure tests that detect when various forms
of atomic operations are available in the host compiler would not
be amiss.


r~
Gerd Hoffmann - April 16, 2010, 8:08 a.m.
On 04/15/10 00:21, Alexander Graf wrote:
>
> On 14.04.2010, at 11:55, Gerd Hoffmann wrote:
>
>>
>> +static inline void atomic_or(uint32_t *var, uint32_t add)
>> +{
>> +   __asm__ __volatile__ ("lock; orl %1, %0" : "+m" (*var) : "r" (add) : "memory");
>
> I hope this is going away? I don't see why QXL should be coupled to x86 host and guest only.

Spice on !x86 (especially big endian boxes) isn't going to fly at the 
moment for a number of reasons.  That is considered a bug though and we 
want fix it long-term.  There is nothing fundamental which prevents qxl 
and spice from being used on -- say -- ppc.

cheers,
   Gerd

Patch

diff --git a/Makefile.target b/Makefile.target
index 842a489..57aa212 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -194,7 +194,7 @@  obj-i386-y += cirrus_vga.o apic.o ioapic.o piix_pci.o
 obj-i386-y += vmmouse.o vmport.o hpet.o
 obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o
 obj-i386-y += debugcon.o multiboot.o
-obj-i386-$(CONFIG_SPICE) += spice.o spice-input.o spice-display.o
+obj-i386-$(CONFIG_SPICE) += spice.o spice-input.o spice-display.o qxl.o
 
 # shared objects
 obj-ppc-y = ppc.o
diff --git a/hw/hw.h b/hw/hw.h
index 328b704..894cac7 100644
--- a/hw/hw.h
+++ b/hw/hw.h
@@ -508,6 +508,17 @@  extern const VMStateInfo vmstate_info_unused_buffer;
     .start        = (_start),                                        \
 }
 
+#define VMSTATE_VBUFFER_UINT32(_field, _state, _version, _test, _start, _field_size) { \
+    .name         = (stringify(_field)),                             \
+    .version_id   = (_version),                                      \
+    .field_exists = (_test),                                         \
+    .size_offset  = vmstate_offset_value(_state, _field_size, uint32_t),\
+    .info         = &vmstate_info_buffer,                            \
+    .flags        = VMS_VBUFFER|VMS_POINTER,                         \
+    .offset       = offsetof(_state, _field),                        \
+    .start        = (_start),                                        \
+}
+
 #define VMSTATE_BUFFER_UNSAFE_INFO(_field, _state, _version, _info, _size) { \
     .name       = (stringify(_field)),                               \
     .version_id = (_version),                                        \
@@ -713,6 +724,9 @@  extern const VMStateDescription vmstate_i2c_slave;
 #define VMSTATE_PARTIAL_VBUFFER(_f, _s, _size)                        \
     VMSTATE_VBUFFER(_f, _s, 0, NULL, 0, _size)
 
+#define VMSTATE_PARTIAL_VBUFFER_UINT32(_f, _s, _size)                        \
+    VMSTATE_VBUFFER_UINT32(_f, _s, 0, NULL, 0, _size)
+
 #define VMSTATE_SUB_VBUFFER(_f, _s, _start, _size)                    \
     VMSTATE_VBUFFER(_f, _s, 0, NULL, _start, _size)
 
diff --git a/hw/pc.c b/hw/pc.c
index 69e597f..8cde987 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -47,6 +47,7 @@ 
 #include "elf.h"
 #include "multiboot.h"
 #include "kvm.h"
+#include "qemu-spice.h"
 
 /* output Bochs bios info messages */
 //#define DEBUG_BIOS
@@ -945,6 +946,13 @@  static void pc_init1(ram_addr_t ram_size,
             pci_vmsvga_init(pci_bus);
         else
             fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __FUNCTION__);
+#ifdef CONFIG_SPICE
+    } else if (qxl_enabled) {
+        if (pci_enabled)
+            pci_create_simple(pci_bus, -1, "qxl");
+        else
+            fprintf(stderr, "%s: qxl: no PCI bus\n", __FUNCTION__);
+#endif
     } else if (std_vga_enabled) {
         if (pci_enabled) {
             pci_vga_init(pci_bus, 0, 0);
diff --git a/hw/qxl.c b/hw/qxl.c
new file mode 100644
index 0000000..ed7e975
--- /dev/null
+++ b/hw/qxl.c
@@ -0,0 +1,1035 @@ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <pthread.h>
+
+#include "qemu-common.h"
+#include "qemu-barrier.h"
+#include "qemu-spice.h"
+#include "qemu-timer.h"
+#include "qemu-queue.h"
+#include "monitor.h"
+#include "console.h"
+#include "sysemu.h"
+#include "pci.h"
+#include "vga_int.h"
+
+#include "spice-display.h"
+
+#undef SPICE_RING_PROD_ITEM
+#define SPICE_RING_PROD_ITEM(r, ret) {                                  \
+        typeof(r) start = r;                                            \
+        typeof(r) end = r + 1;                                          \
+        uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r);           \
+        typeof(&(r)->items[prod]) m_item = &(r)->items[prod];           \
+        if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \
+            abort();                                                    \
+        }                                                               \
+        ret = &m_item->el;                                              \
+    }
+
+#undef SPICE_RING_CONS_ITEM
+#define SPICE_RING_CONS_ITEM(r, ret) {                                  \
+        typeof(r) start = r;                                            \
+        typeof(r) end = r + 1;                                          \
+        uint32_t cons = (r)->cons & SPICE_RING_INDEX_MASK(r);           \
+        typeof(&(r)->items[cons]) m_item = &(r)->items[cons];           \
+        if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \
+            abort();                                                    \
+        }                                                               \
+        ret = &m_item->el;                                              \
+    }
+
+
+#define PANIC_ON(x) if ((x)) {                         \
+    printf("%s: PANIC %s failed\n", __FUNCTION__, #x); \
+    exit(-1);                                          \
+}
+
+enum qxl_mode {
+    QXL_MODE_UNDEFINED,
+    QXL_MODE_VGA,
+    QXL_MODE_NATIVE,
+};
+
+typedef struct PCIQXLDevice {
+    PCIDevice          pci;
+    SimpleSpiceDisplay ssd;
+    int                id;
+    enum qxl_mode      mode;
+    int                generation;
+
+    /* thread signaling */
+    pthread_t          main;
+    int                pipe[2];
+
+    /* ram pci bar */
+    QXLRam             *ram;
+    VGACommonState     vga;
+    int                num_free_res;
+    QXLReleaseInfo     *last_release;
+
+    /* rom pci bar */
+    QXLRom             shadow_rom;
+    QXLRom             *rom;
+    QXLModes           *modes;
+    uint32_t           rom_size;
+    uint64_t           rom_offset;
+
+    /* vram pci bar */
+    uint32_t           vram_size;
+    uint64_t           vram_offset;
+
+    /* io bar */
+    uint32_t           io_base;
+
+} PCIQXLDevice;
+
+#undef ALIGN
+#define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1))
+
+#define PIXEL_SIZE 0.2936875 //1280x1024 is 14.8" x 11.9" 
+
+#define QXL_MODE(_x, _y, _b, _o)                  \
+    {   .x_res = _x,                              \
+        .y_res = _y,                              \
+        .bits  = _b,                              \
+        .stride = (_x) * (_b) / 8,                \
+        .x_mili = PIXEL_SIZE * (_x),              \
+        .y_mili = PIXEL_SIZE * (_y),              \
+        .orientation = _o,                        \
+    }
+
+#define QXL_MODE_16_32(x_res, y_res, orientation) \
+    QXL_MODE(x_res, y_res, 16, orientation),      \
+    QXL_MODE(x_res, y_res, 32, orientation)
+
+#define QXL_MODE_EX(x_res, y_res)                 \
+    QXL_MODE_16_32(x_res, y_res, 0),              \
+    QXL_MODE_16_32(y_res, x_res, 1),              \
+    QXL_MODE_16_32(x_res, y_res, 2),              \
+    QXL_MODE_16_32(y_res, x_res, 3)
+
+static QXLMode qxl_modes[] = {
+    QXL_MODE_EX(640, 480),
+    QXL_MODE_EX(800, 600),
+    QXL_MODE_EX(832, 624),
+    QXL_MODE_EX(1024, 768),
+    QXL_MODE_EX(1152, 864),
+    QXL_MODE_EX(1152, 870),
+    QXL_MODE_EX(1280, 720),
+    QXL_MODE_EX(1280, 768),
+    QXL_MODE_EX(1280, 800),
+    QXL_MODE_EX(1280, 960),
+    QXL_MODE_EX(1280, 1024),
+    QXL_MODE_EX(1360, 768),
+    QXL_MODE_EX(1366, 768),
+    QXL_MODE_EX(1400, 1050),
+    QXL_MODE_EX(1440, 900),
+    QXL_MODE_EX(1600, 900),
+    QXL_MODE_EX(1600, 1200),
+    QXL_MODE_EX(1680, 1050),
+    QXL_MODE_EX(1920, 1080),
+#ifdef QXL_HIRES_MODES
+    QXL_MODE_EX(1920, 1200),
+    QXL_MODE_EX(1920, 1440),
+    QXL_MODE_EX(2048, 1536),
+    QXL_MODE_EX(2560, 1600),
+    QXL_MODE_EX(2560, 2048),
+    QXL_MODE_EX(2800, 2100),
+    QXL_MODE_EX(3200, 2400),
+#endif
+};
+
+#define dprintf(level, fmt, ...)                                         \
+    do { if (debug >= level) fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+
+static int debug = 1;
+static int device_id = 0;
+static PCIQXLDevice *qxl0;
+
+static void qxl_send_events(PCIQXLDevice *d, uint32_t events);
+static void qxl_destroy_primary(PCIQXLDevice *d);
+
+static inline uint32_t msb_mask(uint32_t val)
+{
+    uint32_t mask;
+
+    do {
+        mask = ~(val - 1) & val;
+        val &= ~mask;
+    } while (mask < val);
+
+    return mask;
+}
+
+static inline void atomic_or(uint32_t *var, uint32_t add)
+{
+   __asm__ __volatile__ ("lock; orl %1, %0" : "+m" (*var) : "r" (add) : "memory");
+}
+
+static ram_addr_t qxl_rom_size(void)
+{
+    uint32_t rom_size = sizeof(QXLRom) + sizeof(QXLModes) + sizeof(qxl_modes);
+    rom_size = MAX(rom_size, TARGET_PAGE_SIZE);
+    rom_size = msb_mask(rom_size * 2 - 1);
+    return rom_size;
+}
+
+static void init_qxl_rom(PCIQXLDevice *d)
+{
+    QXLRom *rom = qemu_get_ram_ptr(d->rom_offset);
+    QXLModes *modes = (QXLModes *)(rom + 1);
+    uint32_t ram_header_size;
+    uint32_t fb, maxfb = 0;
+    int i;
+
+    memset(rom, 0, d->rom_size);
+
+    rom->magic = QXL_ROM_MAGIC;
+    rom->id = d->id;
+    rom->modes_offset = sizeof(QXLRom);
+
+    rom->slot_gen_bits = MEMSLOT_GENERATION_BITS;
+    rom->slot_id_bits = MEMSLOT_SLOT_BITS;
+    rom->slots_start = 1;
+    rom->slots_end = NUM_MEMSLOTS - 1;
+    rom->n_surfaces = 10000;
+
+    modes->n_modes = ARRAY_SIZE(qxl_modes);
+    for (i = 0; i < modes->n_modes; i++) {
+        fb = qxl_modes[i].y_res * qxl_modes[i].stride;
+        if (maxfb < fb)
+            maxfb = fb;
+        modes->modes[i] = qxl_modes[i];
+        modes->modes[i].id = i;
+    }
+    if (maxfb < VGA_RAM_SIZE && d->id == 0)
+        maxfb = VGA_RAM_SIZE;
+
+    ram_header_size = ALIGN(sizeof(QXLRam), 4096);
+    rom->surface0_area_size = ALIGN(maxfb, 4096);
+
+    rom->num_pages = d->vga.vram_size;
+    rom->num_pages -= ram_header_size;
+    rom->num_pages -= rom->surface0_area_size;
+    rom->num_pages = rom->num_pages / TARGET_PAGE_SIZE;
+
+    rom->ram_header_offset = d->vga.vram_size - ram_header_size;
+
+    d->shadow_rom = *rom;
+    d->rom = rom;
+    d->modes = modes;
+}
+
+static void init_qxl_ram(PCIQXLDevice *d)
+{
+    uint8_t *buf;
+    uint64_t *item;
+
+    buf = qemu_get_ram_ptr(d->vga.vram_offset);
+    d->ram = (QXLRam *)(buf + d->shadow_rom.ram_header_offset);
+    d->ram->magic = QXL_RAM_MAGIC;
+    d->ram->int_pending = 0;
+    d->ram->int_mask = 0;
+    SPICE_RING_INIT(&d->ram->cmd_ring);
+    SPICE_RING_INIT(&d->ram->cursor_ring);
+    SPICE_RING_INIT(&d->ram->release_ring);
+    SPICE_RING_PROD_ITEM(&d->ram->release_ring, item);
+    *item = 0;
+}
+
+static void qxl_rom_set_dirty(PCIQXLDevice *d)
+{
+    ram_addr_t addr = d->rom_offset;
+    ram_addr_t end  = addr + d->rom_size;
+
+    while (addr < end) {
+        cpu_physical_memory_set_dirty(addr);
+        addr += TARGET_PAGE_SIZE;
+    }
+}
+
+/* spice display interface callbacks */
+
+static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
+{
+    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+
+    dprintf(1, "%s:\n", __FUNCTION__);
+    qxl->ssd.worker = qxl_worker;
+}
+
+static void interface_set_compression_level(QXLInstance *sin, int level)
+{
+    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+
+    dprintf(1, "%s: %d\n", __FUNCTION__, level);
+    qxl->shadow_rom.compression_level = level;
+    qxl->rom->compression_level = level;
+    qxl_rom_set_dirty(qxl);
+}
+
+static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
+{
+    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+
+    qxl->shadow_rom.mm_clock = mm_time;
+    qxl->rom->mm_clock = mm_time;
+    qxl_rom_set_dirty(qxl);
+}
+
+static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
+{
+    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+
+    dprintf(1, "%s:\n", __FUNCTION__);
+    info->memslot_gen_bits = MEMSLOT_GENERATION_BITS;
+    info->memslot_id_bits = MEMSLOT_SLOT_BITS;
+    info->num_memslots = NUM_MEMSLOTS;
+    info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
+    info->internal_groupslot_id = 0;
+    info->qxl_ram_size = qxl->shadow_rom.num_pages << TARGET_PAGE_BITS;
+    info->n_surfaces = 10000;
+}
+
+static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
+{
+    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    SimpleSpiceUpdate *update;
+    QXLCommandRing *ring;
+    QXLCommand *cmd;
+    int notify;
+
+    switch (qxl->mode) {
+    case QXL_MODE_VGA:
+        dprintf(2, "%s: vga\n", __FUNCTION__);
+        update = qemu_spice_create_update(&qxl->ssd);
+        if (update == NULL) {
+            return false;
+        }
+        *ext = update->ext;
+        return true;
+    case QXL_MODE_NATIVE:
+        dprintf(2, "%s: native\n", __FUNCTION__);
+        ring = &qxl->ram->cmd_ring;
+        if (SPICE_RING_IS_EMPTY(ring)) {
+            return false;
+        }
+        SPICE_RING_CONS_ITEM(ring, cmd);
+        ext->cmd = *cmd;
+        ext->group_id = MEMSLOT_GROUP_GUEST;
+        SPICE_RING_POP(ring, notify);
+        if (notify) {
+            qxl_send_events(qxl, QXL_INTERRUPT_DISPLAY);
+        }
+        return true;
+    case QXL_MODE_UNDEFINED:
+    default:
+        return false;
+    }
+}
+
+static int interface_req_cmd_notification(QXLInstance *sin)
+{
+    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    int wait = 1;
+
+    if (qxl->mode == QXL_MODE_NATIVE) {
+        SPICE_RING_CONS_WAIT(&qxl->ram->cmd_ring, wait);
+    }
+    return wait;
+}
+
+static int interface_has_command(QXLInstance *sin)
+{
+    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+
+    switch (qxl->mode) {
+    case QXL_MODE_VGA:
+        return qemu_spice_rect_is_empty(&qxl->ssd.dirty) ? false : true;
+    case QXL_MODE_NATIVE:
+        return SPICE_RING_IS_EMPTY(&qxl->ram->cmd_ring) ? false : true;
+    case QXL_MODE_UNDEFINED:
+    default:
+        return false;
+    }
+}
+
+static inline void qxl_push_free_res(PCIQXLDevice *d)
+{
+    QXLReleaseRing *ring = &d->ram->release_ring;
+    uint64_t *item;
+
+#define QXL_FREE_BUNCH_SIZE 10
+
+    if (SPICE_RING_IS_EMPTY(ring) || (d->num_free_res == QXL_FREE_BUNCH_SIZE &&
+                                      ring->prod - ring->cons + 1 != ring->num_items)) {
+        int notify;
+
+        SPICE_RING_PUSH(ring, notify);
+        if (notify) {
+            qxl_send_events(d, QXL_INTERRUPT_DISPLAY);
+        }
+        SPICE_RING_PROD_ITEM(ring, item);
+        *item = 0;
+        d->num_free_res = 0;
+        d->last_release = NULL;
+    }
+}
+
+static void interface_release_resource(QXLInstance *sin,
+                                       struct QXLReleaseInfoExt ext)
+{
+    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    QXLReleaseRing *ring;
+    uint64_t *item, id;
+
+    if (ext.group_id == MEMSLOT_GROUP_HOST) {
+        /* host group -> vga mode update request */
+        qemu_spice_destroy_update(&qxl->ssd, (void*)ext.info->id);
+        return;
+    }
+
+    /*
+     * ext->info points into guest-visible memory
+     * pci bar 0, drawable.release_info
+     */
+    ring = &qxl->ram->release_ring;
+    SPICE_RING_PROD_ITEM(ring, item);
+    if (*item == 0) {
+        /* stick head into the ring */
+        id = ext.info->id;
+        ext.info->next = 0;
+        *item = id;
+    } else {
+        /* append item to the list */
+        qxl->last_release->next = ext.info->id;
+        ext.info->next = 0;
+    }
+    qxl->last_release = ext.info;
+    qxl->num_free_res++;
+    qxl_push_free_res(qxl);
+}
+
+static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext)
+{
+    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    QXLCursorRing *ring;
+    QXLCommand *cmd;
+    int notify;
+
+    if (qxl->mode == QXL_MODE_NATIVE) {
+        ring = &qxl->ram->cursor_ring;
+        if (SPICE_RING_IS_EMPTY(ring)) {
+            return false;
+        }
+        SPICE_RING_CONS_ITEM(ring, cmd);
+        ext->cmd = *cmd;
+        ext->group_id = MEMSLOT_GROUP_GUEST;
+        SPICE_RING_POP(ring, notify);
+        if (notify) {
+            qxl_send_events(qxl, QXL_INTERRUPT_CURSOR);
+        }
+        return true;
+    } else {
+        return false;
+    }
+}
+
+static int interface_req_cursor_notification(QXLInstance *sin)
+{
+    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    int wait = 1;
+
+    if (qxl->mode == QXL_MODE_NATIVE) {
+        SPICE_RING_CONS_WAIT(&qxl->ram->cursor_ring, wait);
+    }
+    return wait;
+}
+
+static void interface_get_update_area(QXLInstance *sin, const struct SpiceRect **rect , uint32_t **surface_id)
+{
+    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+
+    *rect = &qxl->ram->update_area;
+    *surface_id = &qxl->ram->update_surface;
+}
+
+static void interface_notify_update(QXLInstance *sin, uint32_t update_id)
+{
+    fprintf(stderr, "%s: abort()\n", __FUNCTION__);
+    abort();
+}
+
+static void interface_set_save_data(QXLInstance *sin, void *data, int size)
+{
+    fprintf(stderr, "%s: abort()\n", __FUNCTION__);
+    abort();
+}
+
+static void *interface_get_save_data(QXLInstance *sin)
+{
+    fprintf(stderr, "%s: abort()\n", __FUNCTION__);
+    abort();
+}
+
+static int interface_flush_resources(QXLInstance *sin)
+{
+    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
+    int ret;
+
+    ret = qxl->num_free_res;
+    if (ret) {
+        qxl_push_free_res(qxl);
+    }
+    return ret;
+}
+
+static QXLInterface qxl_interface = {
+    .base.type               = SPICE_INTERFACE_QXL,
+    .base.description        = "qxl gpu",
+    .base.major_version      = SPICE_INTERFACE_QXL_MAJOR,
+    .base.minor_version      = SPICE_INTERFACE_QXL_MINOR,
+
+    .pci_vendor              = REDHAT_PCI_VENDOR_ID,
+    .pci_id                  = QXL_DEVICE_ID,
+    .pci_revision            = QXL_REVISION,
+
+    .attache_worker          = interface_attach_worker,
+    .set_compression_level   = interface_set_compression_level,
+    .set_mm_time             = interface_set_mm_time,
+
+    .get_init_info           = interface_get_init_info,
+    .get_command             = interface_get_command,
+    .req_cmd_notification    = interface_req_cmd_notification,
+    .has_command             = interface_has_command,
+    .release_resource        = interface_release_resource,
+    .get_cursor_command      = interface_get_cursor_command,
+    .req_cursor_notification = interface_req_cursor_notification,
+    .get_update_area         = interface_get_update_area,
+    .notify_update           = interface_notify_update,
+    .set_save_data           = interface_set_save_data,
+    .get_save_data           = interface_get_save_data,
+    .flush_resources         = interface_flush_resources,
+};
+
+static void qxl_enter_vga_mode(PCIQXLDevice *d)
+{
+    if (d->mode == QXL_MODE_VGA) {
+        return;
+    }
+    dprintf(1, "%s\n", __FUNCTION__);
+    qemu_spice_create_host_primary(&d->ssd);
+    d->mode = QXL_MODE_VGA;
+    memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty));
+}
+
+static void qxl_exit_vga_mode(PCIQXLDevice *d)
+{
+    if (d->mode != QXL_MODE_VGA) {
+        return;
+    }
+    dprintf(1, "%s\n", __FUNCTION__);
+    qxl_destroy_primary(d);
+}
+
+static void qxl_set_irq(PCIQXLDevice *d)
+{
+    int level = !!(d->ram->int_pending & d->ram->int_mask);
+    qemu_set_irq(d->pci.irq[0], level);
+}
+
+static void qxl_write_config(PCIDevice *d, uint32_t address,
+                             uint32_t val, int len)
+{
+    PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, d);
+    VGACommonState *vga = &qxl->vga;
+
+    if (qxl->id == 0) {
+        vga_dirty_log_stop(vga);
+    }
+    pci_default_write_config(d, address, val, len);
+    if (qxl->id == 0) {
+        if (vga->map_addr && qxl->pci.io_regions[0].addr == -1)
+            vga->map_addr = 0;
+        vga_dirty_log_start(vga);
+    }
+}
+
+static void qxl_check_state(PCIQXLDevice *d)
+{
+    QXLRam *ram = d->ram;
+
+    assert(SPICE_RING_IS_EMPTY(&ram->cmd_ring));
+    assert(SPICE_RING_IS_EMPTY(&ram->cursor_ring));
+}
+
+static void qxl_reset_state(PCIQXLDevice *d)
+{
+    QXLRam *ram = d->ram;
+    QXLRom *rom = d->rom;
+
+    assert(SPICE_RING_IS_EMPTY(&ram->cmd_ring));
+    assert(SPICE_RING_IS_EMPTY(&ram->cursor_ring));
+    d->shadow_rom.update_id = 0;
+    *rom = d->shadow_rom;
+    qxl_rom_set_dirty(d);
+    init_qxl_ram(d);
+    d->num_free_res = 0;
+    d->last_release = NULL;
+    memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty));
+}
+
+static void qxl_soft_reset(PCIQXLDevice *d)
+{
+    dprintf(1, "%s:\n", __FUNCTION__);
+    qxl_check_state(d);
+
+    if (d->id == 0) {
+        qxl_enter_vga_mode(d);
+    } else {
+        d->mode = QXL_MODE_UNDEFINED;
+    }
+}
+
+static void qxl_hard_reset(PCIQXLDevice *d)
+{
+    dprintf(1, "%s: start\n", __FUNCTION__);
+
+    d->mode = QXL_MODE_UNDEFINED;
+    d->ssd.worker->destroy_surfaces(d->ssd.worker);
+    d->ssd.worker->reset_cursor(d->ssd.worker);
+    d->ssd.worker->reset_image_cache(d->ssd.worker);
+    d->ssd.worker->reset_memslots(d->ssd.worker);
+
+    qxl_reset_state(d);
+    qemu_spice_create_host_memslot(&d->ssd);
+    qxl_soft_reset(d);
+
+    dprintf(1, "%s: done\n", __FUNCTION__);
+}
+
+static void qxl_reset_handler(DeviceState *dev)
+{
+    PCIQXLDevice *d = DO_UPCAST(PCIQXLDevice, pci.qdev, dev);
+    qxl_hard_reset(d);
+}
+
+static void qxl_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    VGACommonState *vga = opaque;
+    PCIQXLDevice *qxl = container_of(vga, PCIQXLDevice, vga);
+
+    if (qxl->mode != QXL_MODE_VGA) {
+        qxl_destroy_primary(qxl);
+        qxl_soft_reset(qxl);
+    }
+    vga_ioport_write(opaque, addr, val);
+}
+
+static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id)
+{
+    static const int regions[] = {
+        QXL_RAM_RANGE_INDEX,
+        QXL_VRAM_RANGE_INDEX,
+    };
+    QXLMemSlot *guest_slot;
+    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_slot  = &d->ram->mem_slot;
+    guest_start = guest_slot->mem_start;
+    guest_end   = guest_slot->mem_end;
+
+    dprintf(1, "%s: slot %d: guest phys 0x%" PRIx64 " - 0x%" PRIx64 "\n",
+            __FUNCTION__, slot_id,
+            guest_start, guest_end);
+
+    PANIC_ON(slot_id >= NUM_MEMSLOTS);
+    PANIC_ON(guest_start > guest_end);
+
+    for (i = 0; i < ARRAY_SIZE(regions); i++) {
+        pci_region = regions[i];
+        pci_start = d->pci.io_regions[pci_region].addr;
+        pci_end = pci_start + d->pci.io_regions[pci_region].size;
+        /* mapped? */
+        if (pci_start == -1) {
+            continue;
+        }
+        /* start address in range ? */
+        if (guest_start < pci_start || guest_start > pci_end) {
+            continue;
+        }
+        /* end address in range ? */
+        if (guest_end > pci_end) {
+            continue;
+        }
+        /* passed */
+        break;
+    }
+    PANIC_ON(i == ARRAY_SIZE(regions)); /* finished loop without match */
+
+    switch (pci_region) {
+    case QXL_RAM_RANGE_INDEX:
+        virt_start = (intptr_t)qemu_get_ram_ptr(d->vga.vram_offset);
+        break;
+    case QXL_VRAM_RANGE_INDEX:
+        virt_start = (intptr_t)qemu_get_ram_ptr(d->vram_offset);
+        break;
+    }
+
+    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;
+    memslot.generation = d->rom->slot_generation = d->generation++;
+    qxl_rom_set_dirty(d);
+
+    dprintf(1, "%s: slot %d: host virt 0x%" PRIx64 " - 0x%" PRIx64 "\n",
+            __FUNCTION__, memslot.slot_id,
+            memslot.virt_start, memslot.virt_end);
+
+    d->ssd.worker->add_memslot(d->ssd.worker, &memslot);
+}
+
+static void qxl_del_memslot(PCIQXLDevice *d, uint32_t slot_id)
+{
+    dprintf(1, "%s: slot %d\n", __FUNCTION__, slot_id);
+    d->ssd.worker->del_memslot(d->ssd.worker, MEMSLOT_GROUP_HOST, slot_id);
+}
+
+static void qxl_create_guest_primary(PCIQXLDevice *d)
+{
+    QXLDevSurfaceCreate surface;
+
+    assert(d->mode != QXL_MODE_NATIVE);
+    qxl_exit_vga_mode(d);
+
+    dprintf(1, "%s: %dx%d\n", __FUNCTION__,
+            d->ram->create_surface.width,
+            d->ram->create_surface.height);
+
+    surface.depth = d->ram->create_surface.depth;
+    surface.height = d->ram->create_surface.height;
+    surface.mem = d->ram->create_surface.mem;
+    surface.mouse_mode = true;
+    surface.position = d->ram->create_surface.position;
+    surface.stride = d->ram->create_surface.stride;
+    surface.width = d->ram->create_surface.width;
+    surface.type = d->ram->create_surface.type;
+    surface.flags = d->ram->create_surface.flags;
+    surface.group_id = MEMSLOT_GROUP_GUEST;
+
+    d->mode = QXL_MODE_NATIVE;
+    d->ssd.worker->create_primary_surface(d->ssd.worker, 0, &surface);
+}
+
+static void qxl_destroy_primary(PCIQXLDevice *d)
+{
+    if (d->mode == QXL_MODE_UNDEFINED) {
+        return;
+    }
+
+    dprintf(1, "%s\n", __FUNCTION__);
+
+    d->mode = QXL_MODE_UNDEFINED;
+    d->ssd.worker->destroy_primary_surface(d->ssd.worker, 0);
+}
+
+static void ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    PCIQXLDevice *d = opaque;
+    uint32_t io_port = addr - d->io_base;
+
+    switch (io_port) {
+    case QXL_IO_RESET:
+    case QXL_IO_MEMSLOT_ADD:
+    case QXL_IO_MEMSLOT_DEL:
+    case QXL_IO_CREATE_PRIMARY:
+        break;
+    default:
+        if (d->mode == QXL_MODE_NATIVE)
+            break;
+        dprintf(1, "%s: unexpected port 0x%x in vga mode\n", __FUNCTION__, io_port);
+        return;
+    }
+
+    switch (io_port) {
+    case QXL_IO_UPDATE_AREA:
+        d->ssd.worker->update_area(d->ssd.worker);
+        break;
+    case QXL_IO_NOTIFY_CMD:
+        d->ssd.worker->wakeup(d->ssd.worker);
+        break;
+    case QXL_IO_NOTIFY_CURSOR:
+        d->ssd.worker->wakeup(d->ssd.worker);
+        break;
+    case QXL_IO_UPDATE_IRQ:
+        qxl_set_irq(d);
+        break;
+    case QXL_IO_NOTIFY_OOM:
+        if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) {
+            break;
+        }
+        pthread_yield();
+        if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) {
+            break;
+        }
+        d->ssd.worker->oom(d->ssd.worker);
+        break;
+    case QXL_IO_LOG:
+        dprintf(1, "qxl %u: log %s", d->id, d->ram->log_buf);
+        break;
+    case QXL_IO_RESET:
+        dprintf(1, "qxl %u: QXL_IO_RESET\n", d->id);
+        qxl_hard_reset(d);
+        break;
+    case QXL_IO_MEMSLOT_ADD:
+        qxl_add_memslot(d, val);
+        break;
+    case QXL_IO_MEMSLOT_DEL:
+        qxl_del_memslot(d, val);
+        break;
+    case QXL_IO_CREATE_PRIMARY:
+        PANIC_ON(val != 0);
+        qxl_create_guest_primary(d);
+        break;
+    case QXL_IO_DESTROY_PRIMARY:
+        PANIC_ON(val != 0);
+        qxl_destroy_primary(d);
+        break;
+    case QXL_IO_DESTROY_SURFACE_WAIT:
+        d->ssd.worker->destroy_surface_wait(d->ssd.worker, val);
+        break;
+    default:
+        fprintf(stderr, "%s: ioport=0x%x, abort()\n", __FUNCTION__, io_port);
+        abort();
+    }
+}
+
+static uint32_t ioport_read(void *opaque, uint32_t addr)
+{
+    dprintf(1, "%s: unexpected\n", __FUNCTION__);
+    return 0xff;
+}
+
+static void qxl_map(PCIDevice *pci, int region_num,
+                    pcibus_t addr, pcibus_t size, int type)
+{
+    PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, pci);
+
+    dprintf(1, "%s: bar %d addr 0x%lx size 0x%lx\n", __FUNCTION__,
+            region_num, addr, size);
+
+    switch (region_num) {
+    case QXL_IO_RANGE_INDEX:
+        register_ioport_write(addr, size, 1, ioport_write, pci);
+        register_ioport_read(addr, size, 1, ioport_read, pci);
+        qxl->io_base = addr;
+        break;
+    case QXL_RAM_RANGE_INDEX:
+        cpu_register_physical_memory(addr, size, qxl->vga.vram_offset | IO_MEM_RAM);
+        qxl->vga.map_addr = addr;
+        qxl->vga.map_end = addr + size;
+        if (qxl->id == 0) {
+            vga_dirty_log_start(&qxl->vga);
+        }
+        break;
+    case QXL_ROM_RANGE_INDEX:
+        cpu_register_physical_memory(addr, size, qxl->rom_offset | IO_MEM_ROM);
+        break;
+    case QXL_VRAM_RANGE_INDEX:
+        cpu_register_physical_memory(addr, size, qxl->vram_offset | IO_MEM_RAM);
+        break;
+    }
+}
+
+static void pipe_read(void *opaque)
+{
+    PCIQXLDevice *d = opaque;
+    char dummy;
+    int len;
+
+    do {
+        len = read(d->pipe[0], &dummy, sizeof(dummy));
+    } while (len == sizeof(dummy));
+    qxl_set_irq(d);
+}
+
+static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
+{
+    assert(d->ssd.running);
+    smp_wmb();
+    if ((d->ram->int_pending & events) == events) {
+        return;
+    }
+    atomic_or(&d->ram->int_pending, events);
+    if (pthread_self() == d->main) {
+        qxl_set_irq(d);
+    } else {
+        if (write(d->pipe[1], d, 1) != 1) {
+            dprintf(1, "%s: write to pipe failed\n", __FUNCTION__);
+        }
+    }
+}
+
+static void init_pipe_signaling(PCIQXLDevice *d)
+{
+   if (pipe(d->pipe) < 0) {
+       dprintf(1, "%s: pipe creation failed\n", __FUNCTION__);
+       return;
+   }
+#ifdef CONFIG_IOTHREAD
+   fcntl(d->pipe[0], F_SETFL, O_NONBLOCK);
+#else
+   fcntl(d->pipe[0], F_SETFL, O_NONBLOCK /* | O_ASYNC */);
+#endif
+   fcntl(d->pipe[1], F_SETFL, O_NONBLOCK);
+   fcntl(d->pipe[0], F_SETOWN, getpid());
+
+   d->main = pthread_self();
+   qemu_set_fd_handler(d->pipe[0], pipe_read, NULL, d);
+}
+
+static void display_update(struct DisplayState *ds, int x, int y, int w, int h)
+{
+    if (qxl0->mode == QXL_MODE_VGA) {
+        qemu_spice_display_update(&qxl0->ssd, x, y, w, h);
+    }
+}
+
+static void display_resize(struct DisplayState *ds)
+{
+    if (qxl0->mode == QXL_MODE_VGA) {
+        qemu_spice_display_resize(&qxl0->ssd);
+    }
+}
+
+static void display_refresh(struct DisplayState *ds)
+{
+    if (qxl0->mode == QXL_MODE_VGA) {
+        qemu_spice_display_refresh(&qxl0->ssd);
+    }
+}
+
+static DisplayChangeListener display_listener = {
+    .dpy_update  = display_update,
+    .dpy_resize  = display_resize,
+    .dpy_refresh = display_refresh,
+};
+
+static int qxl_init(PCIDevice *dev)
+{
+    PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
+    VGACommonState *vga = &qxl->vga;
+    uint8_t* config = qxl->pci.config;
+    ram_addr_t ram_size = msb_mask(qxl->vga.vram_size * 2 - 1);
+
+    qxl->id = device_id;
+    qxl->mode = QXL_MODE_UNDEFINED;
+    qxl->generation = 1;
+
+    if (!qxl->id) {
+        if (ram_size < 32 * 1024 * 1024)
+            ram_size = 32 * 1024 * 1024;
+        vga_common_init(vga, ram_size);
+        vga_init(vga);
+        register_ioport_write(0x3c0, 16, 1, qxl_vga_ioport_write, vga);
+        register_ioport_write(0x3b4,  2, 1, qxl_vga_ioport_write, vga);
+        register_ioport_write(0x3d4,  2, 1, qxl_vga_ioport_write, vga);
+        register_ioport_write(0x3ba,  1, 1, qxl_vga_ioport_write, vga);
+        register_ioport_write(0x3da,  1, 1, qxl_vga_ioport_write, vga);
+
+        qxl0 = qxl;
+        vga->ds = graphic_console_init(vga->update, vga->invalidate,
+                                       vga->screen_dump, vga->text_update, vga);
+        qxl->ssd.ds = vga->ds;
+        qxl->ssd.bufsize = (16 * 1024 * 1024);
+        qxl->ssd.buf = qemu_malloc(qxl->ssd.bufsize);
+        pthread_mutex_init(&qxl->ssd.lock, NULL);
+        register_displaychangelistener(vga->ds, &display_listener);
+
+        if (qxl->pci.romfile == NULL)
+            qxl->pci.romfile = qemu_strdup("vgabios-qxl.bin");
+        pci_config_set_class(config, PCI_CLASS_DISPLAY_VGA);
+    } else {
+        if (ram_size < 16 * 1024 * 1024)
+            ram_size = 16 * 1024 * 1024;
+        qxl->vga.vram_size = ram_size;
+        qxl->vga.vram_offset = qemu_ram_alloc(qxl->vga.vram_size);
+
+        pci_config_set_class(config, PCI_CLASS_DISPLAY_OTHER);
+    }
+
+    pci_config_set_vendor_id(config, REDHAT_PCI_VENDOR_ID);
+    pci_config_set_device_id(config, QXL_DEVICE_ID);
+    pci_set_byte(&config[PCI_REVISION_ID], QXL_REVISION);
+    pci_set_byte(&config[PCI_INTERRUPT_PIN], 1);
+
+    qxl->rom_size = qxl_rom_size();
+    qxl->rom_offset = qemu_ram_alloc(qxl->rom_size);
+    init_qxl_rom(qxl);
+    init_qxl_ram(qxl);
+
+    if (qxl->vram_size < 16 * 1024 * 1024)
+        qxl->vram_size = 16 * 1024 * 1024;
+    qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1);
+    qxl->vram_offset = qemu_ram_alloc(qxl->vram_size);
+
+    pci_register_bar(&qxl->pci, QXL_IO_RANGE_INDEX,
+                     msb_mask(QXL_IO_RANGE_SIZE * 2 - 1),
+                     PCI_BASE_ADDRESS_SPACE_IO, qxl_map);
+
+    pci_register_bar(&qxl->pci, QXL_ROM_RANGE_INDEX,
+                     qxl->rom_size, PCI_BASE_ADDRESS_SPACE_MEMORY,
+                     qxl_map);
+
+    pci_register_bar(&qxl->pci, QXL_RAM_RANGE_INDEX,
+                     qxl->vga.vram_size, PCI_BASE_ADDRESS_SPACE_MEMORY,
+                     qxl_map);
+
+    pci_register_bar(&qxl->pci, QXL_VRAM_RANGE_INDEX, qxl->vram_size,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, qxl_map);
+
+    qxl->ssd.qxl.base.sif = &qxl_interface.base;
+    qxl->ssd.qxl.id = qxl->id;
+    spice_server_add_interface(spice_server, &qxl->ssd.qxl.base);
+    qemu_add_vm_change_state_handler(qemu_spice_vm_change_state_handler, &qxl->ssd);
+
+    init_pipe_signaling(qxl);
+    qxl_reset_state(qxl);
+
+    device_id++;
+    return 0;
+}
+
+static PCIDeviceInfo qxl_info = {
+    .qdev.name    = "qxl",
+    .qdev.desc    = "Spice QXL GPU",
+    .qdev.size    = sizeof(PCIQXLDevice),
+    .qdev.reset   = qxl_reset_handler,
+    .init         = qxl_init,
+    .config_write = qxl_write_config,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 32 * 1024 * 1024),
+        DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, 64 * 1024 * 1024),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void qxl_register(void)
+{
+    pci_qdev_register(&qxl_info);
+}
+
+device_init(qxl_register);
diff --git a/hw/vga_int.h b/hw/vga_int.h
index 6a46a43..beb5423 100644
--- a/hw/vga_int.h
+++ b/hw/vga_int.h
@@ -106,7 +106,7 @@  typedef void (* vga_update_retrace_info_fn)(struct VGACommonState *s);
 typedef struct VGACommonState {
     uint8_t *vram_ptr;
     ram_addr_t vram_offset;
-    unsigned int vram_size;
+    uint32_t vram_size;
     uint32_t lfb_addr;
     uint32_t lfb_end;
     uint32_t map_addr;
diff --git a/qemu-spice.h b/qemu-spice.h
index f061004..b835d4e 100644
--- a/qemu-spice.h
+++ b/qemu-spice.h
@@ -15,6 +15,8 @@  void qemu_spice_init(void);
 void qemu_spice_input_init(void);
 void qemu_spice_display_init(DisplayState *ds);
 
+void qxl_display_init(DisplayState *ds);
+
 #else  /* CONFIG_SPICE */
 
 #define using_spice 0
diff --git a/sysemu.h b/sysemu.h
index d0effa0..560977f 100644
--- a/sysemu.h
+++ b/sysemu.h
@@ -103,7 +103,7 @@  extern int autostart;
 extern int bios_size;
 
 typedef enum {
-    VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB
+    VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB, VGA_QXL,
 } VGAInterfaceType;
 
 extern int vga_interface_type;
@@ -111,6 +111,7 @@  extern int vga_interface_type;
 #define std_vga_enabled (vga_interface_type == VGA_STD)
 #define xenfb_enabled (vga_interface_type == VGA_XENFB)
 #define vmsvga_enabled (vga_interface_type == VGA_VMWARE)
+#define qxl_enabled (vga_interface_type == VGA_QXL)
 
 extern int graphic_width;
 extern int graphic_height;
diff --git a/vl.c b/vl.c
index 1fab3f9..4e87427 100644
--- a/vl.c
+++ b/vl.c
@@ -2075,6 +2075,8 @@  static void select_vgahw (const char *p)
         vga_interface_type = VGA_VMWARE;
     } else if (strstart(p, "xenfb", &opts)) {
         vga_interface_type = VGA_XENFB;
+    } else if (strstart(p, "qxl", &opts)) {
+        vga_interface_type = VGA_QXL;
     } else if (!strstart(p, "none", &opts)) {
     invalid_vga:
         fprintf(stderr, "Unknown vga type: %s\n", p);
@@ -3745,7 +3747,7 @@  int main(int argc, char **argv, char **envp)
         break;
     }
 #ifdef CONFIG_SPICE
-    if (using_spice) {
+    if (using_spice && !qxl_enabled) {
         qemu_spice_display_init(ds);
     }
 #endif