]> git.proxmox.com Git - qemu.git/blobdiff - hw/qxl.c
xtensa-semi: Don't use CPUState
[qemu.git] / hw / qxl.c
index 03848edb75a16865d1fd5b887cee2c0bb1e6486e..e17b0e31af9c04ec6d70364934f611b299ed4ec6 100644 (file)
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -18,8 +18,6 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <pthread.h>
-
 #include "qemu-common.h"
 #include "qemu-timer.h"
 #include "qemu-queue.h"
@@ -127,9 +125,7 @@ static void qxl_ring_set_dirty(PCIQXLDevice *qxl);
 
 void qxl_guest_bug(PCIQXLDevice *qxl, const char *msg, ...)
 {
-#if SPICE_INTERFACE_QXL_MINOR >= 1
     qxl_send_events(qxl, QXL_INTERRUPT_ERROR);
-#endif
     if (qxl->guestdebug) {
         va_list ap;
         va_start(ap, msg);
@@ -145,18 +141,15 @@ void qxl_spice_update_area(PCIQXLDevice *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)
+                           qxl_async_io async, struct QXLCookie *cookie)
 {
     if (async == QXL_SYNC) {
         qxl->ssd.worker->update_area(qxl->ssd.worker, surface_id, area,
                         dirty_rects, num_dirty_rects, clear_dirty_region);
     } else {
-#if SPICE_INTERFACE_QXL_MINOR >= 1
+        assert(cookie != NULL);
         spice_qxl_update_area_async(&qxl->ssd.qxl, surface_id, area,
-                                    clear_dirty_region, 0);
-#else
-        abort();
-#endif
+                                    clear_dirty_region, (uint64_t)cookie);
     }
 }
 
@@ -172,25 +165,25 @@ static void qxl_spice_destroy_surface_wait_complete(PCIQXLDevice *qxl,
 static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id,
                                            qxl_async_io async)
 {
+    QXLCookie *cookie;
+
     if (async) {
-#if SPICE_INTERFACE_QXL_MINOR < 1
-        abort();
-#else
-        spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id,
-                                        (uint64_t)id);
-#endif
+        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, (uint64_t)cookie);
     } else {
         qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id);
         qxl_spice_destroy_surface_wait_complete(qxl, id);
     }
 }
 
-#if SPICE_INTERFACE_QXL_MINOR >= 1
 static void qxl_spice_flush_surfaces_async(PCIQXLDevice *qxl)
 {
-    spice_qxl_flush_surfaces_async(&qxl->ssd.qxl, 0);
+    spice_qxl_flush_surfaces_async(&qxl->ssd.qxl,
+        (uint64_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
+                                 QXL_IO_FLUSH_SURFACES_ASYNC));
 }
-#endif
 
 void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext,
                                uint32_t count)
@@ -219,11 +212,9 @@ static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl)
 static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async)
 {
     if (async) {
-#if SPICE_INTERFACE_QXL_MINOR < 1
-        abort();
-#else
-        spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl, 0);
-#endif
+        spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl,
+                (uint64_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);
@@ -238,6 +229,9 @@ void qxl_spice_reset_image_cache(PCIQXLDevice *qxl)
 void qxl_spice_reset_cursor(PCIQXLDevice *qxl)
 {
     qxl->ssd.worker->reset_cursor(qxl->ssd.worker);
+    qemu_mutex_lock(&qxl->track_lock);
+    qxl->guest_cursor = 0;
+    qemu_mutex_unlock(&qxl->track_lock);
 }
 
 
@@ -330,6 +324,7 @@ static void init_qxl_ram(PCIQXLDevice *d)
     d->ram->magic       = cpu_to_le32(QXL_RAM_MAGIC);
     d->ram->int_pending = cpu_to_le32(0);
     d->ram->int_mask    = cpu_to_le32(0);
+    d->ram->update_surface = 0;
     SPICE_RING_INIT(&d->ram->cmd_ring);
     SPICE_RING_INIT(&d->ram->cursor_ring);
     SPICE_RING_INIT(&d->ram->release_ring);
@@ -341,10 +336,7 @@ static void init_qxl_ram(PCIQXLDevice *d)
 /* can be called from spice server thread context */
 static void qxl_set_dirty(MemoryRegion *mr, ram_addr_t addr, ram_addr_t end)
 {
-    while (addr < end) {
-        memory_region_set_dirty(mr, addr);
-        addr += TARGET_PAGE_SIZE;
-    }
+    memory_region_set_dirty(mr, addr, end - addr);
 }
 
 static void qxl_rom_set_dirty(PCIQXLDevice *qxl)
@@ -402,7 +394,9 @@ static void qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext)
     {
         QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
         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;
     }
