qxl.c 82.8 KB
Newer Older
G
Gerd Hoffmann 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * Copyright (C) 2010 Red Hat, Inc.
 *
 * written by Yaniv Kamay, Izik Eidus, Gerd Hoffmann
 * maintained by Gerd Hoffmann <kraxel@redhat.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 or
 * (at your option) version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

P
Peter Maydell 已提交
21
#include "qemu/osdep.h"
22
#include "qemu/units.h"
23 24
#include <zlib.h>

25
#include "qapi/error.h"
G
Gerd Hoffmann 已提交
26
#include "qemu-common.h"
27 28
#include "qemu/timer.h"
#include "qemu/queue.h"
29
#include "qemu/atomic.h"
30
#include "sysemu/sysemu.h"
31
#include "migration/blocker.h"
A
Alon Levy 已提交
32
#include "trace.h"
G
Gerd Hoffmann 已提交
33

34
#include "qxl.h"
G
Gerd Hoffmann 已提交
35

36 37 38
/*
 * NOTE: SPICE_RING_PROD_ITEM accesses memory on the pci bar and as
 * such can be changed by the guest, so to avoid a guest trigerrable
39
 * abort we just qxl_set_guest_bug and set the return to NULL. Still
40 41
 * it may happen as a result of emulator bug as well.
 */
G
Gerd Hoffmann 已提交
42
#undef SPICE_RING_PROD_ITEM
43
#define SPICE_RING_PROD_ITEM(qxl, r, ret) {                             \
G
Gerd Hoffmann 已提交
44
        uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r);           \
45
        if (prod >= ARRAY_SIZE((r)->items)) {                           \
46
            qxl_set_guest_bug(qxl, "SPICE_RING_PROD_ITEM indices mismatch " \
47
                          "%u >= %zu", prod, ARRAY_SIZE((r)->items));   \
48 49
            ret = NULL;                                                 \
        } else {                                                        \
50
            ret = &(r)->items[prod].el;                                 \
G
Gerd Hoffmann 已提交
51 52 53 54
        }                                                               \
    }

#undef SPICE_RING_CONS_ITEM
55
#define SPICE_RING_CONS_ITEM(qxl, r, ret) {                             \
G
Gerd Hoffmann 已提交
56
        uint32_t cons = (r)->cons & SPICE_RING_INDEX_MASK(r);           \
57
        if (cons >= ARRAY_SIZE((r)->items)) {                           \
58
            qxl_set_guest_bug(qxl, "SPICE_RING_CONS_ITEM indices mismatch " \
59
                          "%u >= %zu", cons, ARRAY_SIZE((r)->items));   \
60 61
            ret = NULL;                                                 \
        } else {                                                        \
62
            ret = &(r)->items[cons].el;                                 \
G
Gerd Hoffmann 已提交
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
        }                                                               \
    }

#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),              \
A
Alon Levy 已提交
87
    QXL_MODE_16_32(x_res, y_res, 1)
G
Gerd Hoffmann 已提交
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115

static QXLMode qxl_modes[] = {
    QXL_MODE_EX(640, 480),
    QXL_MODE_EX(800, 480),
    QXL_MODE_EX(800, 600),
    QXL_MODE_EX(832, 624),
    QXL_MODE_EX(960, 640),
    QXL_MODE_EX(1024, 600),
    QXL_MODE_EX(1024, 768),
    QXL_MODE_EX(1152, 864),
    QXL_MODE_EX(1152, 870),
    QXL_MODE_EX(1280, 720),
    QXL_MODE_EX(1280, 760),
    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),
    /* these modes need more than 8 MB video memory */
    QXL_MODE_EX(1920, 1200),
    QXL_MODE_EX(1920, 1440),
116
    QXL_MODE_EX(2000, 2000),
G
Gerd Hoffmann 已提交
117
    QXL_MODE_EX(2048, 1536),
118
    QXL_MODE_EX(2048, 2048),
G
Gerd Hoffmann 已提交
119 120 121 122 123 124
    QXL_MODE_EX(2560, 1440),
    QXL_MODE_EX(2560, 1600),
    /* these modes need more than 16 MB video memory */
    QXL_MODE_EX(2560, 2048),
    QXL_MODE_EX(2800, 2100),
    QXL_MODE_EX(3200, 2400),
125
    /* these modes need more than 32 MB video memory */
G
Gerd Hoffmann 已提交
126 127
    QXL_MODE_EX(3840, 2160), /* 4k mainstream */
    QXL_MODE_EX(4096, 2160), /* 4k            */
128
    /* these modes need more than 64 MB video memory */
G
Gerd Hoffmann 已提交
129
    QXL_MODE_EX(7680, 4320), /* 8k mainstream */
130
    /* these modes need more than 128 MB video memory */
G
Gerd Hoffmann 已提交
131
    QXL_MODE_EX(8192, 4320), /* 8k            */
G
Gerd Hoffmann 已提交
132 133 134
};

static void qxl_send_events(PCIQXLDevice *d, uint32_t events);
135
static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async);
G
Gerd Hoffmann 已提交
136 137 138 139
static void qxl_reset_memslots(PCIQXLDevice *d);
static void qxl_reset_surfaces(PCIQXLDevice *d);
static void qxl_ring_set_dirty(PCIQXLDevice *qxl);

140 141
static void qxl_hw_update(void *opaque);

142
void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...)
143
{
A
Alon Levy 已提交
144
    trace_qxl_set_guest_bug(qxl->id);
145
    qxl_send_events(qxl, QXL_INTERRUPT_ERROR);
146
    qxl->guest_bug = 1;
147
    if (qxl->guestdebug) {
148 149 150 151 152 153
        va_list ap;
        va_start(ap, msg);
        fprintf(stderr, "qxl-%d: guest bug: ", qxl->id);
        vfprintf(stderr, msg, ap);
        fprintf(stderr, "\n");
        va_end(ap);
154 155 156
    }
}

157 158 159 160
static void qxl_clear_guest_bug(PCIQXLDevice *qxl)
{
    qxl->guest_bug = 0;
}
G
Gerd Hoffmann 已提交
161 162 163 164

void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id,
                           struct QXLRect *area, struct QXLRect *dirty_rects,
                           uint32_t num_dirty_rects,
165
                           uint32_t clear_dirty_region,
A
Alon Levy 已提交
166
                           qxl_async_io async, struct QXLCookie *cookie)
G
Gerd Hoffmann 已提交
167
{
A
Alon Levy 已提交
168 169 170 171
    trace_qxl_spice_update_area(qxl->id, surface_id, area->left, area->right,
                                area->top, area->bottom);
    trace_qxl_spice_update_area_rest(qxl->id, num_dirty_rects,
                                     clear_dirty_region);
172
    if (async == QXL_SYNC) {
173
        spice_qxl_update_area(&qxl->ssd.qxl, surface_id, area,
174 175
                        dirty_rects, num_dirty_rects, clear_dirty_region);
    } else {
A
Alon Levy 已提交
176
        assert(cookie != NULL);
177
        spice_qxl_update_area_async(&qxl->ssd.qxl, surface_id, area,
178
                                    clear_dirty_region, (uintptr_t)cookie);
179
    }
G
Gerd Hoffmann 已提交
180 181
}

182 183
static void qxl_spice_destroy_surface_wait_complete(PCIQXLDevice *qxl,
                                                    uint32_t id)
G
Gerd Hoffmann 已提交
184
{
A
Alon Levy 已提交
185
    trace_qxl_spice_destroy_surface_wait_complete(qxl->id, id);
186 187 188 189
    qemu_mutex_lock(&qxl->track_lock);
    qxl->guest_surfaces.cmds[id] = 0;
    qxl->guest_surfaces.count--;
    qemu_mutex_unlock(&qxl->track_lock);
G
Gerd Hoffmann 已提交
190 191
}

192 193 194
static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id,
                                           qxl_async_io async)
{
A
Alon Levy 已提交
195 196
    QXLCookie *cookie;

A
Alon Levy 已提交
197
    trace_qxl_spice_destroy_surface_wait(qxl->id, id, async);
198
    if (async) {
A
Alon Levy 已提交
199 200 201
        cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
                                QXL_IO_DESTROY_SURFACE_ASYNC);
        cookie->u.surface_id = id;
202
        spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, (uintptr_t)cookie);
203
    } else {
204
        spice_qxl_destroy_surface_wait(&qxl->ssd.qxl, id);
205
        qxl_spice_destroy_surface_wait_complete(qxl, id);
206 207 208
    }
}

209 210
static void qxl_spice_flush_surfaces_async(PCIQXLDevice *qxl)
{
A
Alon Levy 已提交
211 212
    trace_qxl_spice_flush_surfaces_async(qxl->id, qxl->guest_surfaces.count,
                                         qxl->num_free_res);
A
Alon Levy 已提交
213
    spice_qxl_flush_surfaces_async(&qxl->ssd.qxl,
214 215
        (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
                                  QXL_IO_FLUSH_SURFACES_ASYNC));
216 217
}

G
Gerd Hoffmann 已提交
218 219 220
void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext,
                               uint32_t count)
{
A
Alon Levy 已提交
221
    trace_qxl_spice_loadvm_commands(qxl->id, ext, count);
222
    spice_qxl_loadvm_commands(&qxl->ssd.qxl, ext, count);
G
Gerd Hoffmann 已提交
223 224 225 226
}

void qxl_spice_oom(PCIQXLDevice *qxl)
{
A
Alon Levy 已提交
227
    trace_qxl_spice_oom(qxl->id);
228
    spice_qxl_oom(&qxl->ssd.qxl);
G
Gerd Hoffmann 已提交
229 230 231 232
}

void qxl_spice_reset_memslots(PCIQXLDevice *qxl)
{
A
Alon Levy 已提交
233
    trace_qxl_spice_reset_memslots(qxl->id);
234
    spice_qxl_reset_memslots(&qxl->ssd.qxl);
G
Gerd Hoffmann 已提交
235 236
}

237
static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl)
G
Gerd Hoffmann 已提交
238
{
A
Alon Levy 已提交
239
    trace_qxl_spice_destroy_surfaces_complete(qxl->id);
240
    qemu_mutex_lock(&qxl->track_lock);
241
    memset(qxl->guest_surfaces.cmds, 0,
242
           sizeof(qxl->guest_surfaces.cmds[0]) * qxl->ssd.num_surfaces);
243 244
    qxl->guest_surfaces.count = 0;
    qemu_mutex_unlock(&qxl->track_lock);
G
Gerd Hoffmann 已提交
245 246
}

247 248
static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async)
{
A
Alon Levy 已提交
249
    trace_qxl_spice_destroy_surfaces(qxl->id, async);
250
    if (async) {
A
Alon Levy 已提交
251
        spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl,
252 253
                (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
                                          QXL_IO_DESTROY_ALL_SURFACES_ASYNC));
254
    } else {
255
        spice_qxl_destroy_surfaces(&qxl->ssd.qxl);
256 257 258 259
        qxl_spice_destroy_surfaces_complete(qxl);
    }
}

260 261
static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay)
{
262 263
    QXLMonitorsConfig *cfg;

264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
    trace_qxl_spice_monitors_config(qxl->id);
    if (replay) {
        /*
         * don't use QXL_COOKIE_TYPE_IO:
         *  - we are not running yet (post_load), we will assert
         *    in send_events
         *  - this is not a guest io, but a reply, so async_io isn't set.
         */
        spice_qxl_monitors_config_async(&qxl->ssd.qxl,
                qxl->guest_monitors_config,
                MEMSLOT_GROUP_GUEST,
                (uintptr_t)qxl_cookie_new(
                    QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG,
                    0));
    } else {
279 280
#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */
        if (qxl->max_outputs) {
281
            spice_qxl_set_max_monitors(&qxl->ssd.qxl, qxl->max_outputs);
282 283
        }
#endif
284 285 286 287 288 289 290
        qxl->guest_monitors_config = qxl->ram->monitors_config;
        spice_qxl_monitors_config_async(&qxl->ssd.qxl,
                qxl->ram->monitors_config,
                MEMSLOT_GROUP_GUEST,
                (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
                                          QXL_IO_MONITORS_CONFIG_ASYNC));
    }
291 292

    cfg = qxl_phys2virt(qxl, qxl->guest_monitors_config, MEMSLOT_GROUP_GUEST);
293
    if (cfg != NULL && cfg->count == 1) {
294 295 296 297 298 299 300
        qxl->guest_primary.resized = 1;
        qxl->guest_head0_width  = cfg->heads[0].width;
        qxl->guest_head0_height = cfg->heads[0].height;
    } else {
        qxl->guest_head0_width  = 0;
        qxl->guest_head0_height = 0;
    }
301 302
}

G
Gerd Hoffmann 已提交
303 304
void qxl_spice_reset_image_cache(PCIQXLDevice *qxl)
{
A
Alon Levy 已提交
305
    trace_qxl_spice_reset_image_cache(qxl->id);
306
    spice_qxl_reset_image_cache(&qxl->ssd.qxl);
G
Gerd Hoffmann 已提交
307 308 309 310
}

void qxl_spice_reset_cursor(PCIQXLDevice *qxl)
{
A
Alon Levy 已提交
311
    trace_qxl_spice_reset_cursor(qxl->id);
312
    spice_qxl_reset_cursor(&qxl->ssd.qxl);
Y
Yonit Halperin 已提交
313 314 315
    qemu_mutex_lock(&qxl->track_lock);
    qxl->guest_cursor = 0;
    qemu_mutex_unlock(&qxl->track_lock);
G
Gerd Hoffmann 已提交
316 317 318 319
    if (qxl->ssd.cursor) {
        cursor_put(qxl->ssd.cursor);
    }
    qxl->ssd.cursor = cursor_builtin_hidden();
G
Gerd Hoffmann 已提交
320 321
}

322 323 324 325 326 327 328 329 330 331
static uint32_t qxl_crc32(const uint8_t *p, unsigned len)
{
    /*
     * zlib xors the seed with 0xffffffff, and xors the result
     * again with 0xffffffff; Both are not done with linux's crc32,
     * which we want to be compatible with, so undo that.
     */
    return crc32(0xffffffff, p, len) ^ 0xffffffff;
}

G
Gerd Hoffmann 已提交
332 333
static ram_addr_t qxl_rom_size(void)
{
334 335
#define QXL_REQUIRED_SZ (sizeof(QXLRom) + sizeof(QXLModes) + sizeof(qxl_modes))
#define QXL_ROM_SZ 8192
336

337 338
    QEMU_BUILD_BUG_ON(QXL_REQUIRED_SZ > QXL_ROM_SZ);
    return QXL_ROM_SZ;
G
Gerd Hoffmann 已提交
339 340 341 342
}