@@ -489,7 +483,6 @@ static const char *io_port_to_string(uint32_t io_port)
         [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",
-#if SPICE_INTERFACE_QXL_MINOR >= 1
         [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",
@@ -499,7 +492,6 @@ static const char *io_port_to_string(uint32_t io_port)
                                         = "QXL_IO_DESTROY_ALL_SURFACES_ASYNC",
         [QXL_IO_FLUSH_SURFACES_ASYNC]   = "QXL_IO_FLUSH_SURFACES_ASYNC",
         [QXL_IO_FLUSH_RELEASE]          = "QXL_IO_FLUSH_RELEASE",
-#endif
     };
     return io_port_to_string[io_port];
 }
@@ -624,7 +616,7 @@ static void interface_release_resource(QXLInstance *sin,
 
     if (ext.group_id == MEMSLOT_GROUP_HOST) {
         /* host group -> vga mode update request */
-        qemu_spice_destroy_update(&qxl->ssd, (void*)ext.info->id);
+        qemu_spice_destroy_update(&qxl->ssd, (void *)(intptr_t)ext.info->id);
         return;
     }
 
@@ -734,12 +726,9 @@ static int interface_flush_resources(QXLInstance *sin)
 
 static void qxl_create_guest_primary_complete(PCIQXLDevice *d);
 
-#if SPICE_INTERFACE_QXL_MINOR >= 1
-
 /* called from spice server thread context only */
-static void interface_async_complete(QXLInstance *sin, uint64_t cookie)
+static void interface_async_complete_io(PCIQXLDevice *qxl, QXLCookie *cookie)
 {
-    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
     uint32_t current_async;
 
     qemu_mutex_lock(&qxl->async_lock);
@@ -747,8 +736,22 @@ static void interface_async_complete(QXLInstance *sin, uint64_t cookie)
     qxl->current_async = QXL_UNDEFINED_IO;
     qemu_mutex_unlock(&qxl->async_lock);
 
-    dprint(qxl, 2, "async_complete: %d (%ld) done\n", current_async, cookie);
+    dprint(qxl, 2, "async_complete: %d (%p) done\n", 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 != %ld = 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;
@@ -756,13 +759,75 @@ static void interface_async_complete(QXLInstance *sin, uint64_t cookie)
         qxl_spice_destroy_surfaces_complete(qxl);
         break;
     case QXL_IO_DESTROY_SURFACE_ASYNC:
-        qxl_spice_destroy_surface_wait_complete(qxl, (uint32_t)cookie);
+        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);
 }
 
-#endif
+/* called from spice server thread context only */
+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);
+    int i;
+    int qxl_i;
+
+    qemu_mutex_lock(&qxl->ssd.lock);
+    if (surface_id != 0 || !qxl->render_update_cookie_num) {
+        qemu_mutex_unlock(&qxl->ssd.lock);
+        return;
+    }
+    if (qxl->num_dirty_rects + num_updated_rects > QXL_NUM_DIRTY_RECTS) {
+        /*
+         * overflow - treat this as a full update. Not expected to be common.
+         */
+        dprint(qxl, 1, "%s: overflow of dirty rects\n", __func__);
+        qxl->guest_primary.resized = 1;
+    }
+    if (qxl->guest_primary.resized) {
+        /*
+         * Don't bother copying or scheduling the bh since we will flip
+         * the whole area anyway on completion of the update_area async call
+         */
+        qemu_mutex_unlock(&qxl->ssd.lock);
+        return;
+    }
+    qxl_i = qxl->num_dirty_rects;
+    for (i = 0; i < num_updated_rects; i++) {
+        qxl->dirty[qxl_i++] = dirty[i];
+    }
+    qxl->num_dirty_rects += num_updated_rects;
+    dprint(qxl, 1, "%s: scheduling update_area_bh, #dirty %d\n",
+           __func__, qxl->num_dirty_rects);
+    qemu_bh_schedule(qxl->update_area_bh);
+    qemu_mutex_unlock(&qxl->ssd.lock);
+}
+
+/* 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);
+    QXLCookie *cookie = (QXLCookie *)cookie_token;
+
+    switch (cookie->type) {
+    case QXL_COOKIE_TYPE_IO:
+        interface_async_complete_io(qxl, cookie);
+        g_free(cookie);
+        break;
+    case QXL_COOKIE_TYPE_RENDER_UPDATE_AREA:
+        qxl_render_update_area_done(qxl, cookie);
+        break;
+    default:
+        fprintf(stderr, "qxl: %s: unexpected cookie type %d\n",
+                __func__, cookie->type);
+        g_free(cookie);
+    }
+}
 
 static const QXLInterface qxl_interface = {
     .base.type               = SPICE_INTERFACE_QXL,
@@ -783,9 +848,8 @@ static const QXLInterface qxl_interface = {
     .req_cursor_notification = interface_req_cursor_notification,
     .notify_update           = interface_notify_update,
     .flush_resources         = interface_flush_resources,
-#if SPICE_INTERFACE_QXL_MINOR >= 1
     .async_complete          = interface_async_complete,
-#endif
+    .update_area_complete    = interface_update_area_complete,
 };
 
 static void qxl_enter_vga_mode(PCIQXLDevice *d)
@@ -892,12 +956,27 @@ static void qxl_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
     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 void qxl_add_memslot(PCIQXLDevice *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;
@@ -944,6 +1023,7 @@ static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta,
         virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vga.vram);
         break;
     case QXL_VRAM_RANGE_INDEX:
+    case 4 /* vram 64bit */:
         virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vram_bar);
         break;
     default:
@@ -991,7 +1071,7 @@ static void qxl_reset_surfaces(PCIQXLDevice *d)
     qxl_spice_destroy_surfaces(d, QXL_SYNC);
 }
 
-/* called from spice server thread context only */
+/* can be also called from spice server thread context */
 void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id)
 {
     uint64_t phys   = le64_to_cpu(pqxl);
@@ -1000,9 +1080,9 @@ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id)
 
     switch (group_id) {
     case MEMSLOT_GROUP_HOST:
-        return (void*)offset;
+        return (void *)(intptr_t)offset;
     case MEMSLOT_GROUP_GUEST:
-        PANIC_ON(slot > NUM_MEMSLOTS);
+        PANIC_ON(slot >= NUM_MEMSLOTS);
         PANIC_ON(!qxl->guest_slots[slot].active);
         PANIC_ON(offset < qxl->guest_slots[slot].delta);
         offset -= qxl->guest_slots[slot].delta;
@@ -1062,11 +1142,10 @@ static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async)
     if (d->mode == QXL_MODE_UNDEFINED) {
         return 0;
     }
-
     dprint(d, 1, "%s\n", __FUNCTION__);
-
     d->mode = QXL_MODE_UNDEFINED;
     qemu_spice_destroy_primary_surface(&d->ssd, 0, async);
+    qxl_spice_reset_cursor(d);
     return 1;
 }
 