static void init_qxl_rom(PCIQXLDevice *d)
{
343
    QXLRom *rom = memory_region_get_ram_ptr(&d->rom_bar);
G
Gerd Hoffmann 已提交
344 345 346 347
    QXLModes *modes = (QXLModes *)(rom + 1);
    uint32_t ram_header_size;
    uint32_t surface0_area_size;
    uint32_t num_pages;
348 349
    uint32_t fb;
    int i, n;
G
Gerd Hoffmann 已提交
350 351 352 353 354 355 356 357 358 359 360 361

    memset(rom, 0, d->rom_size);

    rom->magic         = cpu_to_le32(QXL_ROM_MAGIC);
    rom->id            = cpu_to_le32(d->id);
    rom->log_level     = cpu_to_le32(d->guestdebug);
    rom->modes_offset  = cpu_to_le32(sizeof(QXLRom));

    rom->slot_gen_bits = MEMSLOT_GENERATION_BITS;
    rom->slot_id_bits  = MEMSLOT_SLOT_BITS;
    rom->slots_start   = 1;
    rom->slots_end     = NUM_MEMSLOTS - 1;
362
    rom->n_surfaces    = cpu_to_le32(d->ssd.num_surfaces);
G
Gerd Hoffmann 已提交
363

364
    for (i = 0, n = 0; i < ARRAY_SIZE(qxl_modes); i++) {
G
Gerd Hoffmann 已提交
365
        fb = qxl_modes[i].y_res * qxl_modes[i].stride;
366 367
        if (fb > d->vgamem_size) {
            continue;
G
Gerd Hoffmann 已提交
368
        }
369 370 371 372 373 374 375 376 377 378 379
        modes->modes[n].id          = cpu_to_le32(i);
        modes->modes[n].x_res       = cpu_to_le32(qxl_modes[i].x_res);
        modes->modes[n].y_res       = cpu_to_le32(qxl_modes[i].y_res);
        modes->modes[n].bits        = cpu_to_le32(qxl_modes[i].bits);
        modes->modes[n].stride      = cpu_to_le32(qxl_modes[i].stride);
        modes->modes[n].x_mili      = cpu_to_le32(qxl_modes[i].x_mili);
        modes->modes[n].y_mili      = cpu_to_le32(qxl_modes[i].y_mili);
        modes->modes[n].orientation = cpu_to_le32(qxl_modes[i].orientation);
        n++;
    }
    modes->n_modes     = cpu_to_le32(n);
G
Gerd Hoffmann 已提交
380 381

    ram_header_size    = ALIGN(sizeof(QXLRam), 4096);
382
    surface0_area_size = ALIGN(d->vgamem_size, 4096);
G
Gerd Hoffmann 已提交
383 384 385
    num_pages          = d->vga.vram_size;
    num_pages         -= ram_header_size;
    num_pages         -= surface0_area_size;
386
    num_pages          = num_pages / QXL_PAGE_SIZE;
G
Gerd Hoffmann 已提交
387

388 389
    assert(ram_header_size + surface0_area_size <= d->vga.vram_size);

G
Gerd Hoffmann 已提交
390 391 392 393 394 395
    rom->draw_area_offset   = cpu_to_le32(0);
    rom->surface0_area_size = cpu_to_le32(surface0_area_size);
    rom->pages_offset       = cpu_to_le32(surface0_area_size);
    rom->num_pages          = cpu_to_le32(num_pages);
    rom->ram_header_offset  = cpu_to_le32(d->vga.vram_size - ram_header_size);

396 397 398 399 400 401 402 403 404 405 406 407
    if (d->xres && d->yres) {
        /* needs linux kernel 4.12+ to work */
        rom->client_monitors_config.count = 1;
        rom->client_monitors_config.heads[0].left = 0;
        rom->client_monitors_config.heads[0].top = 0;
        rom->client_monitors_config.heads[0].right = cpu_to_le32(d->xres);
        rom->client_monitors_config.heads[0].bottom = cpu_to_le32(d->yres);
        rom->client_monitors_config_crc = qxl_crc32(
            (const uint8_t *)&rom->client_monitors_config,
            sizeof(rom->client_monitors_config));
    }

G
Gerd Hoffmann 已提交
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
    d->shadow_rom = *rom;
    d->rom        = rom;
    d->modes      = modes;
}

static void init_qxl_ram(PCIQXLDevice *d)
{
    uint8_t *buf;
    uint64_t *item;

    buf = d->vga.vram_ptr;
    d->ram = (QXLRam *)(buf + le32_to_cpu(d->shadow_rom.ram_header_offset));
    d->ram->magic       = cpu_to_le32(QXL_RAM_MAGIC);
    d->ram->int_pending = cpu_to_le32(0);
    d->ram->int_mask    = cpu_to_le32(0);
A
Alon Levy 已提交
423
    d->ram->update_surface = 0;
A
Anthony PERARD 已提交
424
    d->ram->monitors_config = 0;
G
Gerd Hoffmann 已提交
425 426 427
    SPICE_RING_INIT(&d->ram->cmd_ring);
    SPICE_RING_INIT(&d->ram->cursor_ring);
    SPICE_RING_INIT(&d->ram->release_ring);
428 429
    SPICE_RING_PROD_ITEM(d, &d->ram->release_ring, item);
    assert(item);
G
Gerd Hoffmann 已提交
430 431 432 433 434
    *item = 0;
    qxl_ring_set_dirty(d);
}

/* can be called from spice server thread context */
435
static void qxl_set_dirty(MemoryRegion *mr, ram_addr_t addr, ram_addr_t end)
G
Gerd Hoffmann 已提交
436
{
437
    memory_region_set_dirty(mr, addr, end - addr);
G
Gerd Hoffmann 已提交
438 439 440 441
}

static void qxl_rom_set_dirty(PCIQXLDevice *qxl)
{
442
    qxl_set_dirty(&qxl->rom_bar, 0, qxl->rom_size);
G
Gerd Hoffmann 已提交
443 444 445 446 447 448 449 450 451 452
}

/* called from spice server thread context only */
static void qxl_ram_set_dirty(PCIQXLDevice *qxl, void *ptr)
{
    void *base = qxl->vga.vram_ptr;
    intptr_t offset;

    offset = ptr - base;
    assert(offset < qxl->vga.vram_size);
G
Gerd Hoffmann 已提交
453
    qxl_set_dirty(&qxl->vga.vram, offset, offset + 3);
G
Gerd Hoffmann 已提交
454 455 456 457 458
}

/* can be called from spice server thread context */
static void qxl_ring_set_dirty(PCIQXLDevice *qxl)
{
459 460 461
    ram_addr_t addr = qxl->shadow_rom.ram_header_offset;
    ram_addr_t end  = qxl->vga.vram_size;
    qxl_set_dirty(&qxl->vga.vram, addr, end);
G
Gerd Hoffmann 已提交
462 463 464 465 466 467
}

/*
 * keep track of some command state, for savevm/loadvm.
 * called from spice server thread context only
 */
468
static int qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext)
G
Gerd Hoffmann 已提交
469 470 471 472 473
{
    switch (le32_to_cpu(ext->cmd.type)) {
    case QXL_CMD_SURFACE:
    {
        QXLSurfaceCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
474 475 476 477

        if (!cmd) {
            return 1;
        }
G
Gerd Hoffmann 已提交
478
        uint32_t id = le32_to_cpu(cmd->surface_id);
479

480
        if (id >= qxl->ssd.num_surfaces) {
481
            qxl_set_guest_bug(qxl, "QXL_CMD_SURFACE id %d >= %d", id,
482
                              qxl->ssd.num_surfaces);
483 484
            return 1;
        }
485 486 487 488 489 490
        if (cmd->type == QXL_SURFACE_CMD_CREATE &&
            (cmd->u.surface_create.stride & 0x03) != 0) {
            qxl_set_guest_bug(qxl, "QXL_CMD_SURFACE stride = %d %% 4 != 0\n",
                              cmd->u.surface_create.stride);
            return 1;
        }
491
        qemu_mutex_lock(&qxl->track_lock);
G
Gerd Hoffmann 已提交
492 493 494 495 496 497 498 499 500 501
        if (cmd->type == QXL_SURFACE_CMD_CREATE) {
            qxl->guest_surfaces.cmds[id] = ext->cmd.data;
            qxl->guest_surfaces.count++;
            if (qxl->guest_surfaces.max < qxl->guest_surfaces.count)
                qxl->guest_surfaces.max = qxl->guest_surfaces.count;
        }
        if (cmd->type == QXL_SURFACE_CMD_DESTROY) {
            qxl->guest_surfaces.cmds[id] = 0;
            qxl->guest_surfaces.count--;
        }
502
        qemu_mutex_unlock(&qxl->track_lock);
G
Gerd Hoffmann 已提交
503 504 505 506 507
        break;
    }
    case QXL_CMD_CURSOR:
    {
        QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
508 509 510 511

        if (!cmd) {
            return 1;
        }
G
Gerd Hoffmann 已提交
512
        if (cmd->type == QXL_CURSOR_SET) {
Y
Yonit Halperin 已提交
513
            qemu_mutex_lock(&qxl->track_lock);
G
Gerd Hoffmann 已提交
514
            qxl->guest_cursor = ext->cmd.data;
Y
Yonit Halperin 已提交
515
            qemu_mutex_unlock(&qxl->track_lock);
G
Gerd Hoffmann 已提交
516
        }
517 518 519 520 521
        if (cmd->type == QXL_CURSOR_HIDE) {
            qemu_mutex_lock(&qxl->track_lock);
            qxl->guest_cursor = 0;
            qemu_mutex_unlock(&qxl->track_lock);
        }
G
Gerd Hoffmann 已提交
522 523 524
        break;
    }
    }
525
    return 0;
G
Gerd Hoffmann 已提交
526 527 528 529 530 531 532 533
}

/* spice display interface callbacks */

static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
{
    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);

A
Alon Levy 已提交
534
    trace_qxl_interface_attach_worker(qxl->id);
G
Gerd Hoffmann 已提交
535 536 537 538 539 540
}

static void interface_set_compression_level(QXLInstance *sin, int level)
{
    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);

A
Alon Levy 已提交
541
    trace_qxl_interface_set_compression_level(qxl->id, level);
G
Gerd Hoffmann 已提交
542 543 544 545 546
    qxl->shadow_rom.compression_level = cpu_to_le32(level);
    qxl->rom->compression_level = cpu_to_le32(level);
    qxl_rom_set_dirty(qxl);
}

547
#if SPICE_NEEDS_SET_MM_TIME
G
Gerd Hoffmann 已提交
548 549 550 551
static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
{
    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);

552 553 554 555
    if (!qemu_spice_display_is_running(&qxl->ssd)) {
        return;
    }

A
Alon Levy 已提交
556
    trace_qxl_interface_set_mm_time(qxl->id, mm_time);
G
Gerd Hoffmann 已提交
557 558 559 560
    qxl->shadow_rom.mm_clock = cpu_to_le32(mm_time);
    qxl->rom->mm_clock = cpu_to_le32(mm_time);
    qxl_rom_set_dirty(qxl);
}
561
#endif
G
Gerd Hoffmann 已提交
562 563 564 565 566

static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
{
    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);

A
Alon Levy 已提交
567
    trace_qxl_interface_get_init_info(qxl->id);
G
Gerd Hoffmann 已提交
568 569 570 571 572
    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;
573 574
    info->qxl_ram_size =
        le32_to_cpu(qxl->shadow_rom.num_pages) << QXL_PAGE_BITS;
575
    info->n_surfaces = qxl->ssd.num_surfaces;
G
Gerd Hoffmann 已提交
576 577
}

578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
static const char *qxl_mode_to_string(int mode)
{
    switch (mode) {
    case QXL_MODE_COMPAT:
        return "compat";
    case QXL_MODE_NATIVE:
        return "native";
    case QXL_MODE_UNDEFINED:
        return "undefined";
    case QXL_MODE_VGA:
        return "vga";
    }
    return "INVALID";
}

A
Alon Levy 已提交
593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
static const char *io_port_to_string(uint32_t io_port)
{
    if (io_port >= QXL_IO_RANGE_SIZE) {
        return "out of range";
    }
    static const char *io_port_to_string[QXL_IO_RANGE_SIZE + 1] = {
        [QXL_IO_NOTIFY_CMD]             = "QXL_IO_NOTIFY_CMD",
        [QXL_IO_NOTIFY_CURSOR]          = "QXL_IO_NOTIFY_CURSOR",
        [QXL_IO_UPDATE_AREA]            = "QXL_IO_UPDATE_AREA",
        [QXL_IO_UPDATE_IRQ]             = "QXL_IO_UPDATE_IRQ",
        [QXL_IO_NOTIFY_OOM]             = "QXL_IO_NOTIFY_OOM",
        [QXL_IO_RESET]                  = "QXL_IO_RESET",
        [QXL_IO_SET_MODE]               = "QXL_IO_SET_MODE",
        [QXL_IO_LOG]                    = "QXL_IO_LOG",
        [QXL_IO_MEMSLOT_ADD]            = "QXL_IO_MEMSLOT_ADD",
        [QXL_IO_MEMSLOT_DEL]            = "QXL_IO_MEMSLOT_DEL",
        [QXL_IO_DETACH_PRIMARY]         = "QXL_IO_DETACH_PRIMARY",
        [QXL_IO_ATTACH_PRIMARY]         = "QXL_IO_ATTACH_PRIMARY",
        [QXL_IO_CREATE_PRIMARY]         = "QXL_IO_CREATE_PRIMARY",
        [QXL_IO_DESTROY_PRIMARY]        = "QXL_IO_DESTROY_PRIMARY",
        [QXL_IO_DESTROY_SURFACE_WAIT]   = "QXL_IO_DESTROY_SURFACE_WAIT",
        [QXL_IO_DESTROY_ALL_SURFACES]   = "QXL_IO_DESTROY_ALL_SURFACES",
        [QXL_IO_UPDATE_AREA_ASYNC]      = "QXL_IO_UPDATE_AREA_ASYNC",
        [QXL_IO_MEMSLOT_ADD_ASYNC]      = "QXL_IO_MEMSLOT_ADD_ASYNC",
        [QXL_IO_CREATE_PRIMARY_ASYNC]   = "QXL_IO_CREATE_PRIMARY_ASYNC",
        [QXL_IO_DESTROY_PRIMARY_ASYNC]  = "QXL_IO_DESTROY_PRIMARY_ASYNC",
        [QXL_IO_DESTROY_SURFACE_ASYNC]  = "QXL_IO_DESTROY_SURFACE_ASYNC",
        [QXL_IO_DESTROY_ALL_SURFACES_ASYNC]
                                        = "QXL_IO_DESTROY_ALL_SURFACES_ASYNC",
        [QXL_IO_FLUSH_SURFACES_ASYNC]   = "QXL_IO_FLUSH_SURFACES_ASYNC",
        [QXL_IO_FLUSH_RELEASE]          = "QXL_IO_FLUSH_RELEASE",
624
        [QXL_IO_MONITORS_CONFIG_ASYNC]  = "QXL_IO_MONITORS_CONFIG_ASYNC",
A
Alon Levy 已提交
625 626 627 628
    };
    return io_port_to_string[io_port];
}

G
Gerd Hoffmann 已提交
629 630 631 632 633 634 635
/* called from spice server thread context only */
static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
{
    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
    SimpleSpiceUpdate *update;
    QXLCommandRing *ring;
    QXLCommand *cmd;
636
    int notify, ret;
G
Gerd Hoffmann 已提交
637

A
Alon Levy 已提交
638 639
    trace_qxl_ring_command_check(qxl->id, qxl_mode_to_string(qxl->mode));

G
Gerd Hoffmann 已提交
640 641
    switch (qxl->mode) {
    case QXL_MODE_VGA:
642 643
        ret = false;
        qemu_mutex_lock(&qxl->ssd.lock);
644 645 646
        update = QTAILQ_FIRST(&qxl->ssd.updates);
        if (update != NULL) {
            QTAILQ_REMOVE(&qxl->ssd.updates, update, next);
647 648
            *ext = update->ext;
            ret = true;
G
Gerd Hoffmann 已提交
649
        }
650
        qemu_mutex_unlock(&qxl->ssd.lock);
A
Alon Levy 已提交
651
        if (ret) {
A
Alon Levy 已提交
652
            trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode));
A
Alon Levy 已提交
653 654
            qxl_log_command(qxl, "vga", ext);
        }
655
        return ret;
G
Gerd Hoffmann 已提交
656 657 658 659
    case QXL_MODE_COMPAT:
    case QXL_MODE_NATIVE:
    case QXL_MODE_UNDEFINED:
        ring = &qxl->ram->cmd_ring;
660
        if (qxl->guest_bug || SPICE_RING_IS_EMPTY(ring)) {
G
Gerd Hoffmann 已提交
661 662
            return false;
        }
663 664 665 666
        SPICE_RING_CONS_ITEM(qxl, ring, cmd);
        if (!cmd) {
            return false;
        }
G
Gerd Hoffmann 已提交
667 668 669 670 671 672 673 674 675 676 677
        ext->cmd      = *cmd;
        ext->group_id = MEMSLOT_GROUP_GUEST;
        ext->flags    = qxl->cmdflags;
        SPICE_RING_POP(ring, notify);
        qxl_ring_set_dirty(qxl);
        if (notify) {
            qxl_send_events(qxl, QXL_INTERRUPT_DISPLAY);
        }
        qxl->guest_primary.commands++;
        qxl_track_command(qxl, ext);
        qxl_log_command(qxl, "cmd", ext);
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701
        {
            /*
             * Windows 8 drivers place qxl commands in the vram
             * (instead of the ram) bar.  We can't live migrate such a
             * guest, so add a migration blocker in case we detect
             * this, to avoid triggering the assert in pre_save().
             *
             * https://cgit.freedesktop.org/spice/win32/qxl-wddm-dod/commit/?id=f6e099db39e7d0787f294d5fd0dce328b5210faa
             */
            void *msg = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
            if (msg != NULL && (
                    msg < (void *)qxl->vga.vram_ptr ||
                    msg > ((void *)qxl->vga.vram_ptr + qxl->vga.vram_size))) {
                if (!qxl->migration_blocker) {
                    Error *local_err = NULL;
                    error_setg(&qxl->migration_blocker,
                               "qxl: guest bug: command not in ram bar");
                    migrate_add_blocker(qxl->migration_blocker, &local_err);
                    if (local_err) {
                        error_report_err(local_err);
                    }
                }
            }
        }
702
        trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode));
G
Gerd Hoffmann 已提交
703 704 705 706 707 708 709 710 711 712 713 714
        return true;
    default:
        return false;
    }
}

/* called from spice server thread context only */
static int interface_req_cmd_notification(QXLInstance *sin)
{
    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
    int wait = 1;

A
Alon Levy 已提交
715
    trace_qxl_ring_command_req_notification(qxl->id);
G
Gerd Hoffmann 已提交
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752
    switch (qxl->mode) {
    case QXL_MODE_COMPAT:
    case QXL_MODE_NATIVE:
    case QXL_MODE_UNDEFINED:
        SPICE_RING_CONS_WAIT(&qxl->ram->cmd_ring, wait);
        qxl_ring_set_dirty(qxl);
        break;
    default:
        /* nothing */
        break;
    }
    return wait;
}

/* called from spice server thread context only */
static inline void qxl_push_free_res(PCIQXLDevice *d, int flush)
{
    QXLReleaseRing *ring = &d->ram->release_ring;
    uint64_t *item;
    int notify;

#define QXL_FREE_BUNCH_SIZE 32

    if (ring->prod - ring->cons + 1 == ring->num_items) {
        /* ring full -- can't push */
        return;
    }
    if (!flush && d->oom_running) {
        /* collect everything from oom handler before pushing */
        return;
    }
    if (!flush && d->num_free_res < QXL_FREE_BUNCH_SIZE) {
        /* collect a bit more before pushing */
        return;
    }

    SPICE_RING_PUSH(ring, notify);
A
Alon Levy 已提交
753 754 755 756 757
    trace_qxl_ring_res_push(d->id, qxl_mode_to_string(d->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);
G
Gerd Hoffmann 已提交
758 759 760
    if (notify) {
        qxl_send_events(d, QXL_INTERRUPT_DISPLAY);
    }
761 762 763 764
    SPICE_RING_PROD_ITEM(d, ring, item);
    if (!item) {
        return;
    }
G
Gerd Hoffmann 已提交
765 766 767 768 769 770 771 772
    *item = 0;
    d->num_free_res = 0;
    d->last_release = NULL;
    qxl_ring_set_dirty(d);
}

/* called from spice server thread context only */
static void interface_release_resource(QXLInstance *sin,
773
                                       QXLReleaseInfoExt ext)
G
Gerd Hoffmann 已提交
774 775 776 777 778 779 780
{
    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 */
G
Gerd Hoffmann 已提交
781
        QXLCommandExt *cmdext = (void *)(intptr_t)(ext.info->id);
G
Gerd Hoffmann 已提交
782 783 784 785
        SimpleSpiceUpdate *update;
        g_assert(cmdext->cmd.type == QXL_CMD_DRAW);
        update = container_of(cmdext, SimpleSpiceUpdate, ext);
        qemu_spice_destroy_update(&qxl->ssd, update);
G
Gerd Hoffmann 已提交
786 787 788 789 790 791 792 793
        return;
    }

    /*
     * ext->info points into guest-visible memory
     * pci bar 0, $command.release_info
     */
    ring = &qxl->ram->release_ring;
794 795 796 797
    SPICE_RING_PROD_ITEM(qxl, ring, item);
    if (!item) {
        return;
    }
G
Gerd Hoffmann 已提交
798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813
    if (*item == 0) {
        /* stick head into the ring */
        id = ext.info->id;
        ext.info->next = 0;
        qxl_ram_set_dirty(qxl, &ext.info->next);
        *item = id;
        qxl_ring_set_dirty(qxl);
    } else {
        /* append item to the list */
        qxl->last_release->next = ext.info->id;
        qxl_ram_set_dirty(qxl, &qxl->last_release->next);
        ext.info->next = 0;
        qxl_ram_set_dirty(qxl, &ext.info->next);
    }
    qxl->last_release = ext.info;
    qxl->num_free_res++;
A
Alon Levy 已提交
814
    trace_qxl_ring_res_put(qxl->id, qxl->num_free_res);
G
Gerd Hoffmann 已提交
815 816 817 818 819 820 821 822 823 824 825
    qxl_push_free_res(qxl, 0);
}

/* called from spice server thread context only */
static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext)
{
    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
    QXLCursorRing *ring;
    QXLCommand *cmd;
    int notify;

A
Alon Levy 已提交
826 827
    trace_qxl_ring_cursor_check(qxl->id, qxl_mode_to_string(qxl->mode));

G
Gerd Hoffmann 已提交
828 829 830 831 832 833 834 835
    switch (qxl->mode) {
    case QXL_MODE_COMPAT:
    case QXL_MODE_NATIVE:
    case QXL_MODE_UNDEFINED:
        ring = &qxl->ram->cursor_ring;
        if (SPICE_RING_IS_EMPTY(ring)) {
            return false;
        }
836 837 838 839
        SPICE_RING_CONS_ITEM(qxl, ring, cmd);
        if (!cmd) {
            return false;
        }
G
Gerd Hoffmann 已提交
840 841 842 843 844 845 846 847 848 849 850
        ext->cmd      = *cmd;
        ext->group_id = MEMSLOT_GROUP_GUEST;
        ext->flags    = qxl->cmdflags;
        SPICE_RING_POP(ring, notify);
        qxl_ring_set_dirty(qxl);
        if (notify) {
            qxl_send_events(qxl, QXL_INTERRUPT_CURSOR);
        }
        qxl->guest_primary.commands++;
        qxl_track_command(qxl, ext);
        qxl_log_command(qxl, "csr", ext);
G
Gerd Hoffmann 已提交
851
        if (qxl->have_vga) {
G
Gerd Hoffmann 已提交
852 853
            qxl_render_cursor(qxl, ext);
        }
A
Alon Levy 已提交
854
        trace_qxl_ring_cursor_get(qxl->id, qxl_mode_to_string(qxl->mode));
G
Gerd Hoffmann 已提交
855 856 857 858 859 860 861 862 863 864 865 866
        return true;
    default:
        return false;
    }
}

/* called from spice server thread context only */
static int interface_req_cursor_notification(QXLInstance *sin)
{
    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
    int wait = 1;

A
Alon Levy 已提交
867
    trace_qxl_ring_cursor_req_notification(qxl->id);
G
Gerd Hoffmann 已提交
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884
    switch (qxl->mode) {
    case QXL_MODE_COMPAT:
    case QXL_MODE_NATIVE:
    case QXL_MODE_UNDEFINED:
        SPICE_RING_CONS_WAIT(&qxl->ram->cursor_ring, wait);
        qxl_ring_set_dirty(qxl);
        break;
    default:
        /* nothing */
        break;
    }
    return wait;
}

/* called from spice server thread context */
static void interface_notify_update(QXLInstance *sin, uint32_t update_id)
{
885 886 887 888 889 890 891
    /*
     * Called by spice-server as a result of a QXL_CMD_UPDATE which is not in
     * use by xf86-video-qxl and is defined out in the qxl windows driver.
     * Probably was at some earlier version that is prior to git start (2009),
     * and is still guest trigerrable.
     */
    fprintf(stderr, "%s: deprecated\n", __func__);
G
Gerd Hoffmann 已提交
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906
}

/* called from spice server thread context only */
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, 1);
    }
    return ret;
}

907 908 909
static void qxl_create_guest_primary_complete(PCIQXLDevice *d);

/* called from spice server thread context only */
A
Alon Levy 已提交
910
static void interface_async_complete_io(PCIQXLDevice *qxl, QXLCookie *cookie)
911 912 913 914 915 916 917 918
{
    uint32_t current_async;

    qemu_mutex_lock(&qxl->async_lock);
    current_async = qxl->current_async;
    qxl->current_async = QXL_UNDEFINED_IO;
    qemu_mutex_unlock(&qxl->async_lock);

A
Alon Levy 已提交
919
    trace_qxl_interface_async_complete_io(qxl->id, current_async, cookie);
A
Alon Levy 已提交
920 921 922 923 924 925
    if (!cookie) {
        fprintf(stderr, "qxl: %s: error, cookie is NULL\n", __func__);
        return;
    }
    if (cookie && current_async != cookie->io) {
        fprintf(stderr,
A
Alon Levy 已提交
926 927
                "qxl: %s: error: current_async = %d != %"
                PRId64 " = cookie->io\n", __func__, current_async, cookie->io);
A
Alon Levy 已提交
928
    }
929
    switch (current_async) {
A
Alon Levy 已提交
930 931 932 933
    case QXL_IO_MEMSLOT_ADD_ASYNC:
    case QXL_IO_DESTROY_PRIMARY_ASYNC:
    case QXL_IO_UPDATE_AREA_ASYNC:
    case QXL_IO_FLUSH_SURFACES_ASYNC:
934
    case QXL_IO_MONITORS_CONFIG_ASYNC:
A
Alon Levy 已提交
935
        break;
936 937 938 939 940 941 942
    case QXL_IO_CREATE_PRIMARY_ASYNC:
        qxl_create_guest_primary_complete(qxl);
        break;
    case QXL_IO_DESTROY_ALL_SURFACES_ASYNC:
        qxl_spice_destroy_surfaces_complete(qxl);
        break;
    case QXL_IO_DESTROY_SURFACE_ASYNC:
A
Alon Levy 已提交
943
        qxl_spice_destroy_surface_wait_complete(qxl, cookie->u.surface_id);
944
        break;
A
Alon Levy 已提交
945 946 947
    default:
        fprintf(stderr, "qxl: %s: unexpected current_async %d\n", __func__,
                current_async);
948 949 950 951
    }
    qxl_send_events(qxl, QXL_INTERRUPT_IO_CMD);
}

A
Alon Levy 已提交
952 953 954 955 956 957 958 959 960 961
/* 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);
962 963
    if (surface_id != 0 || !num_updated_rects ||
        !qxl->render_update_cookie_num) {
A
Alon Levy 已提交
964 965 966
        qemu_mutex_unlock(&qxl->ssd.lock);
        return;
    }
A
Alon Levy 已提交
967 968 969
    trace_qxl_interface_update_area_complete(qxl->id, surface_id, dirty->left,
            dirty->right, dirty->top, dirty->bottom);
    trace_qxl_interface_update_area_complete_rest(qxl->id, num_updated_rects);
A
Alon Levy 已提交
970 971 972 973
    if (qxl->num_dirty_rects + num_updated_rects > QXL_NUM_DIRTY_RECTS) {
        /*
         * overflow - treat this as a full update. Not expected to be common.
         */
A
Alon Levy 已提交
974 975
        trace_qxl_interface_update_area_complete_overflow(qxl->id,
                                                          QXL_NUM_DIRTY_RECTS);
A
Alon Levy 已提交
976 977 978 979 980 981 982 983 984 985 986 987 988 989 990
        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;
A
Alon Levy 已提交
991 992
    trace_qxl_interface_update_area_complete_schedule_bh(qxl->id,
                                                         qxl->num_dirty_rects);
A
Alon Levy 已提交
993 994 995 996
    qemu_bh_schedule(qxl->update_area_bh);
    qemu_mutex_unlock(&qxl->ssd.lock);
}