@@ -1120,9 +1199,7 @@ static void ioport_write(void *opaque, target_phys_addr_t addr,
     PCIQXLDevice *d = opaque;
     uint32_t io_port = addr;
     qxl_async_io async = QXL_SYNC;
-#if SPICE_INTERFACE_QXL_MINOR >= 1
     uint32_t orig_io_port = io_port;
-#endif
 
     switch (io_port) {
     case QXL_IO_RESET:
@@ -1132,10 +1209,8 @@ static void ioport_write(void *opaque, target_phys_addr_t addr,
     case QXL_IO_CREATE_PRIMARY:
     case QXL_IO_UPDATE_IRQ:
     case QXL_IO_LOG:
-#if SPICE_INTERFACE_QXL_MINOR >= 1
     case QXL_IO_MEMSLOT_ADD_ASYNC:
     case QXL_IO_CREATE_PRIMARY_ASYNC:
-#endif
         break;
     default:
         if (d->mode != QXL_MODE_VGA) {
@@ -1143,17 +1218,14 @@ static void ioport_write(void *opaque, target_phys_addr_t addr,
         }
         dprint(d, 1, "%s: unexpected port 0x%x (%s) in vga mode\n",
             __func__, io_port, io_port_to_string(io_port));
-#if SPICE_INTERFACE_QXL_MINOR >= 1
         /* 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);
         }
-#endif
         return;
     }
 
-#if SPICE_INTERFACE_QXL_MINOR >= 1
     /* we change the io_port to avoid ifdeffery in the main switch */
     orig_io_port = io_port;
     switch (io_port) {
@@ -1192,14 +1264,21 @@ async_common:
     default:
         break;
     }
-#endif
 
     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,
-                              &update, NULL, 0, 0, async);
+                              cookie ? &cookie->u.area : &update,
+                              NULL, 0, 0, async, cookie);
         break;
     }
     case QXL_IO_NOTIFY_CMD:
@@ -1212,10 +1291,6 @@ async_common:
         qxl_update_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;
         }
@@ -1288,7 +1363,6 @@ async_common:
         }
         qxl_spice_destroy_surface_wait(d, val, async);
         break;
-#if SPICE_INTERFACE_QXL_MINOR >= 1
     case QXL_IO_FLUSH_RELEASE: {
         QXLReleaseRing *ring = &d->ram->release_ring;
         if (ring->prod - ring->cons + 1 == ring->num_items) {
@@ -1309,7 +1383,6 @@ async_common:
                d->num_free_res);
         qxl_spice_flush_surfaces_async(d);
         break;
-#endif
     case QXL_IO_DESTROY_ALL_SURFACES:
         d->mode = QXL_MODE_UNDEFINED;
         qxl_spice_destroy_surfaces(d, async);
@@ -1320,16 +1393,12 @@ async_common:
     }
     return;
 cancel_async:
-#if SPICE_INTERFACE_QXL_MINOR >= 1
     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);
     }
-#else
-    return;
-#endif
 }
 
 static uint64_t ioport_read(void *opaque, target_phys_addr_t addr,
@@ -1372,7 +1441,7 @@ static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
     if ((old_pending & le_events) == le_events) {
         return;
     }
-    if (pthread_self() == d->main) {
+    if (qemu_thread_is_self(&d->main)) {
         qxl_update_irq(d);
     } else {
         if (write(d->pipe[1], d, 1) != 1) {
@@ -1391,7 +1460,7 @@ static void init_pipe_signaling(PCIQXLDevice *d)
    fcntl(d->pipe[1], F_SETFL, O_NONBLOCK);
    fcntl(d->pipe[0], F_SETOWN, getpid());
 
-   d->main = pthread_self();
+   qemu_thread_get_self(&d->main);
    qemu_set_fd_handler(d->pipe[0], pipe_read, NULL, d);
 }
 
@@ -1423,7 +1492,7 @@ static void qxl_hw_invalidate(void *opaque)
     vga->invalidate(vga);
 }
 
-static void qxl_hw_screen_dump(void *opaque, const char *filename)
+static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch)
 {
     PCIQXLDevice *qxl = opaque;
     VGACommonState *vga = &qxl->vga;
@@ -1435,7 +1504,7 @@ static void qxl_hw_screen_dump(void *opaque, const char *filename)
         ppm_save(filename, qxl->ssd.ds->surface);
         break;
     case QXL_MODE_VGA:
-        vga->screen_dump(vga, filename);
+        vga->screen_dump(vga, filename, cswitch);
         break;
     default:
         break;
@@ -1453,6 +1522,46 @@ static void qxl_hw_text_update(void *opaque, console_ch_t *chardata)
     }
 }
 