A
Alon Levy 已提交
997 998 999 1000
/* 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);
1001
    QXLCookie *cookie = (QXLCookie *)(uintptr_t)cookie_token;
A
Alon Levy 已提交
1002 1003 1004 1005

    switch (cookie->type) {
    case QXL_COOKIE_TYPE_IO:
        interface_async_complete_io(qxl, cookie);
A
Alon Levy 已提交
1006 1007 1008 1009
        g_free(cookie);
        break;
    case QXL_COOKIE_TYPE_RENDER_UPDATE_AREA:
        qxl_render_update_area_done(qxl, cookie);
A
Alon Levy 已提交
1010
        break;
1011 1012
    case QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG:
        break;
A
Alon Levy 已提交
1013 1014 1015
    default:
        fprintf(stderr, "qxl: %s: unexpected cookie type %d\n",
                __func__, cookie->type);
A
Alon Levy 已提交
1016
        g_free(cookie);
A
Alon Levy 已提交
1017 1018 1019
    }
}

1020 1021 1022 1023 1024 1025 1026
/* called from spice server thread context only */
static void interface_set_client_capabilities(QXLInstance *sin,
                                              uint8_t client_present,
                                              uint8_t caps[58])
{
    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);

1027 1028 1029 1030 1031 1032
    if (qxl->revision < 4) {
        trace_qxl_set_client_capabilities_unsupported_by_revision(qxl->id,
                                                              qxl->revision);
        return;
    }

1033 1034 1035 1036 1037
    if (runstate_check(RUN_STATE_INMIGRATE) ||
        runstate_check(RUN_STATE_POSTMIGRATE)) {
        return;
    }

1038
    qxl->shadow_rom.client_present = client_present;
1039 1040
    memcpy(qxl->shadow_rom.client_capabilities, caps,
           sizeof(qxl->shadow_rom.client_capabilities));
1041
    qxl->rom->client_present = client_present;
1042 1043
    memcpy(qxl->rom->client_capabilities, caps,
           sizeof(qxl->rom->client_capabilities));
1044 1045 1046 1047 1048
    qxl_rom_set_dirty(qxl);

    qxl_send_events(qxl, QXL_INTERRUPT_CLIENT);
}

1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
static bool qxl_rom_monitors_config_changed(QXLRom *rom,
        VDAgentMonitorsConfig *monitors_config,
        unsigned int max_outputs)
{
    int i;
    unsigned int monitors_count;

    monitors_count = MIN(monitors_config->num_of_monitors, max_outputs);

    if (rom->client_monitors_config.count != monitors_count) {
        return true;
    }

    for (i = 0 ; i < rom->client_monitors_config.count ; ++i) {
        VDAgentMonConfig *monitor = &monitors_config->monitors[i];
        QXLURect *rect = &rom->client_monitors_config.heads[i];
        /* monitor->depth ignored */
        if ((rect->left != monitor->x) ||
            (rect->top != monitor->y)  ||
            (rect->right != monitor->x + monitor->width) ||
            (rect->bottom != monitor->y + monitor->height)) {
            return true;
        }
    }

    return false;
}

1077 1078 1079 1080 1081 1082 1083
/* called from main context only */
static int interface_client_monitors_config(QXLInstance *sin,
                                        VDAgentMonitorsConfig *monitors_config)
{
    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
    QXLRom *rom = memory_region_get_ram_ptr(&qxl->rom_bar);
    int i;
1084
    unsigned max_outputs = ARRAY_SIZE(rom->client_monitors_config.heads);
1085
    bool config_changed = false;
1086

1087 1088 1089 1090 1091
    if (qxl->revision < 4) {
        trace_qxl_client_monitors_config_unsupported_by_device(qxl->id,
                                                               qxl->revision);
        return 0;
    }
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107
    /*
     * Older windows drivers set int_mask to 0 when their ISR is called,
     * then later set it to ~0. So it doesn't relate to the actual interrupts
     * handled. However, they are old, so clearly they don't support this
     * interrupt
     */
    if (qxl->ram->int_mask == 0 || qxl->ram->int_mask == ~0 ||
        !(qxl->ram->int_mask & QXL_INTERRUPT_CLIENT_MONITORS_CONFIG)) {
        trace_qxl_client_monitors_config_unsupported_by_guest(qxl->id,
                                                            qxl->ram->int_mask,
                                                            monitors_config);
        return 0;
    }
    if (!monitors_config) {
        return 1;
    }
1108 1109 1110 1111 1112 1113 1114 1115

#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */
    /* limit number of outputs based on setting limit */
    if (qxl->max_outputs && qxl->max_outputs <= max_outputs) {
        max_outputs = qxl->max_outputs;
    }
#endif

1116 1117 1118 1119
    config_changed = qxl_rom_monitors_config_changed(rom,
                                                     monitors_config,
                                                     max_outputs);

1120 1121 1122 1123
    memset(&rom->client_monitors_config, 0,
           sizeof(rom->client_monitors_config));
    rom->client_monitors_config.count = monitors_config->num_of_monitors;
    /* monitors_config->flags ignored */
1124
    if (rom->client_monitors_config.count >= max_outputs) {
1125 1126
        trace_qxl_client_monitors_config_capped(qxl->id,
                                monitors_config->num_of_monitors,
1127 1128
                                max_outputs);
        rom->client_monitors_config.count = max_outputs;
1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148
    }
    for (i = 0 ; i < rom->client_monitors_config.count ; ++i) {
        VDAgentMonConfig *monitor = &monitors_config->monitors[i];
        QXLURect *rect = &rom->client_monitors_config.heads[i];
        /* monitor->depth ignored */
        rect->left = monitor->x;
        rect->top = monitor->y;
        rect->right = monitor->x + monitor->width;
        rect->bottom = monitor->y + monitor->height;
    }
    rom->client_monitors_config_crc = qxl_crc32(
            (const uint8_t *)&rom->client_monitors_config,
            sizeof(rom->client_monitors_config));
    trace_qxl_client_monitors_config_crc(qxl->id,
            sizeof(rom->client_monitors_config),
            rom->client_monitors_config_crc);

    trace_qxl_interrupt_client_monitors_config(qxl->id,
                        rom->client_monitors_config.count,
                        rom->client_monitors_config.heads);
1149 1150 1151
    if (config_changed) {
        qxl_send_events(qxl, QXL_INTERRUPT_CLIENT_MONITORS_CONFIG);
    }
1152 1153 1154
    return 1;
}

G
Gerd Hoffmann 已提交
1155 1156 1157 1158 1159 1160 1161 1162
static const QXLInterface qxl_interface = {
    .base.type               = SPICE_INTERFACE_QXL,
    .base.description        = "qxl gpu",
    .base.major_version      = SPICE_INTERFACE_QXL_MAJOR,
    .base.minor_version      = SPICE_INTERFACE_QXL_MINOR,

    .attache_worker          = interface_attach_worker,
    .set_compression_level   = interface_set_compression_level,
1163
#if SPICE_NEEDS_SET_MM_TIME
G
Gerd Hoffmann 已提交
1164
    .set_mm_time             = interface_set_mm_time,
1165
#endif
G
Gerd Hoffmann 已提交
1166 1167 1168 1169 1170 1171 1172 1173 1174 1175
    .get_init_info           = interface_get_init_info,

    /* the callbacks below are called from spice server thread context */
    .get_command             = interface_get_command,
    .req_cmd_notification    = interface_req_cmd_notification,
    .release_resource        = interface_release_resource,
    .get_cursor_command      = interface_get_cursor_command,
    .req_cursor_notification = interface_req_cursor_notification,
    .notify_update           = interface_notify_update,
    .flush_resources         = interface_flush_resources,
1176
    .async_complete          = interface_async_complete,
A
Alon Levy 已提交
1177
    .update_area_complete    = interface_update_area_complete,
1178
    .set_client_capabilities = interface_set_client_capabilities,
1179
    .client_monitors_config = interface_client_monitors_config,
G
Gerd Hoffmann 已提交
1180 1181
};

1182 1183 1184 1185
static const GraphicHwOps qxl_ops = {
    .gfx_update  = qxl_hw_update,
};

G
Gerd Hoffmann 已提交
1186 1187 1188 1189 1190
static void qxl_enter_vga_mode(PCIQXLDevice *d)
{
    if (d->mode == QXL_MODE_VGA) {
        return;
    }
A
Alon Levy 已提交
1191
    trace_qxl_enter_vga_mode(d->id);
1192 1193 1194
#if SPICE_SERVER_VERSION >= 0x000c03 /* release 0.12.3 */
    spice_qxl_driver_unload(&d->ssd.qxl);
#endif
1195
    graphic_console_set_hwops(d->ssd.dcl.con, d->vga.hw_ops, &d->vga);
1196
    update_displaychangelistener(&d->ssd.dcl, GUI_REFRESH_INTERVAL_DEFAULT);
G
Gerd Hoffmann 已提交
1197 1198
    qemu_spice_create_host_primary(&d->ssd);
    d->mode = QXL_MODE_VGA;
1199
    qemu_spice_display_switch(&d->ssd, d->ssd.ds);
1200
    vga_dirty_log_start(&d->vga);
1201
    graphic_hw_update(d->vga.con);
G
Gerd Hoffmann 已提交
1202 1203 1204 1205 1206 1207 1208
}

static void qxl_exit_vga_mode(PCIQXLDevice *d)
{
    if (d->mode != QXL_MODE_VGA) {
        return;
    }
A
Alon Levy 已提交
1209
    trace_qxl_exit_vga_mode(d->id);
1210
    graphic_console_set_hwops(d->ssd.dcl.con, &qxl_ops, d);
1211
    update_displaychangelistener(&d->ssd.dcl, GUI_REFRESH_INTERVAL_IDLE);
1212
    vga_dirty_log_stop(&d->vga);
1213
    qxl_destroy_primary(d, QXL_SYNC);
G
Gerd Hoffmann 已提交
1214 1215
}

1216
static void qxl_update_irq(PCIQXLDevice *d)
G
Gerd Hoffmann 已提交
1217 1218 1219 1220
{
    uint32_t pending = le32_to_cpu(d->ram->int_pending);
    uint32_t mask    = le32_to_cpu(d->ram->int_mask);
    int level = !!(pending & mask);
1221
    pci_set_irq(&d->pci, level);
G
Gerd Hoffmann 已提交
1222 1223 1224 1225 1226 1227
    qxl_ring_set_dirty(d);
}

static void qxl_check_state(PCIQXLDevice *d)
{
    QXLRam *ram = d->ram;
1228
    int spice_display_running = qemu_spice_display_is_running(&d->ssd);
G
Gerd Hoffmann 已提交
1229

1230 1231
    assert(!spice_display_running || SPICE_RING_IS_EMPTY(&ram->cmd_ring));
    assert(!spice_display_running || SPICE_RING_IS_EMPTY(&ram->cursor_ring));
G
Gerd Hoffmann 已提交
1232 1233 1234 1235 1236 1237
}

static void qxl_reset_state(PCIQXLDevice *d)
{
    QXLRom *rom = d->rom;

1238
    qxl_check_state(d);
G
Gerd Hoffmann 已提交
1239 1240 1241 1242 1243 1244 1245
    d->shadow_rom.update_id = cpu_to_le32(0);
    *rom = d->shadow_rom;
    qxl_rom_set_dirty(d);
    init_qxl_ram(d);
    d->num_free_res = 0;
    d->last_release = NULL;
    memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty));
A
Alon Levy 已提交
1246
    qxl_update_irq(d);
G
Gerd Hoffmann 已提交
1247 1248 1249 1250
}

static void qxl_soft_reset(PCIQXLDevice *d)
{
A
Alon Levy 已提交
1251
    trace_qxl_soft_reset(d->id);
G
Gerd Hoffmann 已提交
1252
    qxl_check_state(d);
1253
    qxl_clear_guest_bug(d);
1254
    qemu_mutex_lock(&d->async_lock);
1255
    d->current_async = QXL_UNDEFINED_IO;
1256
    qemu_mutex_unlock(&d->async_lock);
G
Gerd Hoffmann 已提交
1257

G
Gerd Hoffmann 已提交
1258
    if (d->have_vga) {
G
Gerd Hoffmann 已提交
1259 1260 1261 1262 1263 1264 1265 1266
        qxl_enter_vga_mode(d);
    } else {
        d->mode = QXL_MODE_UNDEFINED;
    }
}

static void qxl_hard_reset(PCIQXLDevice *d, int loadvm)
{
1267 1268
    bool startstop = qemu_spice_display_is_running(&d->ssd);

A
Alon Levy 已提交
1269
    trace_qxl_hard_reset(d->id, loadvm);
G
Gerd Hoffmann 已提交
1270

1271 1272 1273 1274
    if (startstop) {
        qemu_spice_display_stop();
    }

G
Gerd Hoffmann 已提交
1275 1276
    qxl_spice_reset_cursor(d);
    qxl_spice_reset_image_cache(d);
G
Gerd Hoffmann 已提交
1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287
    qxl_reset_surfaces(d);
    qxl_reset_memslots(d);

    /* pre loadvm reset must not touch QXLRam.  This lives in
     * device memory, is migrated together with RAM and thus
     * already loaded at this point */
    if (!loadvm) {
        qxl_reset_state(d);
    }
    qemu_spice_create_host_memslot(&d->ssd);
    qxl_soft_reset(d);
1288

1289 1290 1291 1292 1293 1294
    if (d->migration_blocker) {
        migrate_del_blocker(d->migration_blocker);
        error_free(d->migration_blocker);
        d->migration_blocker = NULL;
    }

1295 1296 1297
    if (startstop) {
        qemu_spice_display_start();
    }
G
Gerd Hoffmann 已提交
1298 1299 1300 1301
}

static void qxl_reset_handler(DeviceState *dev)
{
G
Gonglei 已提交
1302
    PCIQXLDevice *d = PCI_QXL(PCI_DEVICE(dev));
A
Alon Levy 已提交
1303

G
Gerd Hoffmann 已提交
1304 1305 1306 1307 1308 1309 1310 1311
    qxl_hard_reset(d, 0);
}

static void qxl_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
{
    VGACommonState *vga = opaque;
    PCIQXLDevice *qxl = container_of(vga, PCIQXLDevice, vga);

A
Alon Levy 已提交
1312
    trace_qxl_io_write_vga(qxl->id, qxl_mode_to_string(qxl->mode), addr, val);
G
Gerd Hoffmann 已提交
1313
    if (qxl->mode != QXL_MODE_VGA) {
1314
        qxl_destroy_primary(qxl, QXL_SYNC);
G
Gerd Hoffmann 已提交
1315 1316 1317 1318 1319
        qxl_soft_reset(qxl);
    }
    vga_ioport_write(opaque, addr, val);
}

G
Gerd Hoffmann 已提交
1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333
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(),
};

1334 1335
static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta,
                           qxl_async_io async)
G
Gerd Hoffmann 已提交
1336 1337 1338 1339
{
    static const int regions[] = {
        QXL_RAM_RANGE_INDEX,
        QXL_VRAM_RANGE_INDEX,
G
Gerd Hoffmann 已提交
1340
        QXL_VRAM64_RANGE_INDEX,
G
Gerd Hoffmann 已提交
1341 1342 1343 1344 1345 1346
    };
    uint64_t guest_start;
    uint64_t guest_end;
    int pci_region;
    pcibus_t pci_start;
    pcibus_t pci_end;
1347
    MemoryRegion *mr;
G
Gerd Hoffmann 已提交
1348 1349 1350 1351 1352 1353 1354
    intptr_t virt_start;
    QXLDevMemSlot memslot;
    int i;

    guest_start = le64_to_cpu(d->guest_slots[slot_id].slot.mem_start);
    guest_end   = le64_to_cpu(d->guest_slots[slot_id].slot.mem_end);

A
Alon Levy 已提交
1355
    trace_qxl_memslot_add_guest(d->id, slot_id, guest_start, guest_end);
G
Gerd Hoffmann 已提交
1356

1357
    if (slot_id >= NUM_MEMSLOTS) {
1358
        qxl_set_guest_bug(d, "%s: slot_id >= NUM_MEMSLOTS %d >= %d", __func__,
1359 1360 1361 1362
                      slot_id, NUM_MEMSLOTS);
        return 1;
    }
    if (guest_start > guest_end) {
1363
        qxl_set_guest_bug(d, "%s: guest_start > guest_end 0x%" PRIx64
1364 1365 1366
                         " > 0x%" PRIx64, __func__, guest_start, guest_end);
        return 1;
    }
G
Gerd Hoffmann 已提交
1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386

    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;
    }
1387
    if (i == ARRAY_SIZE(regions)) {
1388
        qxl_set_guest_bug(d, "%s: finished loop without match", __func__);
1389 1390
        return 1;
    }
G
Gerd Hoffmann 已提交
1391 1392 1393

    switch (pci_region) {
    case QXL_RAM_RANGE_INDEX:
1394
        mr = &d->vga.vram;
G
Gerd Hoffmann 已提交
1395 1396
        break;
    case QXL_VRAM_RANGE_INDEX:
G
Gerd Hoffmann 已提交
1397
    case 4 /* vram 64bit */:
1398
        mr = &d->vram_bar;
G
Gerd Hoffmann 已提交
1399 1400 1401
        break;
    default:
        /* should not happen */
1402
        qxl_set_guest_bug(d, "%s: pci_region = %d", __func__, pci_region);
1403
        return 1;
G
Gerd Hoffmann 已提交
1404 1405
    }

1406
    virt_start = (intptr_t)memory_region_get_ram_ptr(mr);
G
Gerd Hoffmann 已提交
1407 1408 1409 1410 1411 1412 1413 1414
    memslot.slot_id = slot_id;
    memslot.slot_group_id = MEMSLOT_GROUP_GUEST; /* guest group */
    memslot.virt_start = virt_start + (guest_start - pci_start);
    memslot.virt_end   = virt_start + (guest_end   - pci_start);
    memslot.addr_delta = memslot.virt_start - delta;
    memslot.generation = d->rom->slot_generation = 0;
    qxl_rom_set_dirty(d);

1415
    qemu_spice_add_memslot(&d->ssd, &memslot, async);
1416 1417
    d->guest_slots[slot_id].mr = mr;
    d->guest_slots[slot_id].offset = memslot.virt_start - virt_start;
G
Gerd Hoffmann 已提交
1418 1419 1420
    d->guest_slots[slot_id].size = memslot.virt_end - memslot.virt_start;
    d->guest_slots[slot_id].delta = delta;
    d->guest_slots[slot_id].active = 1;
1421
    return 0;
G
Gerd Hoffmann 已提交
1422 1423 1424 1425
}

static void qxl_del_memslot(PCIQXLDevice *d, uint32_t slot_id)
{
1426
    qemu_spice_del_memslot(&d->ssd, MEMSLOT_GROUP_HOST, slot_id);
G
Gerd Hoffmann 已提交
1427 1428 1429 1430 1431
    d->guest_slots[slot_id].active = 0;
}

static void qxl_reset_memslots(PCIQXLDevice *d)
{
G
Gerd Hoffmann 已提交
1432
    qxl_spice_reset_memslots(d);
G
Gerd Hoffmann 已提交
1433 1434 1435 1436 1437
    memset(&d->guest_slots, 0, sizeof(d->guest_slots));
}

static void qxl_reset_surfaces(PCIQXLDevice *d)
{
A
Alon Levy 已提交
1438
    trace_qxl_reset_surfaces(d->id);
G
Gerd Hoffmann 已提交
1439
    d->mode = QXL_MODE_UNDEFINED;
1440
    qxl_spice_destroy_surfaces(d, QXL_SYNC);
G
Gerd Hoffmann 已提交
1441 1442
}

1443
/* can be also called from spice server thread context */
1444 1445
static bool qxl_get_check_slot_offset(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
                                      uint32_t *s, uint64_t *o)
G
Gerd Hoffmann 已提交
1446 1447 1448 1449 1450
{
    uint64_t phys   = le64_to_cpu(pqxl);
    uint32_t slot   = (phys >> (64 -  8)) & 0xff;
    uint64_t offset = phys & 0xffffffffffff;

1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461
    if (slot >= NUM_MEMSLOTS) {
        qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot,
                          NUM_MEMSLOTS);
        return false;
    }
    if (!qxl->guest_slots[slot].active) {
        qxl_set_guest_bug(qxl, "inactive slot %d\n", slot);
        return false;
    }
    if (offset < qxl->guest_slots[slot].delta) {
        qxl_set_guest_bug(qxl,
1462
                          "slot %d offset %"PRIu64" < delta %"PRIu64"\n",
1463
                          slot, offset, qxl->guest_slots[slot].delta);
1464 1465 1466 1467 1468
        return false;
    }
    offset -= qxl->guest_slots[slot].delta;
    if (offset > qxl->guest_slots[slot].size) {
        qxl_set_guest_bug(qxl,
1469
                          "slot %d offset %"PRIu64" > size %"PRIu64"\n",
1470
                          slot, offset, qxl->guest_slots[slot].size);
1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483
        return false;
    }

    *s = slot;
    *o = offset;
    return true;
}

/* can be also called from spice server thread context */
void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id)
{
    uint64_t offset;
    uint32_t slot;
1484
    void *ptr;
1485 1486 1487 1488 1489 1490 1491

    switch (group_id) {
    case MEMSLOT_GROUP_HOST:
        offset = le64_to_cpu(pqxl) & 0xffffffffffff;
        return (void *)(intptr_t)offset;
    case MEMSLOT_GROUP_GUEST:
        if (!qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset)) {
1492 1493
            return NULL;
        }
1494 1495 1496 1497
        ptr = memory_region_get_ram_ptr(qxl->guest_slots[slot].mr);
        ptr += qxl->guest_slots[slot].offset;
        ptr += offset;
        return ptr;
G
Gerd Hoffmann 已提交
1498
    }
1499
    return NULL;
G
Gerd Hoffmann 已提交
1500 1501
}

1502 1503 1504 1505 1506 1507 1508 1509
static void qxl_create_guest_primary_complete(PCIQXLDevice *qxl)
{
    /* for local rendering */
    qxl_render_resize(qxl);
}

static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
                                     qxl_async_io async)
G
Gerd Hoffmann 已提交
1510 1511 1512
{
    QXLDevSurfaceCreate surface;
    QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
1513
    uint32_t requested_height = le32_to_cpu(sc->height);
1514 1515
    int requested_stride = le32_to_cpu(sc->stride);

1516 1517 1518 1519 1520 1521 1522
    if (requested_stride == INT32_MIN ||
        abs(requested_stride) * (uint64_t)requested_height
                                        > qxl->vgamem_size) {
        qxl_set_guest_bug(qxl, "%s: requested primary larger than framebuffer"
                               " stride %d x height %" PRIu32 " > %" PRIu32,
                               __func__, requested_stride, requested_height,
                               qxl->vgamem_size);
1523 1524
        return;
    }
G
Gerd Hoffmann 已提交
1525

1526
    if (qxl->mode == QXL_MODE_NATIVE) {
1527
        qxl_set_guest_bug(qxl, "%s: nop since already in QXL_MODE_NATIVE",
1528 1529
                      __func__);
    }
G
Gerd Hoffmann 已提交
1530 1531 1532 1533 1534 1535 1536 1537 1538 1539
    qxl_exit_vga_mode(qxl);

    surface.format     = le32_to_cpu(sc->format);
    surface.height     = le32_to_cpu(sc->height);
    surface.mem        = le64_to_cpu(sc->mem);
    surface.position   = le32_to_cpu(sc->position);
    surface.stride     = le32_to_cpu(sc->stride);
    surface.width      = le32_to_cpu(sc->width);
    surface.type       = le32_to_cpu(sc->type);
    surface.flags      = le32_to_cpu(sc->flags);
A
Alon Levy 已提交
1540 1541 1542 1543
    trace_qxl_create_guest_primary(qxl->id, sc->width, sc->height, sc->mem,
                                   sc->format, sc->position);
    trace_qxl_create_guest_primary_rest(qxl->id, sc->stride, sc->type,
                                        sc->flags);
G
Gerd Hoffmann 已提交
1544

1545 1546 1547 1548 1549 1550
    if ((surface.stride & 0x3) != 0) {
        qxl_set_guest_bug(qxl, "primary surface stride = %d %% 4 != 0",
                          surface.stride);
        return;
    }

G
Gerd Hoffmann 已提交
1551 1552 1553 1554 1555 1556 1557 1558
    surface.mouse_mode = true;
    surface.group_id   = MEMSLOT_GROUP_GUEST;
    if (loadvm) {
        surface.flags |= QXL_SURF_FLAG_KEEP_DATA;
    }

    qxl->mode = QXL_MODE_NATIVE;
    qxl->cmdflags = 0;
1559
    qemu_spice_create_primary_surface(&qxl->ssd, 0, &surface, async);
G
Gerd Hoffmann 已提交
1560

1561 1562 1563
    if (async == QXL_SYNC) {
        qxl_create_guest_primary_complete(qxl);
    }
G
Gerd Hoffmann 已提交
1564 1565
}

1566 1567 1568
/* return 1 if surface destoy was initiated (in QXL_ASYNC case) or
 * done (in QXL_SYNC case), 0 otherwise. */
static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async)
G
Gerd Hoffmann 已提交
1569 1570
{
    if (d->mode == QXL_MODE_UNDEFINED) {
1571
        return 0;
G
Gerd Hoffmann 已提交
1572
    }
A
Alon Levy 已提交
1573
    trace_qxl_destroy_primary(d->id);
G
Gerd Hoffmann 已提交
1574
    d->mode = QXL_MODE_UNDEFINED;
1575
    qemu_spice_destroy_primary_surface(&d->ssd, 0, async);
Y
Yonit Halperin 已提交
1576
    qxl_spice_reset_cursor(d);
1577
    return 1;
G
Gerd Hoffmann 已提交
1578 1579
}

G
Gerd Hoffmann 已提交
1580
static void qxl_set_mode(PCIQXLDevice *d, unsigned int modenr, int loadvm)
G
Gerd Hoffmann 已提交
1581 1582 1583 1584 1585 1586 1587 1588 1589
{
    pcibus_t start = d->pci.io_regions[QXL_RAM_RANGE_INDEX].addr;
    pcibus_t end   = d->pci.io_regions[QXL_RAM_RANGE_INDEX].size + start;
    QXLMode *mode = d->modes->modes + modenr;
    uint64_t devmem = d->pci.io_regions[QXL_RAM_RANGE_INDEX].addr;
    QXLMemSlot slot = {
        .mem_start = start,
        .mem_end = end
    };
G
Gerd Hoffmann 已提交
1590 1591 1592 1593 1594 1595

    if (modenr >= d->modes->n_modes) {
        qxl_set_guest_bug(d, "mode number out of range");
        return;
    }

G
Gerd Hoffmann 已提交
1596 1597 1598 1599 1600 1601 1602 1603 1604 1605
    QXLSurfaceCreate surface = {
        .width      = mode->x_res,
        .height     = mode->y_res,
        .stride     = -mode->x_res * 4,
        .format     = SPICE_SURFACE_FMT_32_xRGB,
        .flags      = loadvm ? QXL_SURF_FLAG_KEEP_DATA : 0,
        .mouse_mode = true,
        .mem        = devmem + d->shadow_rom.draw_area_offset,
    };

A
Alon Levy 已提交
1606 1607
    trace_qxl_set_mode(d->id, modenr, mode->x_res, mode->y_res, mode->bits,
                       devmem);
G
Gerd Hoffmann 已提交
1608 1609 1610 1611 1612
    if (!loadvm) {
        qxl_hard_reset(d, 0);
    }

    d->guest_slots[0].slot = slot;
1613
    assert(qxl_add_memslot(d, 0, devmem, QXL_SYNC) == 0);
G
Gerd Hoffmann 已提交
1614 1615

    d->guest_primary.surface = surface;
1616
    qxl_create_guest_primary(d, 0, QXL_SYNC);
G
Gerd Hoffmann 已提交
1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627

    d->mode = QXL_MODE_COMPAT;
    d->cmdflags = QXL_COMMAND_FLAG_COMPAT;
    if (mode->bits == 16) {
        d->cmdflags |= QXL_COMMAND_FLAG_COMPAT_16BPP;
    }
    d->shadow_rom.mode = cpu_to_le32(modenr);
    d->rom->mode = cpu_to_le32(modenr);
    qxl_rom_set_dirty(d);
}

A
Avi Kivity 已提交
1628
static void ioport_write(void *opaque, hwaddr addr,
1629
                         uint64_t val, unsigned size)