+static void qxl_dirty_surfaces(PCIQXLDevice *qxl)
+{
+    intptr_t vram_start;
+    int i;
+
+    if (qxl->mode != QXL_MODE_NATIVE && qxl->mode != QXL_MODE_COMPAT) {
+        return;
+    }
+
+    /* dirty the primary surface */
+    qxl_set_dirty(&qxl->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);
+
+    /* 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->type == QXL_SURFACE_CMD_CREATE);
+        surface_offset = (intptr_t)qxl_phys2virt(qxl,
+                                                 cmd->u.surface_create.data,
+                                                 MEMSLOT_GROUP_GUEST);
+        surface_offset -= vram_start;
+        surface_size = cmd->u.surface_create.height *
+                       abs(cmd->u.surface_create.stride);
+        dprint(qxl, 3, "%s: dirty surface %d, offset %d, size %d\n", __func__,
+               i, (int)surface_offset, surface_size);
+        qxl_set_dirty(&qxl->vram_bar, surface_offset, surface_size);
+    }
+}
+
 static void qxl_vm_change_state_handler(void *opaque, int running,
                                         RunState state)
 {
@@ -1466,14 +1575,9 @@ static void qxl_vm_change_state_handler(void *opaque, int running,
          * called
          */
          qxl_update_irq(qxl);
-    } else if (qxl->mode == QXL_MODE_NATIVE) {
-        /* dirty all vram (which holds surfaces) and devram (primary surface)
-         * to make sure they are saved */
-        /* FIXME #1: should go out during "live" stage */
-        /* FIXME #2: we only need to save the areas which are actually used */
-        qxl_set_dirty(&qxl->vram_bar, 0, qxl->vram_size);
-        qxl_set_dirty(&qxl->vga.vram, qxl->shadow_rom.draw_area_offset,
-                      qxl->shadow_rom.surface0_area_size);
+    } else {
+        /* make sure surfaces are saved before migration */
+        qxl_dirty_surfaces(qxl);
     }
 }
 
@@ -1497,6 +1601,10 @@ static void display_refresh(struct DisplayState *ds)
 {
     if (qxl0->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);
     }
 }
 
@@ -1506,6 +1614,41 @@ static DisplayChangeListener display_listener = {
     .dpy_refresh = display_refresh,
 };
 