G
Gerd Hoffmann 已提交
1630 1631
{
    PCIQXLDevice *d = opaque;
1632
    uint32_t io_port = addr;
1633 1634
    qxl_async_io async = QXL_SYNC;
    uint32_t orig_io_port = io_port;
G
Gerd Hoffmann 已提交
1635

1636
    if (d->guest_bug && io_port != QXL_IO_RESET) {
1637 1638 1639
        return;
    }

1640
    if (d->revision <= QXL_REVISION_STABLE_V10 &&
1641
        io_port > QXL_IO_FLUSH_RELEASE) {
1642 1643 1644 1645 1646
        qxl_set_guest_bug(d, "unsupported io %d for revision %d\n",
            io_port, d->revision);
        return;
    }

G
Gerd Hoffmann 已提交
1647 1648 1649 1650 1651 1652
    switch (io_port) {
    case QXL_IO_RESET:
    case QXL_IO_SET_MODE:
    case QXL_IO_MEMSLOT_ADD:
    case QXL_IO_MEMSLOT_DEL:
    case QXL_IO_CREATE_PRIMARY:
1653
    case QXL_IO_UPDATE_IRQ:
A
Alon Levy 已提交
1654
    case QXL_IO_LOG:
1655 1656
    case QXL_IO_MEMSLOT_ADD_ASYNC:
    case QXL_IO_CREATE_PRIMARY_ASYNC:
G
Gerd Hoffmann 已提交
1657 1658
        break;
    default:
1659
        if (d->mode != QXL_MODE_VGA) {
G
Gerd Hoffmann 已提交
1660
            break;
1661
        }
A
Alon Levy 已提交
1662
        trace_qxl_io_unexpected_vga_mode(d->id,
A
Alon Levy 已提交
1663
            addr, val, io_port_to_string(io_port));
1664 1665
        /* be nice to buggy guest drivers */
        if (io_port >= QXL_IO_UPDATE_AREA_ASYNC &&
1666
            io_port < QXL_IO_RANGE_SIZE) {
1667 1668
            qxl_send_events(d, QXL_INTERRUPT_IO_CMD);
        }
G
Gerd Hoffmann 已提交
1669 1670 1671
        return;
    }

1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691
    /* we change the io_port to avoid ifdeffery in the main switch */
    orig_io_port = io_port;
    switch (io_port) {
    case QXL_IO_UPDATE_AREA_ASYNC:
        io_port = QXL_IO_UPDATE_AREA;
        goto async_common;
    case QXL_IO_MEMSLOT_ADD_ASYNC:
        io_port = QXL_IO_MEMSLOT_ADD;
        goto async_common;
    case QXL_IO_CREATE_PRIMARY_ASYNC:
        io_port = QXL_IO_CREATE_PRIMARY;
        goto async_common;
    case QXL_IO_DESTROY_PRIMARY_ASYNC:
        io_port = QXL_IO_DESTROY_PRIMARY;
        goto async_common;
    case QXL_IO_DESTROY_SURFACE_ASYNC:
        io_port = QXL_IO_DESTROY_SURFACE_WAIT;
        goto async_common;
    case QXL_IO_DESTROY_ALL_SURFACES_ASYNC:
        io_port = QXL_IO_DESTROY_ALL_SURFACES;
1692 1693
        goto async_common;
    case QXL_IO_FLUSH_SURFACES_ASYNC:
1694
    case QXL_IO_MONITORS_CONFIG_ASYNC:
1695 1696 1697 1698
async_common:
        async = QXL_ASYNC;
        qemu_mutex_lock(&d->async_lock);
        if (d->current_async != QXL_UNDEFINED_IO) {
1699
            qxl_set_guest_bug(d, "%d async started before last (%d) complete",
1700 1701 1702 1703 1704 1705 1706 1707 1708 1709
                io_port, d->current_async);
            qemu_mutex_unlock(&d->async_lock);
            return;
        }
        d->current_async = orig_io_port;
        qemu_mutex_unlock(&d->async_lock);
        break;
    default:
        break;
    }
G
Gerd Hoffmann 已提交
1710 1711 1712
    trace_qxl_io_write(d->id, qxl_mode_to_string(d->mode),
                       addr, io_port_to_string(addr),
                       val, size, async);
1713

G
Gerd Hoffmann 已提交
1714 1715 1716
    switch (io_port) {
    case QXL_IO_UPDATE_AREA:
    {
A
Alon Levy 已提交
1717
        QXLCookie *cookie = NULL;
G
Gerd Hoffmann 已提交
1718
        QXLRect update = d->ram->update_area;
A
Alon Levy 已提交
1719

1720
        if (d->ram->update_surface > d->ssd.num_surfaces) {
1721 1722
            qxl_set_guest_bug(d, "QXL_IO_UPDATE_AREA: invalid surface id %d\n",
                              d->ram->update_surface);
1723
            break;
1724
        }
1725 1726
        if (update.left >= update.right || update.top >= update.bottom ||
            update.left < 0 || update.top < 0) {
1727 1728 1729
            qxl_set_guest_bug(d,
                    "QXL_IO_UPDATE_AREA: invalid area (%ux%u)x(%ux%u)\n",
                    update.left, update.top, update.right, update.bottom);
1730 1731 1732 1733 1734
            if (update.left == update.right || update.top == update.bottom) {
                /* old drivers may provide empty area, keep going */
                qxl_clear_guest_bug(d);
                goto cancel_async;
            }
D
Dunrong Huang 已提交
1735 1736
            break;
        }
A
Alon Levy 已提交
1737 1738 1739 1740 1741
        if (async == QXL_ASYNC) {
            cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
                                    QXL_IO_UPDATE_AREA_ASYNC);
            cookie->u.area = update;
        }
G
Gerd Hoffmann 已提交
1742
        qxl_spice_update_area(d, d->ram->update_surface,
A
Alon Levy 已提交
1743 1744
                              cookie ? &cookie->u.area : &update,
                              NULL, 0, 0, async, cookie);
G
Gerd Hoffmann 已提交
1745 1746 1747
        break;
    }
    case QXL_IO_NOTIFY_CMD:
1748
        qemu_spice_wakeup(&d->ssd);
G
Gerd Hoffmann 已提交
1749 1750
        break;
    case QXL_IO_NOTIFY_CURSOR:
1751
        qemu_spice_wakeup(&d->ssd);
G
Gerd Hoffmann 已提交
1752 1753
        break;
    case QXL_IO_UPDATE_IRQ:
1754
        qxl_update_irq(d);
G
Gerd Hoffmann 已提交
1755 1756 1757 1758 1759 1760
        break;
    case QXL_IO_NOTIFY_OOM:
        if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) {
            break;
        }
        d->oom_running = 1;
G
Gerd Hoffmann 已提交
1761
        qxl_spice_oom(d);
G
Gerd Hoffmann 已提交
1762 1763 1764 1765 1766 1767
        d->oom_running = 0;
        break;
    case QXL_IO_SET_MODE:
        qxl_set_mode(d, val, 0);
        break;
    case QXL_IO_LOG:
A
Alon Levy 已提交
1768
        trace_qxl_io_log(d->id, d->ram->log_buf);
G
Gerd Hoffmann 已提交
1769
        if (d->guestdebug) {
P
Peter Maydell 已提交
1770
            fprintf(stderr, "qxl/guest-%d: %" PRId64 ": %s", d->id,
1771
                    qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), d->ram->log_buf);
G
Gerd Hoffmann 已提交
1772 1773 1774 1775 1776 1777
        }
        break;
    case QXL_IO_RESET:
        qxl_hard_reset(d, 0);
        break;
    case QXL_IO_MEMSLOT_ADD:
1778
        if (val >= NUM_MEMSLOTS) {
1779
            qxl_set_guest_bug(d, "QXL_IO_MEMSLOT_ADD: val out of range");
1780 1781 1782
            break;
        }
        if (d->guest_slots[val].active) {
1783 1784
            qxl_set_guest_bug(d,
                        "QXL_IO_MEMSLOT_ADD: memory slot already active");
1785 1786
            break;
        }
G
Gerd Hoffmann 已提交
1787
        d->guest_slots[val].slot = d->ram->mem_slot;
1788
        qxl_add_memslot(d, val, 0, async);
G
Gerd Hoffmann 已提交
1789 1790
        break;
    case QXL_IO_MEMSLOT_DEL:
1791
        if (val >= NUM_MEMSLOTS) {
1792
            qxl_set_guest_bug(d, "QXL_IO_MEMSLOT_DEL: val out of range");
1793 1794
            break;
        }
G
Gerd Hoffmann 已提交
1795 1796 1797
        qxl_del_memslot(d, val);
        break;
    case QXL_IO_CREATE_PRIMARY:
1798
        if (val != 0) {
1799
            qxl_set_guest_bug(d, "QXL_IO_CREATE_PRIMARY (async=%d): val != 0",
1800 1801
                          async);
            goto cancel_async;
1802
        }
G
Gerd Hoffmann 已提交
1803
        d->guest_primary.surface = d->ram->create_surface;
1804
        qxl_create_guest_primary(d, 0, async);
G
Gerd Hoffmann 已提交
1805 1806
        break;
    case QXL_IO_DESTROY_PRIMARY:
1807
        if (val != 0) {
1808
            qxl_set_guest_bug(d, "QXL_IO_DESTROY_PRIMARY (async=%d): val != 0",
1809 1810 1811 1812
                          async);
            goto cancel_async;
        }
        if (!qxl_destroy_primary(d, async)) {
A
Alon Levy 已提交
1813 1814
            trace_qxl_io_destroy_primary_ignored(d->id,
                                                 qxl_mode_to_string(d->mode));
1815
            goto cancel_async;
1816
        }
G
Gerd Hoffmann 已提交
1817 1818
        break;
    case QXL_IO_DESTROY_SURFACE_WAIT:
1819
        if (val >= d->ssd.num_surfaces) {
1820
            qxl_set_guest_bug(d, "QXL_IO_DESTROY_SURFACE (async=%d):"
1821
                             "%" PRIu64 " >= NUM_SURFACES", async, val);
1822 1823 1824
            goto cancel_async;
        }
        qxl_spice_destroy_surface_wait(d, val, async);
G
Gerd Hoffmann 已提交
1825
        break;
1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838
    case QXL_IO_FLUSH_RELEASE: {
        QXLReleaseRing *ring = &d->ram->release_ring;
        if (ring->prod - ring->cons + 1 == ring->num_items) {
            fprintf(stderr,
                "ERROR: no flush, full release ring [p%d,%dc]\n",
                ring->prod, ring->cons);
        }
        qxl_push_free_res(d, 1 /* flush */);
        break;
    }
    case QXL_IO_FLUSH_SURFACES_ASYNC:
        qxl_spice_flush_surfaces_async(d);
        break;
G
Gerd Hoffmann 已提交
1839
    case QXL_IO_DESTROY_ALL_SURFACES:
1840 1841
        d->mode = QXL_MODE_UNDEFINED;
        qxl_spice_destroy_surfaces(d, async);
G
Gerd Hoffmann 已提交
1842
        break;
1843 1844 1845
    case QXL_IO_MONITORS_CONFIG_ASYNC:
        qxl_spice_monitors_config_async(d, 0);
        break;
G
Gerd Hoffmann 已提交
1846
    default:
1847
        qxl_set_guest_bug(d, "%s: unexpected ioport=0x%x\n", __func__, io_port);
G
Gerd Hoffmann 已提交
1848
    }
1849 1850 1851 1852 1853 1854 1855 1856
    return;
cancel_async:
    if (async) {
        qxl_send_events(d, QXL_INTERRUPT_IO_CMD);
        qemu_mutex_lock(&d->async_lock);
        d->current_async = QXL_UNDEFINED_IO;
        qemu_mutex_unlock(&d->async_lock);
    }
G
Gerd Hoffmann 已提交
1857 1858
}

A
Avi Kivity 已提交
1859
static uint64_t ioport_read(void *opaque, hwaddr addr,
1860
                            unsigned size)
G
Gerd Hoffmann 已提交
1861
{
A
Alon Levy 已提交
1862
    PCIQXLDevice *qxl = opaque;
G
Gerd Hoffmann 已提交
1863

A
Alon Levy 已提交
1864
    trace_qxl_io_read_unexpected(qxl->id);
G
Gerd Hoffmann 已提交
1865 1866 1867
    return 0xff;
}

1868 1869 1870 1871 1872 1873 1874 1875
static const MemoryRegionOps qxl_io_ops = {
    .read = ioport_read,
    .write = ioport_write,
    .valid = {
        .min_access_size = 1,
        .max_access_size = 1,
    },
};
G
Gerd Hoffmann 已提交
1876

1877
static void qxl_update_irq_bh(void *opaque)
G
Gerd Hoffmann 已提交
1878 1879
{
    PCIQXLDevice *d = opaque;
1880
    qxl_update_irq(d);
G
Gerd Hoffmann 已提交
1881 1882 1883 1884 1885 1886 1887
}

static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
{
    uint32_t old_pending;
    uint32_t le_events = cpu_to_le32(events);

A
Alon Levy 已提交
1888
    trace_qxl_send_events(d->id, events);
1889 1890 1891 1892 1893 1894 1895
    if (!qemu_spice_display_is_running(&d->ssd)) {
        /* spice-server tracks guest running state and should not do this */
        fprintf(stderr, "%s: spice-server bug: guest stopped, ignoring\n",
                __func__);
        trace_qxl_send_events_vm_stopped(d->id, events);
        return;
    }
1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920
    /*
     * Older versions of Spice forgot to define the QXLRam struct
     * with the '__aligned__(4)' attribute. clang 7 and newer will
     * thus warn that atomic_fetch_or(&d->ram->int_pending, ...)
     * might be a misaligned atomic access, and will generate an
     * out-of-line call for it, which results in a link error since
     * we don't currently link against libatomic.
     *
     * In fact we set up d->ram in init_qxl_ram() so it always starts
     * at a 4K boundary, so we know that &d->ram->int_pending is
     * naturally aligned for a uint32_t. Newer Spice versions
     * (with Spice commit beda5ec7a6848be20c0cac2a9a8ef2a41e8069c1)
     * will fix the bug directly. To deal with older versions,
     * we tell the compiler to assume the address really is aligned.
     * Any compiler which cares about the misalignment will have
     * __builtin_assume_aligned.
     */
#ifdef HAS_ASSUME_ALIGNED
#define ALIGNED_UINT32_PTR(P) ((uint32_t *)__builtin_assume_aligned(P, 4))
#else
#define ALIGNED_UINT32_PTR(P) ((uint32_t *)P)
#endif

    old_pending = atomic_fetch_or(ALIGNED_UINT32_PTR(&d->ram->int_pending),
                                  le_events);
G
Gerd Hoffmann 已提交
1921 1922 1923
    if ((old_pending & le_events) == le_events) {
        return;
    }
1924
    qemu_bh_schedule(d->update_irq);
G
Gerd Hoffmann 已提交
1925 1926 1927 1928 1929 1930 1931 1932
}

/* graphics console */

static void qxl_hw_update(void *opaque)
{
    PCIQXLDevice *qxl = opaque;

1933
    qxl_render_update(qxl);
G
Gerd Hoffmann 已提交
1934 1935
}

G
Gerd Hoffmann 已提交
1936 1937 1938
static void qxl_dirty_one_surface(PCIQXLDevice *qxl, QXLPHYSICAL pqxl,
                                  uint32_t height, int32_t stride)
{
1939 1940
    uint64_t offset, size;
    uint32_t slot;
G
Gerd Hoffmann 已提交
1941 1942 1943 1944
    bool rc;

    rc = qxl_get_check_slot_offset(qxl, pqxl, &slot, &offset);
    assert(rc == true);
1945 1946
    size = (uint64_t)height * abs(stride);
    trace_qxl_surfaces_dirty(qxl->id, offset, size);
G
Gerd Hoffmann 已提交
1947
    qxl_set_dirty(qxl->guest_slots[slot].mr,
1948 1949
                  qxl->guest_slots[slot].offset + offset,
                  qxl->guest_slots[slot].offset + offset + size);
G
Gerd Hoffmann 已提交
1950 1951
}

1952 1953 1954 1955
static void qxl_dirty_surfaces(PCIQXLDevice *qxl)
{
    int i;

1956
    if (qxl->mode != QXL_MODE_NATIVE && qxl->mode != QXL_MODE_COMPAT) {
1957 1958 1959 1960
        return;
    }

    /* dirty the primary surface */
G
Gerd Hoffmann 已提交
1961 1962 1963
    qxl_dirty_one_surface(qxl, qxl->guest_primary.surface.mem,
                          qxl->guest_primary.surface.height,
                          qxl->guest_primary.surface.stride);
1964 1965

    /* dirty the off-screen surfaces */
1966
    for (i = 0; i < qxl->ssd.num_surfaces; i++) {
1967 1968 1969 1970 1971 1972 1973 1974
        QXLSurfaceCmd *cmd;

        if (qxl->guest_surfaces.cmds[i] == 0) {
            continue;
        }

        cmd = qxl_phys2virt(qxl, qxl->guest_surfaces.cmds[i],
                            MEMSLOT_GROUP_GUEST);
1975
        assert(cmd);
1976
        assert(cmd->type == QXL_SURFACE_CMD_CREATE);
G
Gerd Hoffmann 已提交
1977 1978 1979
        qxl_dirty_one_surface(qxl, cmd->u.surface_create.data,
                              cmd->u.surface_create.height,
                              cmd->u.surface_create.stride);
1980 1981 1982
    }
}

1983 1984
static void qxl_vm_change_state_handler(void *opaque, int running,
                                        RunState state)
G
Gerd Hoffmann 已提交
1985 1986 1987
{
    PCIQXLDevice *qxl = opaque;

1988 1989 1990
    if (running) {
        /*
         * if qxl_send_events was called from spice server context before
1991
         * migration ended, qxl_update_irq for these events might not have been
1992 1993
         * called
         */
1994
         qxl_update_irq(qxl);
1995 1996 1997
    } else {
        /* make sure surfaces are saved before migration */
        qxl_dirty_surfaces(qxl);
G
Gerd Hoffmann 已提交
1998 1999 2000 2001 2002
    }
}

/* display change listener */

2003 2004
static void display_update(DisplayChangeListener *dcl,
                           int x, int y, int w, int h)
G
Gerd Hoffmann 已提交
2005
{
G
Gerd Hoffmann 已提交
2006 2007 2008 2009
    PCIQXLDevice *qxl = container_of(dcl, PCIQXLDevice, ssd.dcl);

    if (qxl->mode == QXL_MODE_VGA) {
        qemu_spice_display_update(&qxl->ssd, x, y, w, h);
G
Gerd Hoffmann 已提交
2010 2011 2012
    }
}

2013 2014
static void display_switch(DisplayChangeListener *dcl,
                           struct DisplaySurface *surface)
G
Gerd Hoffmann 已提交
2015
{
G
Gerd Hoffmann 已提交
2016 2017
    PCIQXLDevice *qxl = container_of(dcl, PCIQXLDevice, ssd.dcl);

G
Gerd Hoffmann 已提交
2018
    qxl->ssd.ds = surface;
G
Gerd Hoffmann 已提交
2019
    if (qxl->mode == QXL_MODE_VGA) {
2020
        qemu_spice_display_switch(&qxl->ssd, surface);
G
Gerd Hoffmann 已提交
2021 2022 2023
    }
}

2024
static void display_refresh(DisplayChangeListener *dcl)
G
Gerd Hoffmann 已提交
2025
{
G
Gerd Hoffmann 已提交
2026 2027 2028 2029
    PCIQXLDevice *qxl = container_of(dcl, PCIQXLDevice, ssd.dcl);

    if (qxl->mode == QXL_MODE_VGA) {
        qemu_spice_display_refresh(&qxl->ssd);
G
Gerd Hoffmann 已提交
2030 2031 2032
    }
}

2033 2034
static DisplayChangeListenerOps display_listener_ops = {
    .dpy_name        = "spice/qxl",
2035
    .dpy_gfx_update  = display_update,
2036
    .dpy_gfx_switch  = display_switch,
2037
    .dpy_refresh     = display_refresh,
G
Gerd Hoffmann 已提交
2038 2039
};

2040
static void qxl_init_ramsize(PCIQXLDevice *qxl)
2041
{
2042 2043 2044 2045
    /* vga mode framebuffer / primary surface (bar 0, first part) */
    if (qxl->vgamem_size_mb < 8) {
        qxl->vgamem_size_mb = 8;
    }
2046 2047 2048 2049 2050 2051
    /* XXX: we round vgamem_size_mb up to a nearest power of two and it must be
     * less than vga_common_init()'s maximum on qxl->vga.vram_size (512 now).
     */
    if (qxl->vgamem_size_mb > 256) {
        qxl->vgamem_size_mb = 256;
    }
2052
    qxl->vgamem_size = qxl->vgamem_size_mb * MiB;
2053 2054

    /* vga ram (bar 0, total) */
2055
    if (qxl->ram_size_mb != -1) {
2056
        qxl->vga.vram_size = qxl->ram_size_mb * MiB;
2057
    }
2058 2059
    if (qxl->vga.vram_size < qxl->vgamem_size * 2) {
        qxl->vga.vram_size = qxl->vgamem_size * 2;
2060 2061
    }

G
Gerd Hoffmann 已提交
2062 2063
    /* vram32 (surfaces, 32bit, bar 1) */
    if (qxl->vram32_size_mb != -1) {
2064
        qxl->vram32_size = qxl->vram32_size_mb * MiB;
G
Gerd Hoffmann 已提交
2065 2066 2067 2068 2069 2070
    }
    if (qxl->vram32_size < 4096) {
        qxl->vram32_size = 4096;
    }

    /* vram (surfaces, 64bit, bar 4+5) */
2071
    if (qxl->vram_size_mb != -1) {
2072
        qxl->vram_size = (uint64_t)qxl->vram_size_mb * MiB;
2073
    }
G
Gerd Hoffmann 已提交
2074 2075
    if (qxl->vram_size < qxl->vram32_size) {
        qxl->vram_size = qxl->vram32_size;
2076
    }
G
Gerd Hoffmann 已提交
2077

2078
    if (qxl->revision == 1) {
G
Gerd Hoffmann 已提交
2079
        qxl->vram32_size = 4096;
2080 2081
        qxl->vram_size = 4096;
    }
2082 2083 2084 2085
    qxl->vgamem_size = pow2ceil(qxl->vgamem_size);
    qxl->vga.vram_size = pow2ceil(qxl->vga.vram_size);
    qxl->vram32_size = pow2ceil(qxl->vram32_size);
    qxl->vram_size = pow2ceil(qxl->vram_size);
2086 2087
}

M
Markus Armbruster 已提交
2088
static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp)
G
Gerd Hoffmann 已提交
2089 2090 2091 2092 2093
{
    uint8_t* config = qxl->pci.config;
    uint32_t pci_device_rev;
    uint32_t io_size;

2094
    qemu_spice_display_init_common(&qxl->ssd);
G
Gerd Hoffmann 已提交
2095 2096
    qxl->mode = QXL_MODE_UNDEFINED;
    qxl->num_memslots = NUM_MEMSLOTS;
2097
    qemu_mutex_init(&qxl->track_lock);
2098 2099
    qemu_mutex_init(&qxl->async_lock);
    qxl->current_async = QXL_UNDEFINED_IO;
2100
    qxl->guest_bug = 0;
G
Gerd Hoffmann 已提交
2101 2102 2103 2104

    switch (qxl->revision) {
    case 1: /* spice 0.4 -- qxl-1 */
        pci_device_rev = QXL_REVISION_STABLE_V04;
2105
        io_size = 8;
G
Gerd Hoffmann 已提交
2106 2107 2108
        break;
    case 2: /* spice 0.6 -- qxl-2 */
        pci_device_rev = QXL_REVISION_STABLE_V06;
2109
        io_size = 16;
G
Gerd Hoffmann 已提交
2110
        break;
G
Gerd Hoffmann 已提交
2111
    case 3: /* qxl-3 */
2112 2113 2114 2115 2116
        pci_device_rev = QXL_REVISION_STABLE_V10;
        io_size = 32; /* PCI region size must be pow2 */
        break;
    case 4: /* qxl-4 */
        pci_device_rev = QXL_REVISION_STABLE_V12;
2117
        io_size = pow2ceil(QXL_IO_RANGE_SIZE);
G
Gerd Hoffmann 已提交
2118
        break;
A
Alon Levy 已提交
2119
    default:
M
Markus Armbruster 已提交
2120 2121 2122
        error_setg(errp, "Invalid revision %d for qxl device (max %d)",
                   qxl->revision, QXL_DEFAULT_REVISION);
        return;
G
Gerd Hoffmann 已提交
2123 2124 2125 2126 2127 2128
    }

    pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev);
    pci_set_byte(&config[PCI_INTERRUPT_PIN], 1);

    qxl->rom_size = qxl_rom_size();
2129
    memory_region_init_ram(&qxl->rom_bar, OBJECT(qxl), "qxl.vrom",
2130
                           qxl->rom_size, &error_fatal);
G
Gerd Hoffmann 已提交
2131 2132 2133
    init_qxl_rom(qxl);
    init_qxl_ram(qxl);

2134
    qxl->guest_surfaces.cmds = g_new0(QXLPHYSICAL, qxl->ssd.num_surfaces);
2135
    memory_region_init_ram(&qxl->vram_bar, OBJECT(qxl), "qxl.vram",
2136
                           qxl->vram_size, &error_fatal);
2137 2138
    memory_region_init_alias(&qxl->vram32_bar, OBJECT(qxl), "qxl.vram32",
                             &qxl->vram_bar, 0, qxl->vram32_size);
G
Gerd Hoffmann 已提交
2139

2140
    memory_region_init_io(&qxl->io_bar, OBJECT(qxl), &qxl_io_ops, qxl,
2141
                          "qxl-ioports", io_size);
G
Gerd Hoffmann 已提交
2142
    if (qxl->have_vga) {
2143 2144
        vga_dirty_log_start(&qxl->vga);
    }
2145
    memory_region_set_flush_coalesced(&qxl->io_bar);
2146 2147


2148 2149
    pci_register_bar(&qxl->pci, QXL_IO_RANGE_INDEX,
                     PCI_BASE_ADDRESS_SPACE_IO, &qxl->io_bar);
G
Gerd Hoffmann 已提交
2150

2151 2152
    pci_register_bar(&qxl->pci, QXL_ROM_RANGE_INDEX,
                     PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->rom_bar);
G
Gerd Hoffmann 已提交
2153

2154 2155
    pci_register_bar(&qxl->pci, QXL_RAM_RANGE_INDEX,
                     PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vga.vram);
G
Gerd Hoffmann 已提交
2156

2157
    pci_register_bar(&qxl->pci, QXL_VRAM_RANGE_INDEX,
G
Gerd Hoffmann 已提交
2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172
                     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 */
2173
    dprint(qxl, 1, "ram/%s: %" PRId64 " MB [region 0]\n",
G
Gerd Hoffmann 已提交
2174
           qxl->have_vga ? "pri" : "sec", qxl->vga.vram_size / MiB);
2175 2176 2177 2178
    dprint(qxl, 1, "vram/32: %" PRIx64 " MB [region 1]\n",
           qxl->vram32_size / MiB);
    dprint(qxl, 1, "vram/64: %" PRIx64 " MB %s\n",
           qxl->vram_size / MiB,
G
Gerd Hoffmann 已提交
2179
           qxl->vram32_size < qxl->vram_size ? "[region 4]" : "[unmapped]");
G
Gerd Hoffmann 已提交
2180 2181

    qxl->ssd.qxl.base.sif = &qxl_interface.base;
G
Gerd Hoffmann 已提交
2182
    if (qemu_spice_add_display_interface(&qxl->ssd.qxl, qxl->vga.con) != 0) {
M
Markus Armbruster 已提交
2183 2184 2185
        error_setg(errp, "qxl interface %d.%d not supported by spice-server",
                   SPICE_INTERFACE_QXL_MAJOR, SPICE_INTERFACE_QXL_MINOR);
        return;
2186
    }
G
Gerd Hoffmann 已提交
2187 2188
    qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl);

2189
    qxl->update_irq = qemu_bh_new(qxl_update_irq_bh, qxl);
G
Gerd Hoffmann 已提交
2190 2191
    qxl_reset_state(qxl);

A
Alon Levy 已提交
2192
    qxl->update_area_bh = qemu_bh_new(qxl_render_update_area_bh, qxl);
2193
    qxl->ssd.cursor_bh = qemu_bh_new(qemu_spice_cursor_refresh_bh, &qxl->ssd);
G
Gerd Hoffmann 已提交
2194 2195
}

M
Markus Armbruster 已提交
2196
static void qxl_realize_primary(PCIDevice *dev, Error **errp)
G
Gerd Hoffmann 已提交
2197
{
G
Gonglei 已提交
2198
    PCIQXLDevice *qxl = PCI_QXL(dev);
G
Gerd Hoffmann 已提交
2199
    VGACommonState *vga = &qxl->vga;
M
Markus Armbruster 已提交
2200
    Error *local_err = NULL;
G
Gerd Hoffmann 已提交
2201

2202
    qxl_init_ramsize(qxl);
2203
    vga->vbe_size = qxl->vgamem_size;
2204
    vga->vram_size_mb = qxl->vga.vram_size / MiB;
2205
    vga_common_init(vga, OBJECT(dev));
P
Paolo Bonzini 已提交
2206 2207
    vga_init(vga, OBJECT(dev),
             pci_address_space(dev), pci_address_space_io(dev), false);
2208
    portio_list_init(&qxl->vga_port_list, OBJECT(dev), qxl_vga_portio_list,
2209
                     vga, "vga");
2210 2211
    portio_list_set_flush_coalesced(&qxl->vga_port_list);
    portio_list_add(&qxl->vga_port_list, pci_address_space_io(dev), 0x3b0);
G
Gerd Hoffmann 已提交
2212
    qxl->have_vga = true;
G
Gerd Hoffmann 已提交
2213

2214
    vga->con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl);
G
Gerd Hoffmann 已提交
2215 2216 2217 2218 2219 2220
    qxl->id = qemu_console_get_index(vga->con); /* == channel_id */
    if (qxl->id != 0) {
        error_setg(errp, "primary qxl-vga device must be console 0 "
                   "(first display device on the command line)");
        return;
    }