+static void qxl_init_ramsize(PCIQXLDevice *qxl, uint32_t ram_min_mb)
+{
+    /* vga ram (bar 0) */
+    if (qxl->ram_size_mb != -1) {
+        qxl->vga.vram_size = qxl->ram_size_mb * 1024 * 1024;
+    }
+    if (qxl->vga.vram_size < ram_min_mb * 1024 * 1024) {
+        qxl->vga.vram_size = ram_min_mb * 1024 * 1024;
+    }
+
+    /* 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->revision == 1) {
+        qxl->vram32_size = 4096;
+        qxl->vram_size = 4096;
+    }
+    qxl->vga.vram_size = msb_mask(qxl->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)
 {
     uint8_t* config = qxl->pci.config;
@@ -1527,9 +1670,7 @@ static int qxl_init_common(PCIQXLDevice *qxl)
     case 2: /* spice 0.6 -- qxl-2 */
         pci_device_rev = QXL_REVISION_STABLE_V06;
         break;
-#if SPICE_INTERFACE_QXL_MINOR >= 1
     case 3: /* qxl-3 */
-#endif
     default:
         pci_device_rev = QXL_DEFAULT_REVISION;
         break;
@@ -1539,20 +1680,15 @@ static int qxl_init_common(PCIQXLDevice *qxl)
     pci_set_byte(&config[PCI_INTERRUPT_PIN], 1);
 
     qxl->rom_size = qxl_rom_size();
-    memory_region_init_ram(&qxl->rom_bar, &qxl->pci.qdev, "qxl.vrom",
-                           qxl->rom_size);
+    memory_region_init_ram(&qxl->rom_bar, "qxl.vrom", qxl->rom_size);
+    vmstate_register_ram(&qxl->rom_bar, &qxl->pci.qdev);
     init_qxl_rom(qxl);
     init_qxl_ram(qxl);
 
-    if (qxl->vram_size < 16 * 1024 * 1024) {
-        qxl->vram_size = 16 * 1024 * 1024;
-    }
-    if (qxl->revision == 1) {
-        qxl->vram_size = 4096;
-    }
-    qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1);
-    memory_region_init_ram(&qxl->vram_bar, &qxl->pci.qdev, "qxl.vram",
-                           qxl->vram_size);
+    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);
 
     io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1);
     if (qxl->revision == 1) {
@@ -1576,7 +1712,29 @@ static int qxl_init_common(PCIQXLDevice *qxl)
                      PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vga.vram);
 
     pci_register_bar(&qxl->pci, QXL_VRAM_RANGE_INDEX,
-                     PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vram_bar);
+                     PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->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_BASE_ADDRESS_SPACE_MEMORY |
+                         PCI_BASE_ADDRESS_MEM_TYPE_64 |
+                         PCI_BASE_ADDRESS_MEM_PREFETCH,
+                         &qxl->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));
+    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;
@@ -1586,6 +1744,8 @@ static int qxl_init_common(PCIQXLDevice *qxl)
     init_pipe_signaling(qxl);
     qxl_reset_state(qxl);
 
+    qxl->update_area_bh = qemu_bh_new(qxl_render_update_area_bh, qxl);
+
     return 0;
 }
 
@@ -1593,20 +1753,14 @@ static int qxl_init_primary(PCIDevice *dev)
 {
     PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
     VGACommonState *vga = &qxl->vga;
-    ram_addr_t ram_size = msb_mask(qxl->vga.vram_size * 2 - 1);
+    PortioList *qxl_vga_port_list = g_new(PortioList, 1);
 
     qxl->id = 0;
-
-    if (ram_size < 32 * 1024 * 1024) {
-        ram_size = 32 * 1024 * 1024;
-    }
-    vga_common_init(vga, ram_size);
+    qxl_init_ramsize(qxl, 32);
+    vga_common_init(vga, qxl->vga.vram_size);
     vga_init(vga, pci_address_space(dev), pci_address_space_io(dev), false);
-    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);
+    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);
@@ -1622,16 +1776,11 @@ static int qxl_init_secondary(PCIDevice *dev)
 {
     static int device_id = 1;
     PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
-    ram_addr_t ram_size = msb_mask(qxl->vga.vram_size * 2 - 1);
 
     qxl->id = device_id++;
-
-    if (ram_size < 16 * 1024 * 1024) {
-        ram_size = 16 * 1024 * 1024;
-    }
-    qxl->vga.vram_size = ram_size;
-    memory_region_init_ram(&qxl->vga.vram, &qxl->pci.qdev, "qxl.vgavram",
-                           qxl->vga.vram_size);
+    qxl_init_ramsize(qxl, 16);
+    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);
 
     return qxl_init_common(qxl);
@@ -1662,12 +1811,25 @@ static int qxl_pre_load(void *opaque)
     return 0;
 }
 
+static void qxl_create_memslots(PCIQXLDevice *d)
+{
+    int i;
+
+    for (i = 0; i < NUM_MEMSLOTS; i++) {
+        if (!d->guest_slots[i].active) {
+            continue;
+        }
+        dprint(d, 1, "%s: restoring guest slot %d\n", __func__, i);
+        qxl_add_memslot(d, i, 0, QXL_SYNC);
+    }
+}
+
 static int qxl_post_load(void *opaque, int version)
 {
     PCIQXLDevice* d = opaque;
     uint8_t *ram_start = d->vga.vram_ptr;
     QXLCommandExt *cmds;
-    int in, out, i, newmode;
+    int in, out, newmode;
 
     dprint(d, 1, "%s: start\n", __FUNCTION__);
 
@@ -1684,19 +1846,16 @@ static int qxl_post_load(void *opaque, int version)
         qxl_mode_to_string(d->mode));
     newmode = d->mode;
     d->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:
-        for (i = 0; i < NUM_MEMSLOTS; i++) {
-            if (!d->guest_slots[i].active) {
-                continue;
-            }
-            qxl_add_memslot(d, i, 0, QXL_SYNC);
-        }
+        qxl_create_memslots(d);
         qxl_create_guest_primary(d, 1, QXL_SYNC);
 
         /* replay surface-create and cursor-set commands */
@@ -1710,15 +1869,19 @@ static int qxl_post_load(void *opaque, int version)
             cmds[out].group_id = MEMSLOT_GROUP_GUEST;
             out++;
         }
-        cmds[out].cmd.data = d->guest_cursor;
-        cmds[out].cmd.type = QXL_CMD_CURSOR;
-        cmds[out].group_id = MEMSLOT_GROUP_GUEST;
-        out++;
+        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;
     }
@@ -1787,60 +1950,72 @@ static VMStateDescription qxl_vmstate = {
     },
 };
 
-static PCIDeviceInfo qxl_info_primary = {
-    .qdev.name    = "qxl-vga",
-    .qdev.desc    = "Spice QXL GPU (primary, vga compatible)",
-    .qdev.size    = sizeof(PCIQXLDevice),
-    .qdev.reset   = qxl_reset_handler,
-    .qdev.vmsd    = &qxl_vmstate,
-    .no_hotplug   = 1,
-    .init         = qxl_init_primary,
-    .romfile      = "vgabios-qxl.bin",
-    .vendor_id    = REDHAT_PCI_VENDOR_ID,
-    .device_id    = QXL_DEVICE_ID_STABLE,
-    .class_id     = PCI_CLASS_DISPLAY_VGA,
-    .qdev.props = (Property[]) {
+static Property qxl_properties[] = {
         DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size,
                            64 * 1024 * 1024),
-        DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size,
+        DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram32_size,
                            64 * 1024 * 1024),
         DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision,
                            QXL_DEFAULT_REVISION),
         DEFINE_PROP_UINT32("debug", PCIQXLDevice, 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, 0),
+        DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, 0),
         DEFINE_PROP_END_OF_LIST(),
-    }
 };
 
-static PCIDeviceInfo qxl_info_secondary = {
-    .qdev.name    = "qxl",
-    .qdev.desc    = "Spice QXL GPU (secondary)",
-    .qdev.size    = sizeof(PCIQXLDevice),
-    .qdev.reset   = qxl_reset_handler,
-    .qdev.vmsd    = &qxl_vmstate,
-    .init         = qxl_init_secondary,
-    .vendor_id    = REDHAT_PCI_VENDOR_ID,
-    .device_id    = QXL_DEVICE_ID_STABLE,
-    .class_id     = PCI_CLASS_DISPLAY_OTHER,
-    .qdev.props = (Property[]) {
-        DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size,
-                           64 * 1024 * 1024),
-        DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size,
-                           64 * 1024 * 1024),
-        DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision,
-                           QXL_DEFAULT_REVISION),
-        DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0),
-        DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0),
-        DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0),
-        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(PCIQXLDevice),
+    .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(PCIQXLDevice),
+    .class_init    = qxl_secondary_class_init,
 };
 
-static void qxl_register(void)
+static void qxl_register_types(void)
 {
-    pci_qdev_register(&qxl_info_primary);
-    pci_qdev_register(&qxl_info_secondary);
+    type_register_static(&qxl_primary_info);
+    type_register_static(&qxl_secondary_info);
 }
 
-device_init(qxl_register);
+type_init(qxl_register_types)