G
Gerd Hoffmann 已提交
2221

M
Markus Armbruster 已提交
2222 2223 2224 2225
    qxl_realize_common(qxl, &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
        return;
G
Gerd Hoffmann 已提交
2226 2227
    }

2228
    qxl->ssd.dcl.ops = &display_listener_ops;
2229
    qxl->ssd.dcl.con = vga->con;
2230
    register_displaychangelistener(&qxl->ssd.dcl);
G
Gerd Hoffmann 已提交
2231 2232
}

M
Markus Armbruster 已提交
2233
static void qxl_realize_secondary(PCIDevice *dev, Error **errp)
G
Gerd Hoffmann 已提交
2234
{
G
Gonglei 已提交
2235
    PCIQXLDevice *qxl = PCI_QXL(dev);
G
Gerd Hoffmann 已提交
2236

2237
    qxl_init_ramsize(qxl);
2238
    memory_region_init_ram(&qxl->vga.vram, OBJECT(dev), "qxl.vgavram",
2239
                           qxl->vga.vram_size, &error_fatal);
2240
    qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram);
2241
    qxl->vga.con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl);
G
Gerd Hoffmann 已提交
2242
    qxl->id = qemu_console_get_index(qxl->vga.con); /* == channel_id */
G
Gerd Hoffmann 已提交
2243

M
Markus Armbruster 已提交
2244
    qxl_realize_common(qxl, errp);
G
Gerd Hoffmann 已提交
2245 2246
}

2247
static int qxl_pre_save(void *opaque)
G
Gerd Hoffmann 已提交
2248 2249 2250 2251
{
    PCIQXLDevice* d = opaque;
    uint8_t *ram_start = d->vga.vram_ptr;

A
Alon Levy 已提交
2252
    trace_qxl_pre_save(d->id);
G
Gerd Hoffmann 已提交
2253 2254 2255 2256 2257 2258
    if (d->last_release == NULL) {
        d->last_release_offset = 0;
    } else {
        d->last_release_offset = (uint8_t *)d->last_release - ram_start;
    }
    assert(d->last_release_offset < d->vga.vram_size);
2259 2260

    return 0;
G
Gerd Hoffmann 已提交
2261 2262 2263 2264 2265 2266
}

static int qxl_pre_load(void *opaque)
{
    PCIQXLDevice* d = opaque;

A
Alon Levy 已提交
2267
    trace_qxl_pre_load(d->id);
G
Gerd Hoffmann 已提交
2268 2269 2270 2271 2272
    qxl_hard_reset(d, 1);
    qxl_exit_vga_mode(d);
    return 0;
}

2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284
static void qxl_create_memslots(PCIQXLDevice *d)
{
    int i;

    for (i = 0; i < NUM_MEMSLOTS; i++) {
        if (!d->guest_slots[i].active) {
            continue;
        }
        qxl_add_memslot(d, i, 0, QXL_SYNC);
    }
}

G
Gerd Hoffmann 已提交
2285 2286 2287 2288 2289
static int qxl_post_load(void *opaque, int version)
{
    PCIQXLDevice* d = opaque;
    uint8_t *ram_start = d->vga.vram_ptr;
    QXLCommandExt *cmds;
2290
    int in, out, newmode;
G
Gerd Hoffmann 已提交
2291 2292 2293 2294 2295 2296 2297 2298 2299 2300

    assert(d->last_release_offset < d->vga.vram_size);
    if (d->last_release_offset == 0) {
        d->last_release = NULL;
    } else {
        d->last_release = (QXLReleaseInfo *)(ram_start + d->last_release_offset);
    }

    d->modes = (QXLModes*)((uint8_t*)d->rom + d->rom->modes_offset);

A
Alon Levy 已提交
2301
    trace_qxl_post_load(d->id, qxl_mode_to_string(d->mode));
G
Gerd Hoffmann 已提交
2302 2303
    newmode = d->mode;
    d->mode = QXL_MODE_UNDEFINED;
2304

G
Gerd Hoffmann 已提交
2305 2306
    switch (newmode) {
    case QXL_MODE_UNDEFINED:
2307
        qxl_create_memslots(d);
G
Gerd Hoffmann 已提交
2308 2309
        break;
    case QXL_MODE_VGA:
2310
        qxl_create_memslots(d);
G
Gerd Hoffmann 已提交
2311 2312 2313
        qxl_enter_vga_mode(d);
        break;
    case QXL_MODE_NATIVE:
2314
        qxl_create_memslots(d);
2315
        qxl_create_guest_primary(d, 1, QXL_SYNC);
G
Gerd Hoffmann 已提交
2316 2317

        /* replay surface-create and cursor-set commands */
2318
        cmds = g_new0(QXLCommandExt, d->ssd.num_surfaces + 1);
2319
        for (in = 0, out = 0; in < d->ssd.num_surfaces; in++) {
G
Gerd Hoffmann 已提交
2320 2321 2322 2323 2324 2325 2326 2327
            if (d->guest_surfaces.cmds[in] == 0) {
                continue;
            }
            cmds[out].cmd.data = d->guest_surfaces.cmds[in];
            cmds[out].cmd.type = QXL_CMD_SURFACE;
            cmds[out].group_id = MEMSLOT_GROUP_GUEST;
            out++;
        }
Y
Yonit Halperin 已提交
2328 2329 2330 2331 2332 2333
        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++;
        }
G
Gerd Hoffmann 已提交
2334
        qxl_spice_loadvm_commands(d, cmds, out);
2335
        g_free(cmds);
2336 2337 2338
        if (d->guest_monitors_config) {
            qxl_spice_monitors_config_async(d, 1);
        }
G
Gerd Hoffmann 已提交
2339 2340
        break;
    case QXL_MODE_COMPAT:
2341 2342
        /* note: no need to call qxl_create_memslots, qxl_set_mode
         * creates the mem slot. */
G
Gerd Hoffmann 已提交
2343 2344 2345 2346 2347 2348
        qxl_set_mode(d, d->shadow_rom.mode, 1);
        break;
    }
    return 0;
}

2349
#define QXL_SAVE_VERSION 21
G
Gerd Hoffmann 已提交
2350

2351 2352 2353 2354 2355 2356 2357 2358
static bool qxl_monitors_config_needed(void *opaque)
{
    PCIQXLDevice *qxl = opaque;

    return qxl->guest_monitors_config != 0;
}


G
Gerd Hoffmann 已提交
2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388
static VMStateDescription qxl_memslot = {
    .name               = "qxl-memslot",
    .version_id         = QXL_SAVE_VERSION,
    .minimum_version_id = QXL_SAVE_VERSION,
    .fields = (VMStateField[]) {
        VMSTATE_UINT64(slot.mem_start, struct guest_slots),
        VMSTATE_UINT64(slot.mem_end,   struct guest_slots),
        VMSTATE_UINT32(active,         struct guest_slots),
        VMSTATE_END_OF_LIST()
    }
};

static VMStateDescription qxl_surface = {
    .name               = "qxl-surface",
    .version_id         = QXL_SAVE_VERSION,
    .minimum_version_id = QXL_SAVE_VERSION,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32(width,      QXLSurfaceCreate),
        VMSTATE_UINT32(height,     QXLSurfaceCreate),
        VMSTATE_INT32(stride,      QXLSurfaceCreate),
        VMSTATE_UINT32(format,     QXLSurfaceCreate),
        VMSTATE_UINT32(position,   QXLSurfaceCreate),
        VMSTATE_UINT32(mouse_mode, QXLSurfaceCreate),
        VMSTATE_UINT32(flags,      QXLSurfaceCreate),
        VMSTATE_UINT32(type,       QXLSurfaceCreate),
        VMSTATE_UINT64(mem,        QXLSurfaceCreate),
        VMSTATE_END_OF_LIST()
    }
};

2389 2390 2391 2392
static VMStateDescription qxl_vmstate_monitors_config = {
    .name               = "qxl/monitors-config",
    .version_id         = 1,
    .minimum_version_id = 1,
2393
    .needed = qxl_monitors_config_needed,
2394 2395 2396 2397 2398 2399
    .fields = (VMStateField[]) {
        VMSTATE_UINT64(guest_monitors_config, PCIQXLDevice),
        VMSTATE_END_OF_LIST()
    },
};

G
Gerd Hoffmann 已提交
2400 2401 2402 2403 2404 2405 2406
static VMStateDescription qxl_vmstate = {
    .name               = "qxl",
    .version_id         = QXL_SAVE_VERSION,
    .minimum_version_id = QXL_SAVE_VERSION,
    .pre_save           = qxl_pre_save,
    .pre_load           = qxl_pre_load,
    .post_load          = qxl_post_load,
2407
    .fields = (VMStateField[]) {
G
Gerd Hoffmann 已提交
2408 2409 2410 2411 2412 2413 2414
        VMSTATE_PCI_DEVICE(pci, PCIQXLDevice),
        VMSTATE_STRUCT(vga, PCIQXLDevice, 0, vmstate_vga_common, VGACommonState),
        VMSTATE_UINT32(shadow_rom.mode, PCIQXLDevice),
        VMSTATE_UINT32(num_free_res, PCIQXLDevice),
        VMSTATE_UINT32(last_release_offset, PCIQXLDevice),
        VMSTATE_UINT32(mode, PCIQXLDevice),
        VMSTATE_UINT32(ssd.unique, PCIQXLDevice),
2415
        VMSTATE_INT32_EQUAL(num_memslots, PCIQXLDevice, NULL),
2416 2417 2418 2419
        VMSTATE_STRUCT_ARRAY(guest_slots, PCIQXLDevice, NUM_MEMSLOTS, 0,
                             qxl_memslot, struct guest_slots),
        VMSTATE_STRUCT(guest_primary.surface, PCIQXLDevice, 0,
                       qxl_surface, QXLSurfaceCreate),
2420
        VMSTATE_INT32_EQUAL(ssd.num_surfaces, PCIQXLDevice, NULL),
2421 2422 2423
        VMSTATE_VARRAY_INT32(guest_surfaces.cmds, PCIQXLDevice,
                             ssd.num_surfaces, 0,
                             vmstate_info_uint64, uint64_t),
2424
        VMSTATE_UINT64(guest_cursor, PCIQXLDevice),
G
Gerd Hoffmann 已提交
2425 2426
        VMSTATE_END_OF_LIST()
    },
2427 2428 2429
    .subsections = (const VMStateDescription*[]) {
        &qxl_vmstate_monitors_config,
        NULL
2430
    }
G
Gerd Hoffmann 已提交
2431 2432
};

G
Gerd Hoffmann 已提交
2433
static Property qxl_properties[] = {
2434 2435
        DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * MiB),
        DEFINE_PROP_UINT64("vram_size", PCIQXLDevice, vram32_size, 64 * MiB),
G
Gerd Hoffmann 已提交
2436 2437 2438 2439 2440
        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),
2441
        DEFINE_PROP_UINT32("ram_size_mb",  PCIQXLDevice, ram_size_mb, -1),
2442 2443
        DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, -1),
        DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1),
G
Gerd Hoffmann 已提交
2444
        DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 16),
2445
        DEFINE_PROP_INT32("surfaces", PCIQXLDevice, ssd.num_surfaces, 1024),
2446 2447 2448
#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */
        DEFINE_PROP_UINT16("max_outputs", PCIQXLDevice, max_outputs, 0),
#endif
2449 2450
        DEFINE_PROP_UINT32("xres", PCIQXLDevice, xres, 0),
        DEFINE_PROP_UINT32("yres", PCIQXLDevice, yres, 0),
2451
        DEFINE_PROP_BOOL("global-vmstate", PCIQXLDevice, vga.global_vmstate, false),
G
Gerd Hoffmann 已提交
2452 2453 2454
        DEFINE_PROP_END_OF_LIST(),
};

G
Gonglei 已提交
2455
static void qxl_pci_class_init(ObjectClass *klass, void *data)
2456
{
2457
    DeviceClass *dc = DEVICE_CLASS(klass);
2458 2459 2460 2461
    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);

    k->vendor_id = REDHAT_PCI_VENDOR_ID;
    k->device_id = QXL_DEVICE_ID_STABLE;
2462
    set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
2463 2464 2465
    dc->reset = qxl_reset_handler;
    dc->vmsd = &qxl_vmstate;
    dc->props = qxl_properties;
G
Gonglei 已提交
2466 2467 2468 2469 2470 2471 2472 2473
}

static const TypeInfo qxl_pci_type_info = {
    .name = TYPE_PCI_QXL,
    .parent = TYPE_PCI_DEVICE,
    .instance_size = sizeof(PCIQXLDevice),
    .abstract = true,
    .class_init = qxl_pci_class_init,
2474 2475 2476 2477
    .interfaces = (InterfaceInfo[]) {
        { INTERFACE_CONVENTIONAL_PCI_DEVICE },
        { },
    },
G
Gonglei 已提交
2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488
};

static void qxl_primary_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);
    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);

    k->realize = qxl_realize_primary;
    k->romfile = "vgabios-qxl.bin";
    k->class_id = PCI_CLASS_DISPLAY_VGA;
    dc->desc = "Spice QXL GPU (primary, vga compatible)";
2489
    dc->hotpluggable = false;
2490 2491
}

2492
static const TypeInfo qxl_primary_info = {
2493
    .name          = "qxl-vga",
G
Gonglei 已提交
2494
    .parent        = TYPE_PCI_QXL,
2495
    .class_init    = qxl_primary_class_init,
G
Gerd Hoffmann 已提交
2496 2497
};

2498 2499
static void qxl_secondary_class_init(ObjectClass *klass, void *data)
{
2500
    DeviceClass *dc = DEVICE_CLASS(klass);
2501 2502
    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);

M
Markus Armbruster 已提交
2503
    k->realize = qxl_realize_secondary;
2504
    k->class_id = PCI_CLASS_DISPLAY_OTHER;
2505
    dc->desc = "Spice QXL GPU (secondary)";
2506 2507
}

2508
static const TypeInfo qxl_secondary_info = {
2509
    .name          = "qxl",
G
Gonglei 已提交
2510
    .parent        = TYPE_PCI_QXL,
2511
    .class_init    = qxl_secondary_class_init,
G
Gerd Hoffmann 已提交
2512 2513
};

A
Andreas Färber 已提交
2514
static void qxl_register_types(void)
G
Gerd Hoffmann 已提交
2515
{
G
Gonglei 已提交
2516
    type_register_static(&qxl_pci_type_info);
2517 2518
    type_register_static(&qxl_primary_info);
    type_register_static(&qxl_secondary_info);
G
Gerd Hoffmann 已提交
2519 2520
}

A
Andreas Färber 已提交
2521
type_init(qxl_register_types)