]> Git Repo - qemu.git/blame - hw/qxl.c
vga: make vram size configurable
[qemu.git] / hw / qxl.c
CommitLineData
a19cbfb3
GH
1/*
2 * Copyright (C) 2010 Red Hat, Inc.
3 *
4 * written by Yaniv Kamay, Izik Eidus, Gerd Hoffmann
5 * maintained by Gerd Hoffmann <[email protected]>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 or
10 * (at your option) version 3 of the License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 */
20
a19cbfb3
GH
21#include "qemu-common.h"
22#include "qemu-timer.h"
23#include "qemu-queue.h"
24#include "monitor.h"
25#include "sysemu.h"
c480bb7d 26#include "trace.h"
a19cbfb3
GH
27
28#include "qxl.h"
29
4a1e244e
GH
30#define VGA_RAM_SIZE (8192 * 1024)
31
0b81c478
AL
32/*
33 * NOTE: SPICE_RING_PROD_ITEM accesses memory on the pci bar and as
34 * such can be changed by the guest, so to avoid a guest trigerrable
0a530548 35 * abort we just qxl_set_guest_bug and set the return to NULL. Still
0b81c478
AL
36 * it may happen as a result of emulator bug as well.
37 */
a19cbfb3 38#undef SPICE_RING_PROD_ITEM
0b81c478 39#define SPICE_RING_PROD_ITEM(qxl, r, ret) { \
a19cbfb3
GH
40 typeof(r) start = r; \
41 typeof(r) end = r + 1; \
42 uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r); \
43 typeof(&(r)->items[prod]) m_item = &(r)->items[prod]; \
44 if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \
0a530548 45 qxl_set_guest_bug(qxl, "SPICE_RING_PROD_ITEM indices mismatch " \
0b81c478
AL
46 "! %p <= %p < %p", (uint8_t *)start, \
47 (uint8_t *)m_item, (uint8_t *)end); \
48 ret = NULL; \
49 } else { \
50 ret = &m_item->el; \
a19cbfb3 51 } \
a19cbfb3
GH
52 }
53
54#undef SPICE_RING_CONS_ITEM
0b81c478 55#define SPICE_RING_CONS_ITEM(qxl, r, ret) { \
a19cbfb3
GH
56 typeof(r) start = r; \
57 typeof(r) end = r + 1; \
58 uint32_t cons = (r)->cons & SPICE_RING_INDEX_MASK(r); \
59 typeof(&(r)->items[cons]) m_item = &(r)->items[cons]; \
60 if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \
0a530548 61 qxl_set_guest_bug(qxl, "SPICE_RING_CONS_ITEM indices mismatch " \
0b81c478
AL
62 "! %p <= %p < %p", (uint8_t *)start, \
63 (uint8_t *)m_item, (uint8_t *)end); \
64 ret = NULL; \
65 } else { \
66 ret = &m_item->el; \
a19cbfb3 67 } \
a19cbfb3
GH
68 }
69
70#undef ALIGN
71#define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1))
72
73#define PIXEL_SIZE 0.2936875 //1280x1024 is 14.8" x 11.9"
74
75#define QXL_MODE(_x, _y, _b, _o) \
76 { .x_res = _x, \
77 .y_res = _y, \
78 .bits = _b, \
79 .stride = (_x) * (_b) / 8, \
80 .x_mili = PIXEL_SIZE * (_x), \
81 .y_mili = PIXEL_SIZE * (_y), \
82 .orientation = _o, \
83 }
84
85#define QXL_MODE_16_32(x_res, y_res, orientation) \
86 QXL_MODE(x_res, y_res, 16, orientation), \
87 QXL_MODE(x_res, y_res, 32, orientation)
88
89#define QXL_MODE_EX(x_res, y_res) \
90 QXL_MODE_16_32(x_res, y_res, 0), \
91 QXL_MODE_16_32(y_res, x_res, 1), \
92 QXL_MODE_16_32(x_res, y_res, 2), \
93 QXL_MODE_16_32(y_res, x_res, 3)
94
95static QXLMode qxl_modes[] = {
96 QXL_MODE_EX(640, 480),
97 QXL_MODE_EX(800, 480),
98 QXL_MODE_EX(800, 600),
99 QXL_MODE_EX(832, 624),
100 QXL_MODE_EX(960, 640),
101 QXL_MODE_EX(1024, 600),
102 QXL_MODE_EX(1024, 768),
103 QXL_MODE_EX(1152, 864),
104 QXL_MODE_EX(1152, 870),
105 QXL_MODE_EX(1280, 720),
106 QXL_MODE_EX(1280, 760),
107 QXL_MODE_EX(1280, 768),
108 QXL_MODE_EX(1280, 800),
109 QXL_MODE_EX(1280, 960),
110 QXL_MODE_EX(1280, 1024),
111 QXL_MODE_EX(1360, 768),
112 QXL_MODE_EX(1366, 768),
113 QXL_MODE_EX(1400, 1050),
114 QXL_MODE_EX(1440, 900),
115 QXL_MODE_EX(1600, 900),
116 QXL_MODE_EX(1600, 1200),
117 QXL_MODE_EX(1680, 1050),
118 QXL_MODE_EX(1920, 1080),
119#if VGA_RAM_SIZE >= (16 * 1024 * 1024)
120 /* these modes need more than 8 MB video memory */
121 QXL_MODE_EX(1920, 1200),
122 QXL_MODE_EX(1920, 1440),
123 QXL_MODE_EX(2048, 1536),
124 QXL_MODE_EX(2560, 1440),
125 QXL_MODE_EX(2560, 1600),
126#endif
127#if VGA_RAM_SIZE >= (32 * 1024 * 1024)
128 /* these modes need more than 16 MB video memory */
129 QXL_MODE_EX(2560, 2048),
130 QXL_MODE_EX(2800, 2100),
131 QXL_MODE_EX(3200, 2400),
132#endif
133};
134
135static PCIQXLDevice *qxl0;
136
137static void qxl_send_events(PCIQXLDevice *d, uint32_t events);
5ff4e36c 138static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async);
a19cbfb3
GH
139static void qxl_reset_memslots(PCIQXLDevice *d);
140static void qxl_reset_surfaces(PCIQXLDevice *d);
141static void qxl_ring_set_dirty(PCIQXLDevice *qxl);
142
0a530548 143void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...)
2bce0400 144{
2bce0400 145 qxl_send_events(qxl, QXL_INTERRUPT_ERROR);
087e6a42 146 qxl->guest_bug = 1;
2bce0400 147 if (qxl->guestdebug) {
7635392c
AL
148 va_list ap;
149 va_start(ap, msg);
150 fprintf(stderr, "qxl-%d: guest bug: ", qxl->id);
151 vfprintf(stderr, msg, ap);
152 fprintf(stderr, "\n");
153 va_end(ap);
2bce0400
GH
154 }
155}
156
087e6a42
AL
157static void qxl_clear_guest_bug(PCIQXLDevice *qxl)
158{
159 qxl->guest_bug = 0;
160}
aee32bf3
GH
161
162void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id,
163 struct QXLRect *area, struct QXLRect *dirty_rects,
164 uint32_t num_dirty_rects,
5ff4e36c 165 uint32_t clear_dirty_region,
2e1a98c9 166 qxl_async_io async, struct QXLCookie *cookie)
aee32bf3 167{
c480bb7d
AL
168 trace_qxl_spice_update_area(qxl->id, surface_id, area->left, area->right,
169 area->top, area->bottom);
170 trace_qxl_spice_update_area_rest(qxl->id, num_dirty_rects,
171 clear_dirty_region);
5ff4e36c
AL
172 if (async == QXL_SYNC) {
173 qxl->ssd.worker->update_area(qxl->ssd.worker, surface_id, area,
174 dirty_rects, num_dirty_rects, clear_dirty_region);
175 } else {
2e1a98c9 176 assert(cookie != NULL);
5ff4e36c 177 spice_qxl_update_area_async(&qxl->ssd.qxl, surface_id, area,
5dba0d45 178 clear_dirty_region, (uintptr_t)cookie);
5ff4e36c 179 }
aee32bf3
GH
180}
181
5ff4e36c
AL
182static void qxl_spice_destroy_surface_wait_complete(PCIQXLDevice *qxl,
183 uint32_t id)
aee32bf3 184{
c480bb7d 185 trace_qxl_spice_destroy_surface_wait_complete(qxl->id, id);
14898cf6 186 qemu_mutex_lock(&qxl->track_lock);
14898cf6
GH
187 qxl->guest_surfaces.cmds[id] = 0;
188 qxl->guest_surfaces.count--;
189 qemu_mutex_unlock(&qxl->track_lock);
aee32bf3
GH
190}
191
5ff4e36c
AL
192static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id,
193 qxl_async_io async)
194{
2e1a98c9
AL
195 QXLCookie *cookie;
196
c480bb7d 197 trace_qxl_spice_destroy_surface_wait(qxl->id, id, async);
5ff4e36c 198 if (async) {
2e1a98c9
AL
199 cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
200 QXL_IO_DESTROY_SURFACE_ASYNC);
201 cookie->u.surface_id = id;
5dba0d45 202 spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, (uintptr_t)cookie);
5ff4e36c
AL
203 } else {
204 qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id);
5ff4e36c
AL
205 }
206}
207
3e16b9c5
AL
208static void qxl_spice_flush_surfaces_async(PCIQXLDevice *qxl)
209{
c480bb7d
AL
210 trace_qxl_spice_flush_surfaces_async(qxl->id, qxl->guest_surfaces.count,
211 qxl->num_free_res);
2e1a98c9 212 spice_qxl_flush_surfaces_async(&qxl->ssd.qxl,
5dba0d45
PM
213 (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
214 QXL_IO_FLUSH_SURFACES_ASYNC));
3e16b9c5 215}
3e16b9c5 216
aee32bf3
GH
217void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext,
218 uint32_t count)
219{
c480bb7d 220 trace_qxl_spice_loadvm_commands(qxl->id, ext, count);
aee32bf3
GH
221 qxl->ssd.worker->loadvm_commands(qxl->ssd.worker, ext, count);
222}
223
224void qxl_spice_oom(PCIQXLDevice *qxl)
225{
c480bb7d 226 trace_qxl_spice_oom(qxl->id);
aee32bf3
GH
227 qxl->ssd.worker->oom(qxl->ssd.worker);
228}
229
230void qxl_spice_reset_memslots(PCIQXLDevice *qxl)
231{
c480bb7d 232 trace_qxl_spice_reset_memslots(qxl->id);
aee32bf3
GH
233 qxl->ssd.worker->reset_memslots(qxl->ssd.worker);
234}
235
5ff4e36c 236static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl)
aee32bf3 237{
c480bb7d 238 trace_qxl_spice_destroy_surfaces_complete(qxl->id);
14898cf6 239 qemu_mutex_lock(&qxl->track_lock);
14898cf6
GH
240 memset(&qxl->guest_surfaces.cmds, 0, sizeof(qxl->guest_surfaces.cmds));
241 qxl->guest_surfaces.count = 0;
242 qemu_mutex_unlock(&qxl->track_lock);
aee32bf3
GH
243}
244
5ff4e36c
AL
245static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async)
246{
c480bb7d 247 trace_qxl_spice_destroy_surfaces(qxl->id, async);
5ff4e36c 248 if (async) {
2e1a98c9 249 spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl,
5dba0d45
PM
250 (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
251 QXL_IO_DESTROY_ALL_SURFACES_ASYNC));
5ff4e36c
AL
252 } else {
253 qxl->ssd.worker->destroy_surfaces(qxl->ssd.worker);
254 qxl_spice_destroy_surfaces_complete(qxl);
255 }
256}
257
aee32bf3
GH
258void qxl_spice_reset_image_cache(PCIQXLDevice *qxl)
259{
c480bb7d 260 trace_qxl_spice_reset_image_cache(qxl->id);
aee32bf3
GH
261 qxl->ssd.worker->reset_image_cache(qxl->ssd.worker);
262}
263
264void qxl_spice_reset_cursor(PCIQXLDevice *qxl)
265{
c480bb7d 266 trace_qxl_spice_reset_cursor(qxl->id);
aee32bf3 267 qxl->ssd.worker->reset_cursor(qxl->ssd.worker);
30f6da66
YH
268 qemu_mutex_lock(&qxl->track_lock);
269 qxl->guest_cursor = 0;
270 qemu_mutex_unlock(&qxl->track_lock);
aee32bf3
GH
271}
272
273
a19cbfb3
GH
274static inline uint32_t msb_mask(uint32_t val)
275{
276 uint32_t mask;
277
278 do {
279 mask = ~(val - 1) & val;
280 val &= ~mask;
281 } while (mask < val);
282
283 return mask;
284}
285
286static ram_addr_t qxl_rom_size(void)
287{
288 uint32_t rom_size = sizeof(QXLRom) + sizeof(QXLModes) + sizeof(qxl_modes);
289 rom_size = MAX(rom_size, TARGET_PAGE_SIZE);
290 rom_size = msb_mask(rom_size * 2 - 1);
291 return rom_size;
292}
293
294static void init_qxl_rom(PCIQXLDevice *d)
295{
b1950430 296 QXLRom *rom = memory_region_get_ram_ptr(&d->rom_bar);
a19cbfb3
GH
297 QXLModes *modes = (QXLModes *)(rom + 1);
298 uint32_t ram_header_size;
299 uint32_t surface0_area_size;
300 uint32_t num_pages;
301 uint32_t fb, maxfb = 0;
302 int i;
303
304 memset(rom, 0, d->rom_size);
305
306 rom->magic = cpu_to_le32(QXL_ROM_MAGIC);
307 rom->id = cpu_to_le32(d->id);
308 rom->log_level = cpu_to_le32(d->guestdebug);
309 rom->modes_offset = cpu_to_le32(sizeof(QXLRom));
310
311 rom->slot_gen_bits = MEMSLOT_GENERATION_BITS;
312 rom->slot_id_bits = MEMSLOT_SLOT_BITS;
313 rom->slots_start = 1;
314 rom->slots_end = NUM_MEMSLOTS - 1;
315 rom->n_surfaces = cpu_to_le32(NUM_SURFACES);
316
317 modes->n_modes = cpu_to_le32(ARRAY_SIZE(qxl_modes));
318 for (i = 0; i < modes->n_modes; i++) {
319 fb = qxl_modes[i].y_res * qxl_modes[i].stride;
320 if (maxfb < fb) {
321 maxfb = fb;
322 }
323 modes->modes[i].id = cpu_to_le32(i);
324 modes->modes[i].x_res = cpu_to_le32(qxl_modes[i].x_res);
325 modes->modes[i].y_res = cpu_to_le32(qxl_modes[i].y_res);
326 modes->modes[i].bits = cpu_to_le32(qxl_modes[i].bits);
327 modes->modes[i].stride = cpu_to_le32(qxl_modes[i].stride);
328 modes->modes[i].x_mili = cpu_to_le32(qxl_modes[i].x_mili);
329 modes->modes[i].y_mili = cpu_to_le32(qxl_modes[i].y_mili);
330 modes->modes[i].orientation = cpu_to_le32(qxl_modes[i].orientation);
331 }
332 if (maxfb < VGA_RAM_SIZE && d->id == 0)
333 maxfb = VGA_RAM_SIZE;
334
335 ram_header_size = ALIGN(sizeof(QXLRam), 4096);
336 surface0_area_size = ALIGN(maxfb, 4096);
337 num_pages = d->vga.vram_size;
338 num_pages -= ram_header_size;
339 num_pages -= surface0_area_size;
340 num_pages = num_pages / TARGET_PAGE_SIZE;
341
342 rom->draw_area_offset = cpu_to_le32(0);
343 rom->surface0_area_size = cpu_to_le32(surface0_area_size);
344 rom->pages_offset = cpu_to_le32(surface0_area_size);
345 rom->num_pages = cpu_to_le32(num_pages);
346 rom->ram_header_offset = cpu_to_le32(d->vga.vram_size - ram_header_size);
347
348 d->shadow_rom = *rom;
349 d->rom = rom;
350 d->modes = modes;
351}
352
353static void init_qxl_ram(PCIQXLDevice *d)
354{
355 uint8_t *buf;
356 uint64_t *item;
357
358 buf = d->vga.vram_ptr;
359 d->ram = (QXLRam *)(buf + le32_to_cpu(d->shadow_rom.ram_header_offset));
360 d->ram->magic = cpu_to_le32(QXL_RAM_MAGIC);
361 d->ram->int_pending = cpu_to_le32(0);
362 d->ram->int_mask = cpu_to_le32(0);
9f0f352d 363 d->ram->update_surface = 0;
a19cbfb3
GH
364 SPICE_RING_INIT(&d->ram->cmd_ring);
365 SPICE_RING_INIT(&d->ram->cursor_ring);
366 SPICE_RING_INIT(&d->ram->release_ring);
0b81c478
AL
367 SPICE_RING_PROD_ITEM(d, &d->ram->release_ring, item);
368 assert(item);
a19cbfb3
GH
369 *item = 0;
370 qxl_ring_set_dirty(d);
371}
372
373/* can be called from spice server thread context */
b1950430 374static void qxl_set_dirty(MemoryRegion *mr, ram_addr_t addr, ram_addr_t end)
a19cbfb3 375{
fd4aa979 376 memory_region_set_dirty(mr, addr, end - addr);
a19cbfb3
GH
377}
378
379static void qxl_rom_set_dirty(PCIQXLDevice *qxl)
380{
b1950430 381 qxl_set_dirty(&qxl->rom_bar, 0, qxl->rom_size);
a19cbfb3
GH
382}
383
384/* called from spice server thread context only */
385static void qxl_ram_set_dirty(PCIQXLDevice *qxl, void *ptr)
386{
a19cbfb3
GH
387 void *base = qxl->vga.vram_ptr;
388 intptr_t offset;
389
390 offset = ptr - base;
391 offset &= ~(TARGET_PAGE_SIZE-1);
392 assert(offset < qxl->vga.vram_size);
b1950430 393 qxl_set_dirty(&qxl->vga.vram, offset, offset + TARGET_PAGE_SIZE);
a19cbfb3
GH
394}
395
396/* can be called from spice server thread context */
397static void qxl_ring_set_dirty(PCIQXLDevice *qxl)
398{
b1950430
AK
399 ram_addr_t addr = qxl->shadow_rom.ram_header_offset;
400 ram_addr_t end = qxl->vga.vram_size;
401 qxl_set_dirty(&qxl->vga.vram, addr, end);
a19cbfb3
GH
402}
403
404/*
405 * keep track of some command state, for savevm/loadvm.
406 * called from spice server thread context only
407 */
fae2afb1 408static int qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext)
a19cbfb3
GH
409{
410 switch (le32_to_cpu(ext->cmd.type)) {
411 case QXL_CMD_SURFACE:
412 {
413 QXLSurfaceCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
fae2afb1
AL
414
415 if (!cmd) {
416 return 1;
417 }
a19cbfb3 418 uint32_t id = le32_to_cpu(cmd->surface_id);
47eddfbf
AL
419
420 if (id >= NUM_SURFACES) {
0a530548
AL
421 qxl_set_guest_bug(qxl, "QXL_CMD_SURFACE id %d >= %d", id,
422 NUM_SURFACES);
47eddfbf
AL
423 return 1;
424 }
14898cf6 425 qemu_mutex_lock(&qxl->track_lock);
a19cbfb3
GH
426 if (cmd->type == QXL_SURFACE_CMD_CREATE) {
427 qxl->guest_surfaces.cmds[id] = ext->cmd.data;
428 qxl->guest_surfaces.count++;
429 if (qxl->guest_surfaces.max < qxl->guest_surfaces.count)
430 qxl->guest_surfaces.max = qxl->guest_surfaces.count;
431 }
432 if (cmd->type == QXL_SURFACE_CMD_DESTROY) {
433 qxl->guest_surfaces.cmds[id] = 0;
434 qxl->guest_surfaces.count--;
435 }
14898cf6 436 qemu_mutex_unlock(&qxl->track_lock);
a19cbfb3
GH
437 break;
438 }
439 case QXL_CMD_CURSOR:
440 {
441 QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
fae2afb1
AL
442
443 if (!cmd) {
444 return 1;
445 }
a19cbfb3 446 if (cmd->type == QXL_CURSOR_SET) {
30f6da66 447 qemu_mutex_lock(&qxl->track_lock);
a19cbfb3 448 qxl->guest_cursor = ext->cmd.data;
30f6da66 449 qemu_mutex_unlock(&qxl->track_lock);
a19cbfb3
GH
450 }
451 break;
452 }
453 }
fae2afb1 454 return 0;
a19cbfb3
GH
455}
456
457/* spice display interface callbacks */
458
459static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
460{
461 PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
462
c480bb7d 463 trace_qxl_interface_attach_worker(qxl->id);
a19cbfb3
GH
464 qxl->ssd.worker = qxl_worker;
465}
466
467static void interface_set_compression_level(QXLInstance *sin, int level)
468{
469 PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
470
c480bb7d 471 trace_qxl_interface_set_compression_level(qxl->id, level);
a19cbfb3
GH
472 qxl->shadow_rom.compression_level = cpu_to_le32(level);
473 qxl->rom->compression_level = cpu_to_le32(level);
474 qxl_rom_set_dirty(qxl);
475}
476
477static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
478{
479 PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
480
c480bb7d 481 trace_qxl_interface_set_mm_time(qxl->id, mm_time);
a19cbfb3
GH
482 qxl->shadow_rom.mm_clock = cpu_to_le32(mm_time);
483 qxl->rom->mm_clock = cpu_to_le32(mm_time);
484 qxl_rom_set_dirty(qxl);
485}
486
487static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
488{
489 PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
490
c480bb7d 491 trace_qxl_interface_get_init_info(qxl->id);
a19cbfb3
GH
492 info->memslot_gen_bits = MEMSLOT_GENERATION_BITS;
493 info->memslot_id_bits = MEMSLOT_SLOT_BITS;
494 info->num_memslots = NUM_MEMSLOTS;
495 info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
496 info->internal_groupslot_id = 0;
497 info->qxl_ram_size = le32_to_cpu(qxl->shadow_rom.num_pages) << TARGET_PAGE_BITS;
498 info->n_surfaces = NUM_SURFACES;
499}
500
5b77870c
AL
501static const char *qxl_mode_to_string(int mode)
502{
503 switch (mode) {
504 case QXL_MODE_COMPAT:
505 return "compat";
506 case QXL_MODE_NATIVE:
507 return "native";
508 case QXL_MODE_UNDEFINED:
509 return "undefined";
510 case QXL_MODE_VGA:
511 return "vga";
512 }
513 return "INVALID";
514}
515
8b92e298
AL
516static const char *io_port_to_string(uint32_t io_port)
517{
518 if (io_port >= QXL_IO_RANGE_SIZE) {
519 return "out of range";
520 }
521 static const char *io_port_to_string[QXL_IO_RANGE_SIZE + 1] = {
522 [QXL_IO_NOTIFY_CMD] = "QXL_IO_NOTIFY_CMD",
523 [QXL_IO_NOTIFY_CURSOR] = "QXL_IO_NOTIFY_CURSOR",
524 [QXL_IO_UPDATE_AREA] = "QXL_IO_UPDATE_AREA",
525 [QXL_IO_UPDATE_IRQ] = "QXL_IO_UPDATE_IRQ",
526 [QXL_IO_NOTIFY_OOM] = "QXL_IO_NOTIFY_OOM",
527 [QXL_IO_RESET] = "QXL_IO_RESET",
528 [QXL_IO_SET_MODE] = "QXL_IO_SET_MODE",
529 [QXL_IO_LOG] = "QXL_IO_LOG",
530 [QXL_IO_MEMSLOT_ADD] = "QXL_IO_MEMSLOT_ADD",
531 [QXL_IO_MEMSLOT_DEL] = "QXL_IO_MEMSLOT_DEL",
532 [QXL_IO_DETACH_PRIMARY] = "QXL_IO_DETACH_PRIMARY",
533 [QXL_IO_ATTACH_PRIMARY] = "QXL_IO_ATTACH_PRIMARY",
534 [QXL_IO_CREATE_PRIMARY] = "QXL_IO_CREATE_PRIMARY",
535 [QXL_IO_DESTROY_PRIMARY] = "QXL_IO_DESTROY_PRIMARY",
536 [QXL_IO_DESTROY_SURFACE_WAIT] = "QXL_IO_DESTROY_SURFACE_WAIT",
537 [QXL_IO_DESTROY_ALL_SURFACES] = "QXL_IO_DESTROY_ALL_SURFACES",
8b92e298
AL
538 [QXL_IO_UPDATE_AREA_ASYNC] = "QXL_IO_UPDATE_AREA_ASYNC",
539 [QXL_IO_MEMSLOT_ADD_ASYNC] = "QXL_IO_MEMSLOT_ADD_ASYNC",
540 [QXL_IO_CREATE_PRIMARY_ASYNC] = "QXL_IO_CREATE_PRIMARY_ASYNC",
541 [QXL_IO_DESTROY_PRIMARY_ASYNC] = "QXL_IO_DESTROY_PRIMARY_ASYNC",
542 [QXL_IO_DESTROY_SURFACE_ASYNC] = "QXL_IO_DESTROY_SURFACE_ASYNC",
543 [QXL_IO_DESTROY_ALL_SURFACES_ASYNC]
544 = "QXL_IO_DESTROY_ALL_SURFACES_ASYNC",
545 [QXL_IO_FLUSH_SURFACES_ASYNC] = "QXL_IO_FLUSH_SURFACES_ASYNC",
546 [QXL_IO_FLUSH_RELEASE] = "QXL_IO_FLUSH_RELEASE",
8b92e298
AL
547 };
548 return io_port_to_string[io_port];
549}
550
a19cbfb3
GH
551/* called from spice server thread context only */
552static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
553{
554 PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
555 SimpleSpiceUpdate *update;
556 QXLCommandRing *ring;
557 QXLCommand *cmd;
e0c64d08 558 int notify, ret;
a19cbfb3 559
c480bb7d
AL
560 trace_qxl_ring_command_check(qxl->id, qxl_mode_to_string(qxl->mode));
561
a19cbfb3
GH
562 switch (qxl->mode) {
563 case QXL_MODE_VGA:
e0c64d08
GH
564 ret = false;
565 qemu_mutex_lock(&qxl->ssd.lock);
566 if (qxl->ssd.update != NULL) {
567 update = qxl->ssd.update;
568 qxl->ssd.update = NULL;
569 *ext = update->ext;
570 ret = true;
a19cbfb3 571 }
e0c64d08 572 qemu_mutex_unlock(&qxl->ssd.lock);
212496c9 573 if (ret) {
c480bb7d 574 trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode));
212496c9
AL
575 qxl_log_command(qxl, "vga", ext);
576 }
e0c64d08 577 return ret;
a19cbfb3
GH
578 case QXL_MODE_COMPAT:
579 case QXL_MODE_NATIVE:
580 case QXL_MODE_UNDEFINED:
a19cbfb3 581 ring = &qxl->ram->cmd_ring;
087e6a42 582 if (qxl->guest_bug || SPICE_RING_IS_EMPTY(ring)) {
a19cbfb3
GH
583 return false;
584 }
0b81c478
AL
585 SPICE_RING_CONS_ITEM(qxl, ring, cmd);
586 if (!cmd) {
587 return false;
588 }
a19cbfb3
GH
589 ext->cmd = *cmd;
590 ext->group_id = MEMSLOT_GROUP_GUEST;
591 ext->flags = qxl->cmdflags;
592 SPICE_RING_POP(ring, notify);
593 qxl_ring_set_dirty(qxl);
594 if (notify) {
595 qxl_send_events(qxl, QXL_INTERRUPT_DISPLAY);
596 }
597 qxl->guest_primary.commands++;
598 qxl_track_command(qxl, ext);
599 qxl_log_command(qxl, "cmd", ext);
0b81c478 600 trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode));
a19cbfb3
GH
601 return true;
602 default:
603 return false;
604 }
605}
606
607/* called from spice server thread context only */
608static int interface_req_cmd_notification(QXLInstance *sin)
609{
610 PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
611 int wait = 1;
612
c480bb7d 613 trace_qxl_ring_command_req_notification(qxl->id);
a19cbfb3
GH
614 switch (qxl->mode) {
615 case QXL_MODE_COMPAT:
616 case QXL_MODE_NATIVE:
617 case QXL_MODE_UNDEFINED:
618 SPICE_RING_CONS_WAIT(&qxl->ram->cmd_ring, wait);
619 qxl_ring_set_dirty(qxl);
620 break;
621 default:
622 /* nothing */
623 break;
624 }
625 return wait;
626}
627
628/* called from spice server thread context only */
629static inline void qxl_push_free_res(PCIQXLDevice *d, int flush)
630{
631 QXLReleaseRing *ring = &d->ram->release_ring;
632 uint64_t *item;
633 int notify;
634
635#define QXL_FREE_BUNCH_SIZE 32
636
637 if (ring->prod - ring->cons + 1 == ring->num_items) {
638 /* ring full -- can't push */
639 return;
640 }
641 if (!flush && d->oom_running) {
642 /* collect everything from oom handler before pushing */
643 return;
644 }
645 if (!flush && d->num_free_res < QXL_FREE_BUNCH_SIZE) {
646 /* collect a bit more before pushing */
647 return;
648 }
649
650 SPICE_RING_PUSH(ring, notify);
c480bb7d
AL
651 trace_qxl_ring_res_push(d->id, qxl_mode_to_string(d->mode),
652 d->guest_surfaces.count, d->num_free_res,
653 d->last_release, notify ? "yes" : "no");
654 trace_qxl_ring_res_push_rest(d->id, ring->prod - ring->cons,
655 ring->num_items, ring->prod, ring->cons);
a19cbfb3
GH
656 if (notify) {
657 qxl_send_events(d, QXL_INTERRUPT_DISPLAY);
658 }
0b81c478
AL
659 SPICE_RING_PROD_ITEM(d, ring, item);
660 if (!item) {
661 return;
662 }
a19cbfb3
GH
663 *item = 0;
664 d->num_free_res = 0;
665 d->last_release = NULL;
666 qxl_ring_set_dirty(d);
667}
668
669/* called from spice server thread context only */
670static void interface_release_resource(QXLInstance *sin,
671 struct QXLReleaseInfoExt ext)
672{
673 PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
674 QXLReleaseRing *ring;
675 uint64_t *item, id;
676
677 if (ext.group_id == MEMSLOT_GROUP_HOST) {
678 /* host group -> vga mode update request */
f4a8a424 679 qemu_spice_destroy_update(&qxl->ssd, (void *)(intptr_t)ext.info->id);
a19cbfb3
GH
680 return;
681 }
682
683 /*
684 * ext->info points into guest-visible memory
685 * pci bar 0, $command.release_info
686 */
687 ring = &qxl->ram->release_ring;
0b81c478
AL
688 SPICE_RING_PROD_ITEM(qxl, ring, item);
689 if (!item) {
690 return;
691 }
a19cbfb3
GH
692 if (*item == 0) {
693 /* stick head into the ring */
694 id = ext.info->id;
695 ext.info->next = 0;
696 qxl_ram_set_dirty(qxl, &ext.info->next);
697 *item = id;
698 qxl_ring_set_dirty(qxl);
699 } else {
700 /* append item to the list */
701 qxl->last_release->next = ext.info->id;
702 qxl_ram_set_dirty(qxl, &qxl->last_release->next);
703 ext.info->next = 0;
704 qxl_ram_set_dirty(qxl, &ext.info->next);
705 }
706 qxl->last_release = ext.info;
707 qxl->num_free_res++;
c480bb7d 708 trace_qxl_ring_res_put(qxl->id, qxl->num_free_res);
a19cbfb3
GH
709 qxl_push_free_res(qxl, 0);
710}
711
712/* called from spice server thread context only */
713static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext)
714{
715 PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
716 QXLCursorRing *ring;
717 QXLCommand *cmd;
718 int notify;
719
c480bb7d
AL
720 trace_qxl_ring_cursor_check(qxl->id, qxl_mode_to_string(qxl->mode));
721
a19cbfb3
GH
722 switch (qxl->mode) {
723 case QXL_MODE_COMPAT:
724 case QXL_MODE_NATIVE:
725 case QXL_MODE_UNDEFINED:
726 ring = &qxl->ram->cursor_ring;
727 if (SPICE_RING_IS_EMPTY(ring)) {
728 return false;
729 }
0b81c478
AL
730 SPICE_RING_CONS_ITEM(qxl, ring, cmd);
731 if (!cmd) {
732 return false;
733 }
a19cbfb3
GH
734 ext->cmd = *cmd;
735 ext->group_id = MEMSLOT_GROUP_GUEST;
736 ext->flags = qxl->cmdflags;
737 SPICE_RING_POP(ring, notify);
738 qxl_ring_set_dirty(qxl);
739 if (notify) {
740 qxl_send_events(qxl, QXL_INTERRUPT_CURSOR);
741 }
742 qxl->guest_primary.commands++;
743 qxl_track_command(qxl, ext);
744 qxl_log_command(qxl, "csr", ext);
745 if (qxl->id == 0) {
746 qxl_render_cursor(qxl, ext);
747 }
c480bb7d 748 trace_qxl_ring_cursor_get(qxl->id, qxl_mode_to_string(qxl->mode));
a19cbfb3
GH
749 return true;
750 default:
751 return false;
752 }
753}
754
755/* called from spice server thread context only */
756static int interface_req_cursor_notification(QXLInstance *sin)
757{
758 PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
759 int wait = 1;
760
c480bb7d 761 trace_qxl_ring_cursor_req_notification(qxl->id);
a19cbfb3
GH
762 switch (qxl->mode) {
763 case QXL_MODE_COMPAT:
764 case QXL_MODE_NATIVE:
765 case QXL_MODE_UNDEFINED:
766 SPICE_RING_CONS_WAIT(&qxl->ram->cursor_ring, wait);
767 qxl_ring_set_dirty(qxl);
768 break;
769 default:
770 /* nothing */
771 break;
772 }
773 return wait;
774}
775
776/* called from spice server thread context */
777static void interface_notify_update(QXLInstance *sin, uint32_t update_id)
778{
baeae407
AL
779 /*
780 * Called by spice-server as a result of a QXL_CMD_UPDATE which is not in
781 * use by xf86-video-qxl and is defined out in the qxl windows driver.
782 * Probably was at some earlier version that is prior to git start (2009),
783 * and is still guest trigerrable.
784 */
785 fprintf(stderr, "%s: deprecated\n", __func__);
a19cbfb3
GH
786}
787
788/* called from spice server thread context only */
789static int interface_flush_resources(QXLInstance *sin)
790{
791 PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
792 int ret;
793
a19cbfb3
GH
794 ret = qxl->num_free_res;
795 if (ret) {
796 qxl_push_free_res(qxl, 1);
797 }
798 return ret;
799}
800
5ff4e36c
AL
801static void qxl_create_guest_primary_complete(PCIQXLDevice *d);
802
5ff4e36c 803/* called from spice server thread context only */
2e1a98c9 804static void interface_async_complete_io(PCIQXLDevice *qxl, QXLCookie *cookie)
5ff4e36c 805{
5ff4e36c
AL
806 uint32_t current_async;
807
808 qemu_mutex_lock(&qxl->async_lock);
809 current_async = qxl->current_async;
810 qxl->current_async = QXL_UNDEFINED_IO;
811 qemu_mutex_unlock(&qxl->async_lock);
812
c480bb7d 813 trace_qxl_interface_async_complete_io(qxl->id, current_async, cookie);
2e1a98c9
AL
814 if (!cookie) {
815 fprintf(stderr, "qxl: %s: error, cookie is NULL\n", __func__);
816 return;
817 }
818 if (cookie && current_async != cookie->io) {
819 fprintf(stderr,
2fce7edf
AL
820 "qxl: %s: error: current_async = %d != %"
821 PRId64 " = cookie->io\n", __func__, current_async, cookie->io);
2e1a98c9 822 }
5ff4e36c 823 switch (current_async) {
81fb6f15
AL
824 case QXL_IO_MEMSLOT_ADD_ASYNC:
825 case QXL_IO_DESTROY_PRIMARY_ASYNC:
826 case QXL_IO_UPDATE_AREA_ASYNC:
827 case QXL_IO_FLUSH_SURFACES_ASYNC:
828 break;
5ff4e36c
AL
829 case QXL_IO_CREATE_PRIMARY_ASYNC:
830 qxl_create_guest_primary_complete(qxl);
831 break;
832 case QXL_IO_DESTROY_ALL_SURFACES_ASYNC:
833 qxl_spice_destroy_surfaces_complete(qxl);
834 break;
835 case QXL_IO_DESTROY_SURFACE_ASYNC:
2e1a98c9 836 qxl_spice_destroy_surface_wait_complete(qxl, cookie->u.surface_id);
5ff4e36c 837 break;
81fb6f15
AL
838 default:
839 fprintf(stderr, "qxl: %s: unexpected current_async %d\n", __func__,
840 current_async);
5ff4e36c
AL
841 }
842 qxl_send_events(qxl, QXL_INTERRUPT_IO_CMD);
843}
844
81fb6f15
AL
845/* called from spice server thread context only */
846static void interface_update_area_complete(QXLInstance *sin,
847 uint32_t surface_id,
848 QXLRect *dirty, uint32_t num_updated_rects)
849{
850 PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
851 int i;
852 int qxl_i;
853
854 qemu_mutex_lock(&qxl->ssd.lock);
855 if (surface_id != 0 || !qxl->render_update_cookie_num) {
856 qemu_mutex_unlock(&qxl->ssd.lock);
857 return;
858 }
c480bb7d
AL
859 trace_qxl_interface_update_area_complete(qxl->id, surface_id, dirty->left,
860 dirty->right, dirty->top, dirty->bottom);
861 trace_qxl_interface_update_area_complete_rest(qxl->id, num_updated_rects);
81fb6f15
AL
862 if (qxl->num_dirty_rects + num_updated_rects > QXL_NUM_DIRTY_RECTS) {
863 /*
864 * overflow - treat this as a full update. Not expected to be common.
865 */
c480bb7d
AL
866 trace_qxl_interface_update_area_complete_overflow(qxl->id,
867 QXL_NUM_DIRTY_RECTS);
81fb6f15
AL
868 qxl->guest_primary.resized = 1;
869 }
870 if (qxl->guest_primary.resized) {
871 /*
872 * Don't bother copying or scheduling the bh since we will flip
873 * the whole area anyway on completion of the update_area async call
874 */
875 qemu_mutex_unlock(&qxl->ssd.lock);
876 return;
877 }
878 qxl_i = qxl->num_dirty_rects;
879 for (i = 0; i < num_updated_rects; i++) {
880 qxl->dirty[qxl_i++] = dirty[i];
881 }
882 qxl->num_dirty_rects += num_updated_rects;
c480bb7d
AL
883 trace_qxl_interface_update_area_complete_schedule_bh(qxl->id,
884 qxl->num_dirty_rects);
81fb6f15
AL
885 qemu_bh_schedule(qxl->update_area_bh);
886 qemu_mutex_unlock(&qxl->ssd.lock);
887}
888
2e1a98c9
AL
889/* called from spice server thread context only */
890static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token)
891{
892 PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
5dba0d45 893 QXLCookie *cookie = (QXLCookie *)(uintptr_t)cookie_token;
2e1a98c9
AL
894
895 switch (cookie->type) {
896 case QXL_COOKIE_TYPE_IO:
897 interface_async_complete_io(qxl, cookie);
81fb6f15
AL
898 g_free(cookie);
899 break;
900 case QXL_COOKIE_TYPE_RENDER_UPDATE_AREA:
901 qxl_render_update_area_done(qxl, cookie);
2e1a98c9
AL
902 break;
903 default:
904 fprintf(stderr, "qxl: %s: unexpected cookie type %d\n",
905 __func__, cookie->type);
81fb6f15 906 g_free(cookie);
2e1a98c9 907 }
2e1a98c9
AL
908}
909
a19cbfb3
GH
910static const QXLInterface qxl_interface = {
911 .base.type = SPICE_INTERFACE_QXL,
912 .base.description = "qxl gpu",
913 .base.major_version = SPICE_INTERFACE_QXL_MAJOR,
914 .base.minor_version = SPICE_INTERFACE_QXL_MINOR,
915
916 .attache_worker = interface_attach_worker,
917 .set_compression_level = interface_set_compression_level,
918 .set_mm_time = interface_set_mm_time,
919 .get_init_info = interface_get_init_info,
920
921 /* the callbacks below are called from spice server thread context */
922 .get_command = interface_get_command,
923 .req_cmd_notification = interface_req_cmd_notification,
924 .release_resource = interface_release_resource,
925 .get_cursor_command = interface_get_cursor_command,
926 .req_cursor_notification = interface_req_cursor_notification,
927 .notify_update = interface_notify_update,
928 .flush_resources = interface_flush_resources,
5ff4e36c 929 .async_complete = interface_async_complete,
81fb6f15 930 .update_area_complete = interface_update_area_complete,
a19cbfb3
GH
931};
932
933static void qxl_enter_vga_mode(PCIQXLDevice *d)
934{
935 if (d->mode == QXL_MODE_VGA) {
936 return;
937 }
c480bb7d 938 trace_qxl_enter_vga_mode(d->id);
a19cbfb3
GH
939 qemu_spice_create_host_primary(&d->ssd);
940 d->mode = QXL_MODE_VGA;
941 memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty));
0f7bfd81 942 vga_dirty_log_start(&d->vga);
a19cbfb3
GH
943}
944
945static void qxl_exit_vga_mode(PCIQXLDevice *d)
946{
947 if (d->mode != QXL_MODE_VGA) {
948 return;
949 }
c480bb7d 950 trace_qxl_exit_vga_mode(d->id);
0f7bfd81 951 vga_dirty_log_stop(&d->vga);
5ff4e36c 952 qxl_destroy_primary(d, QXL_SYNC);
a19cbfb3
GH
953}
954
40010aea 955static void qxl_update_irq(PCIQXLDevice *d)
a19cbfb3
GH
956{
957 uint32_t pending = le32_to_cpu(d->ram->int_pending);
958 uint32_t mask = le32_to_cpu(d->ram->int_mask);
959 int level = !!(pending & mask);
960 qemu_set_irq(d->pci.irq[0], level);
961 qxl_ring_set_dirty(d);
962}
963
a19cbfb3
GH
964static void qxl_check_state(PCIQXLDevice *d)
965{
966 QXLRam *ram = d->ram;
967
be48e995
YH
968 assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cmd_ring));
969 assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cursor_ring));
a19cbfb3
GH
970}
971
972static void qxl_reset_state(PCIQXLDevice *d)
973{
a19cbfb3
GH
974 QXLRom *rom = d->rom;
975
be48e995 976 qxl_check_state(d);
a19cbfb3
GH
977 d->shadow_rom.update_id = cpu_to_le32(0);
978 *rom = d->shadow_rom;
979 qxl_rom_set_dirty(d);
980 init_qxl_ram(d);
981 d->num_free_res = 0;
982 d->last_release = NULL;
983 memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty));
984}
985
986static void qxl_soft_reset(PCIQXLDevice *d)
987{
c480bb7d 988 trace_qxl_soft_reset(d->id);
a19cbfb3 989 qxl_check_state(d);
087e6a42 990 qxl_clear_guest_bug(d);
a5f68c22 991 d->current_async = QXL_UNDEFINED_IO;
a19cbfb3
GH
992
993 if (d->id == 0) {
994 qxl_enter_vga_mode(d);
995 } else {
996 d->mode = QXL_MODE_UNDEFINED;
997 }
998}
999
1000static void qxl_hard_reset(PCIQXLDevice *d, int loadvm)
1001{
c480bb7d 1002 trace_qxl_hard_reset(d->id, loadvm);
a19cbfb3 1003
aee32bf3
GH
1004 qxl_spice_reset_cursor(d);
1005 qxl_spice_reset_image_cache(d);
a19cbfb3
GH
1006 qxl_reset_surfaces(d);
1007 qxl_reset_memslots(d);
1008
1009 /* pre loadvm reset must not touch QXLRam. This lives in
1010 * device memory, is migrated together with RAM and thus
1011 * already loaded at this point */
1012 if (!loadvm) {
1013 qxl_reset_state(d);
1014 }
1015 qemu_spice_create_host_memslot(&d->ssd);
1016 qxl_soft_reset(d);
a19cbfb3
GH
1017}
1018
1019static void qxl_reset_handler(DeviceState *dev)
1020{
1021 PCIQXLDevice *d = DO_UPCAST(PCIQXLDevice, pci.qdev, dev);
c480bb7d 1022
a19cbfb3
GH
1023 qxl_hard_reset(d, 0);
1024}
1025
1026static void qxl_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
1027{
1028 VGACommonState *vga = opaque;
1029 PCIQXLDevice *qxl = container_of(vga, PCIQXLDevice, vga);
1030
c480bb7d 1031 trace_qxl_io_write_vga(qxl->id, qxl_mode_to_string(qxl->mode), addr, val);
a19cbfb3 1032 if (qxl->mode != QXL_MODE_VGA) {
5ff4e36c 1033 qxl_destroy_primary(qxl, QXL_SYNC);
a19cbfb3
GH
1034 qxl_soft_reset(qxl);
1035 }
1036 vga_ioport_write(opaque, addr, val);
1037}
1038
f67ab77a
GH
1039static const MemoryRegionPortio qxl_vga_portio_list[] = {
1040 { 0x04, 2, 1, .read = vga_ioport_read,
1041 .write = qxl_vga_ioport_write }, /* 3b4 */
1042 { 0x0a, 1, 1, .read = vga_ioport_read,
1043 .write = qxl_vga_ioport_write }, /* 3ba */
1044 { 0x10, 16, 1, .read = vga_ioport_read,
1045 .write = qxl_vga_ioport_write }, /* 3c0 */
1046 { 0x24, 2, 1, .read = vga_ioport_read,
1047 .write = qxl_vga_ioport_write }, /* 3d4 */
1048 { 0x2a, 1, 1, .read = vga_ioport_read,
1049 .write = qxl_vga_ioport_write }, /* 3da */
1050 PORTIO_END_OF_LIST(),
1051};
1052
e954ea28
AL
1053static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta,
1054 qxl_async_io async)
a19cbfb3
GH
1055{
1056 static const int regions[] = {
1057 QXL_RAM_RANGE_INDEX,
1058 QXL_VRAM_RANGE_INDEX,
6f2b175a 1059 QXL_VRAM64_RANGE_INDEX,
a19cbfb3
GH
1060 };
1061 uint64_t guest_start;
1062 uint64_t guest_end;
1063 int pci_region;
1064 pcibus_t pci_start;
1065 pcibus_t pci_end;
1066 intptr_t virt_start;
1067 QXLDevMemSlot memslot;
1068 int i;
1069
1070 guest_start = le64_to_cpu(d->guest_slots[slot_id].slot.mem_start);
1071 guest_end = le64_to_cpu(d->guest_slots[slot_id].slot.mem_end);
1072
c480bb7d 1073 trace_qxl_memslot_add_guest(d->id, slot_id, guest_start, guest_end);
a19cbfb3 1074
e954ea28 1075 if (slot_id >= NUM_MEMSLOTS) {
0a530548 1076 qxl_set_guest_bug(d, "%s: slot_id >= NUM_MEMSLOTS %d >= %d", __func__,
e954ea28
AL
1077 slot_id, NUM_MEMSLOTS);
1078 return 1;
1079 }
1080 if (guest_start > guest_end) {
0a530548 1081 qxl_set_guest_bug(d, "%s: guest_start > guest_end 0x%" PRIx64
e954ea28
AL
1082 " > 0x%" PRIx64, __func__, guest_start, guest_end);
1083 return 1;
1084 }
a19cbfb3
GH
1085
1086 for (i = 0; i < ARRAY_SIZE(regions); i++) {
1087 pci_region = regions[i];
1088 pci_start = d->pci.io_regions[pci_region].addr;
1089 pci_end = pci_start + d->pci.io_regions[pci_region].size;
1090 /* mapped? */
1091 if (pci_start == -1) {
1092 continue;
1093 }
1094 /* start address in range ? */
1095 if (guest_start < pci_start || guest_start > pci_end) {
1096 continue;
1097 }
1098 /* end address in range ? */
1099 if (guest_end > pci_end) {
1100 continue;
1101 }
1102 /* passed */
1103 break;
1104 }
e954ea28 1105 if (i == ARRAY_SIZE(regions)) {
0a530548 1106 qxl_set_guest_bug(d, "%s: finished loop without match", __func__);
e954ea28
AL
1107 return 1;
1108 }
a19cbfb3
GH
1109
1110 switch (pci_region) {
1111 case QXL_RAM_RANGE_INDEX:
b1950430 1112 virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vga.vram);
a19cbfb3
GH
1113 break;
1114 case QXL_VRAM_RANGE_INDEX:
6f2b175a 1115 case 4 /* vram 64bit */:
b1950430 1116 virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vram_bar);
a19cbfb3
GH
1117 break;
1118 default:
1119 /* should not happen */
0a530548 1120 qxl_set_guest_bug(d, "%s: pci_region = %d", __func__, pci_region);
e954ea28 1121 return 1;
a19cbfb3
GH
1122 }
1123
1124 memslot.slot_id = slot_id;
1125 memslot.slot_group_id = MEMSLOT_GROUP_GUEST; /* guest group */
1126 memslot.virt_start = virt_start + (guest_start - pci_start);
1127 memslot.virt_end = virt_start + (guest_end - pci_start);
1128 memslot.addr_delta = memslot.virt_start - delta;
1129 memslot.generation = d->rom->slot_generation = 0;
1130 qxl_rom_set_dirty(d);
1131
5ff4e36c 1132 qemu_spice_add_memslot(&d->ssd, &memslot, async);
a19cbfb3
GH
1133 d->guest_slots[slot_id].ptr = (void*)memslot.virt_start;
1134 d->guest_slots[slot_id].size = memslot.virt_end - memslot.virt_start;
1135 d->guest_slots[slot_id].delta = delta;
1136 d->guest_slots[slot_id].active = 1;
e954ea28 1137 return 0;
a19cbfb3
GH
1138}
1139
1140static void qxl_del_memslot(PCIQXLDevice *d, uint32_t slot_id)
1141{
5c59d118 1142 qemu_spice_del_memslot(&d->ssd, MEMSLOT_GROUP_HOST, slot_id);
a19cbfb3
GH
1143 d->guest_slots[slot_id].active = 0;
1144}
1145
1146static void qxl_reset_memslots(PCIQXLDevice *d)
1147{
aee32bf3 1148 qxl_spice_reset_memslots(d);
a19cbfb3
GH
1149 memset(&d->guest_slots, 0, sizeof(d->guest_slots));
1150}
1151
1152static void qxl_reset_surfaces(PCIQXLDevice *d)
1153{
c480bb7d 1154 trace_qxl_reset_surfaces(d->id);
a19cbfb3 1155 d->mode = QXL_MODE_UNDEFINED;
5ff4e36c 1156 qxl_spice_destroy_surfaces(d, QXL_SYNC);
a19cbfb3
GH
1157}
1158
e25139b3 1159/* can be also called from spice server thread context */
a19cbfb3
GH
1160void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id)
1161{
1162 uint64_t phys = le64_to_cpu(pqxl);
1163 uint32_t slot = (phys >> (64 - 8)) & 0xff;
1164 uint64_t offset = phys & 0xffffffffffff;
1165
1166 switch (group_id) {
1167 case MEMSLOT_GROUP_HOST:
f4a8a424 1168 return (void *)(intptr_t)offset;
a19cbfb3 1169 case MEMSLOT_GROUP_GUEST:
4b635c59 1170 if (slot >= NUM_MEMSLOTS) {
0a530548
AL
1171 qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot,
1172 NUM_MEMSLOTS);
4b635c59
AL
1173 return NULL;
1174 }
1175 if (!qxl->guest_slots[slot].active) {
0a530548 1176 qxl_set_guest_bug(qxl, "inactive slot %d\n", slot);
4b635c59
AL
1177 return NULL;
1178 }
1179 if (offset < qxl->guest_slots[slot].delta) {
0a530548
AL
1180 qxl_set_guest_bug(qxl,
1181 "slot %d offset %"PRIu64" < delta %"PRIu64"\n",
4b635c59
AL
1182 slot, offset, qxl->guest_slots[slot].delta);
1183 return NULL;
1184 }
a19cbfb3 1185 offset -= qxl->guest_slots[slot].delta;
4b635c59 1186 if (offset > qxl->guest_slots[slot].size) {
0a530548
AL
1187 qxl_set_guest_bug(qxl,
1188 "slot %d offset %"PRIu64" > size %"PRIu64"\n",
4b635c59
AL
1189 slot, offset, qxl->guest_slots[slot].size);
1190 return NULL;
1191 }
a19cbfb3 1192 return qxl->guest_slots[slot].ptr + offset;
a19cbfb3 1193 }
4b635c59 1194 return NULL;
a19cbfb3
GH
1195}
1196
5ff4e36c
AL
1197static void qxl_create_guest_primary_complete(PCIQXLDevice *qxl)
1198{
1199 /* for local rendering */
1200 qxl_render_resize(qxl);
1201}
1202
1203static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
1204 qxl_async_io async)
a19cbfb3
GH
1205{
1206 QXLDevSurfaceCreate surface;
1207 QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
1208
ddf9f4b7 1209 if (qxl->mode == QXL_MODE_NATIVE) {
0a530548 1210 qxl_set_guest_bug(qxl, "%s: nop since already in QXL_MODE_NATIVE",
ddf9f4b7
AL
1211 __func__);
1212 }
a19cbfb3
GH
1213 qxl_exit_vga_mode(qxl);
1214
a19cbfb3
GH
1215 surface.format = le32_to_cpu(sc->format);
1216 surface.height = le32_to_cpu(sc->height);
1217 surface.mem = le64_to_cpu(sc->mem);
1218 surface.position = le32_to_cpu(sc->position);
1219 surface.stride = le32_to_cpu(sc->stride);
1220 surface.width = le32_to_cpu(sc->width);
1221 surface.type = le32_to_cpu(sc->type);
1222 surface.flags = le32_to_cpu(sc->flags);
c480bb7d
AL
1223 trace_qxl_create_guest_primary(qxl->id, sc->width, sc->height, sc->mem,
1224 sc->format, sc->position);
1225 trace_qxl_create_guest_primary_rest(qxl->id, sc->stride, sc->type,
1226 sc->flags);
a19cbfb3
GH
1227
1228 surface.mouse_mode = true;
1229 surface.group_id = MEMSLOT_GROUP_GUEST;
1230 if (loadvm) {
1231 surface.flags |= QXL_SURF_FLAG_KEEP_DATA;
1232 }
1233
1234 qxl->mode = QXL_MODE_NATIVE;
1235 qxl->cmdflags = 0;
5ff4e36c 1236 qemu_spice_create_primary_surface(&qxl->ssd, 0, &surface, async);
a19cbfb3 1237
5ff4e36c
AL
1238 if (async == QXL_SYNC) {
1239 qxl_create_guest_primary_complete(qxl);
1240 }
a19cbfb3
GH
1241}
1242
5ff4e36c
AL
1243/* return 1 if surface destoy was initiated (in QXL_ASYNC case) or
1244 * done (in QXL_SYNC case), 0 otherwise. */
1245static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async)
a19cbfb3
GH
1246{
1247 if (d->mode == QXL_MODE_UNDEFINED) {
5ff4e36c 1248 return 0;
a19cbfb3 1249 }
c480bb7d 1250 trace_qxl_destroy_primary(d->id);
a19cbfb3 1251 d->mode = QXL_MODE_UNDEFINED;
5ff4e36c 1252 qemu_spice_destroy_primary_surface(&d->ssd, 0, async);
30f6da66 1253 qxl_spice_reset_cursor(d);
5ff4e36c 1254 return 1;
a19cbfb3
GH
1255}
1256
1257static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm)
1258{
1259 pcibus_t start = d->pci.io_regions[QXL_RAM_RANGE_INDEX].addr;
1260 pcibus_t end = d->pci.io_regions[QXL_RAM_RANGE_INDEX].size + start;
1261 QXLMode *mode = d->modes->modes + modenr;
1262 uint64_t devmem = d->pci.io_regions[QXL_RAM_RANGE_INDEX].addr;
1263 QXLMemSlot slot = {
1264 .mem_start = start,
1265 .mem_end = end
1266 };
1267 QXLSurfaceCreate surface = {
1268 .width = mode->x_res,
1269 .height = mode->y_res,
1270 .stride = -mode->x_res * 4,
1271 .format = SPICE_SURFACE_FMT_32_xRGB,
1272 .flags = loadvm ? QXL_SURF_FLAG_KEEP_DATA : 0,
1273 .mouse_mode = true,
1274 .mem = devmem + d->shadow_rom.draw_area_offset,
1275 };
1276
c480bb7d
AL
1277 trace_qxl_set_mode(d->id, modenr, mode->x_res, mode->y_res, mode->bits,
1278 devmem);
a19cbfb3
GH
1279 if (!loadvm) {
1280 qxl_hard_reset(d, 0);
1281 }
1282
1283 d->guest_slots[0].slot = slot;
e954ea28 1284 assert(qxl_add_memslot(d, 0, devmem, QXL_SYNC) == 0);
a19cbfb3
GH
1285
1286 d->guest_primary.surface = surface;
5ff4e36c 1287 qxl_create_guest_primary(d, 0, QXL_SYNC);
a19cbfb3
GH
1288
1289 d->mode = QXL_MODE_COMPAT;
1290 d->cmdflags = QXL_COMMAND_FLAG_COMPAT;
1291#ifdef QXL_COMMAND_FLAG_COMPAT_16BPP /* new in spice 0.6.1 */
1292 if (mode->bits == 16) {
1293 d->cmdflags |= QXL_COMMAND_FLAG_COMPAT_16BPP;
1294 }
1295#endif
1296 d->shadow_rom.mode = cpu_to_le32(modenr);
1297 d->rom->mode = cpu_to_le32(modenr);
1298 qxl_rom_set_dirty(d);
1299}
1300
b1950430
AK
1301static void ioport_write(void *opaque, target_phys_addr_t addr,
1302 uint64_t val, unsigned size)
a19cbfb3
GH
1303{
1304 PCIQXLDevice *d = opaque;
b1950430 1305 uint32_t io_port = addr;
5ff4e36c 1306 qxl_async_io async = QXL_SYNC;
5ff4e36c 1307 uint32_t orig_io_port = io_port;
a19cbfb3 1308
087e6a42
AL
1309 if (d->guest_bug && !io_port == QXL_IO_RESET) {
1310 return;
1311 }
1312
a19cbfb3
GH
1313 switch (io_port) {
1314 case QXL_IO_RESET:
1315 case QXL_IO_SET_MODE:
1316 case QXL_IO_MEMSLOT_ADD:
1317 case QXL_IO_MEMSLOT_DEL:
1318 case QXL_IO_CREATE_PRIMARY:
81144d1a 1319 case QXL_IO_UPDATE_IRQ:
a3d14054 1320 case QXL_IO_LOG:
5ff4e36c
AL
1321 case QXL_IO_MEMSLOT_ADD_ASYNC:
1322 case QXL_IO_CREATE_PRIMARY_ASYNC:
a19cbfb3
GH
1323 break;
1324 default:
e21a298a 1325 if (d->mode != QXL_MODE_VGA) {
a19cbfb3 1326 break;
e21a298a 1327 }
c480bb7d
AL
1328 trace_qxl_io_unexpected_vga_mode(d->id,
1329 io_port, io_port_to_string(io_port));
5ff4e36c
AL
1330 /* be nice to buggy guest drivers */
1331 if (io_port >= QXL_IO_UPDATE_AREA_ASYNC &&
1332 io_port <= QXL_IO_DESTROY_ALL_SURFACES_ASYNC) {
1333 qxl_send_events(d, QXL_INTERRUPT_IO_CMD);
1334 }
a19cbfb3
GH
1335 return;
1336 }
1337
5ff4e36c
AL
1338 /* we change the io_port to avoid ifdeffery in the main switch */
1339 orig_io_port = io_port;
1340 switch (io_port) {
1341 case QXL_IO_UPDATE_AREA_ASYNC:
1342 io_port = QXL_IO_UPDATE_AREA;
1343 goto async_common;
1344 case QXL_IO_MEMSLOT_ADD_ASYNC:
1345 io_port = QXL_IO_MEMSLOT_ADD;
1346 goto async_common;
1347 case QXL_IO_CREATE_PRIMARY_ASYNC:
1348 io_port = QXL_IO_CREATE_PRIMARY;
1349 goto async_common;
1350 case QXL_IO_DESTROY_PRIMARY_ASYNC:
1351 io_port = QXL_IO_DESTROY_PRIMARY;
1352 goto async_common;
1353 case QXL_IO_DESTROY_SURFACE_ASYNC:
1354 io_port = QXL_IO_DESTROY_SURFACE_WAIT;
1355 goto async_common;
1356 case QXL_IO_DESTROY_ALL_SURFACES_ASYNC:
1357 io_port = QXL_IO_DESTROY_ALL_SURFACES;
3e16b9c5
AL
1358 goto async_common;
1359 case QXL_IO_FLUSH_SURFACES_ASYNC:
5ff4e36c
AL
1360async_common:
1361 async = QXL_ASYNC;
1362 qemu_mutex_lock(&d->async_lock);
1363 if (d->current_async != QXL_UNDEFINED_IO) {
0a530548 1364 qxl_set_guest_bug(d, "%d async started before last (%d) complete",
5ff4e36c
AL
1365 io_port, d->current_async);
1366 qemu_mutex_unlock(&d->async_lock);
1367 return;
1368 }
1369 d->current_async = orig_io_port;
1370 qemu_mutex_unlock(&d->async_lock);
5ff4e36c
AL
1371 break;
1372 default:
1373 break;
1374 }
c480bb7d
AL
1375 trace_qxl_io_write(d->id, qxl_mode_to_string(d->mode), addr, val, size,
1376 async);
5ff4e36c 1377
a19cbfb3
GH
1378 switch (io_port) {
1379 case QXL_IO_UPDATE_AREA:
1380 {
81fb6f15 1381 QXLCookie *cookie = NULL;
a19cbfb3 1382 QXLRect update = d->ram->update_area;
81fb6f15
AL
1383
1384 if (async == QXL_ASYNC) {
1385 cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
1386 QXL_IO_UPDATE_AREA_ASYNC);
1387 cookie->u.area = update;
1388 }
aee32bf3 1389 qxl_spice_update_area(d, d->ram->update_surface,
81fb6f15
AL
1390 cookie ? &cookie->u.area : &update,
1391 NULL, 0, 0, async, cookie);
a19cbfb3
GH
1392 break;
1393 }
1394 case QXL_IO_NOTIFY_CMD:
5c59d118 1395 qemu_spice_wakeup(&d->ssd);
a19cbfb3
GH
1396 break;
1397 case QXL_IO_NOTIFY_CURSOR:
5c59d118 1398 qemu_spice_wakeup(&d->ssd);
a19cbfb3
GH
1399 break;
1400 case QXL_IO_UPDATE_IRQ:
40010aea 1401 qxl_update_irq(d);
a19cbfb3
GH
1402 break;
1403 case QXL_IO_NOTIFY_OOM:
a19cbfb3
GH
1404 if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) {
1405 break;
1406 }
1407 d->oom_running = 1;
aee32bf3 1408 qxl_spice_oom(d);
a19cbfb3
GH
1409 d->oom_running = 0;
1410 break;
1411 case QXL_IO_SET_MODE:
a19cbfb3
GH
1412 qxl_set_mode(d, val, 0);
1413 break;
1414 case QXL_IO_LOG:
1415 if (d->guestdebug) {
a680f7e7 1416 fprintf(stderr, "qxl/guest-%d: %" PRId64 ": %s", d->id,
6ebebb55 1417 qemu_get_clock_ns(vm_clock), d->ram->log_buf);
a19cbfb3
GH
1418 }
1419 break;
1420 case QXL_IO_RESET:
a19cbfb3
GH
1421 qxl_hard_reset(d, 0);
1422 break;
1423 case QXL_IO_MEMSLOT_ADD:
2bce0400 1424 if (val >= NUM_MEMSLOTS) {
0a530548 1425 qxl_set_guest_bug(d, "QXL_IO_MEMSLOT_ADD: val out of range");
2bce0400
GH
1426 break;
1427 }
1428 if (d->guest_slots[val].active) {
0a530548
AL
1429 qxl_set_guest_bug(d,
1430 "QXL_IO_MEMSLOT_ADD: memory slot already active");
2bce0400
GH
1431 break;
1432 }
a19cbfb3 1433 d->guest_slots[val].slot = d->ram->mem_slot;
5ff4e36c 1434 qxl_add_memslot(d, val, 0, async);
a19cbfb3
GH
1435 break;
1436 case QXL_IO_MEMSLOT_DEL:
2bce0400 1437 if (val >= NUM_MEMSLOTS) {
0a530548 1438 qxl_set_guest_bug(d, "QXL_IO_MEMSLOT_DEL: val out of range");
2bce0400
GH
1439 break;
1440 }
a19cbfb3
GH
1441 qxl_del_memslot(d, val);
1442 break;
1443 case QXL_IO_CREATE_PRIMARY:
2bce0400 1444 if (val != 0) {
0a530548 1445 qxl_set_guest_bug(d, "QXL_IO_CREATE_PRIMARY (async=%d): val != 0",
5ff4e36c
AL
1446 async);
1447 goto cancel_async;
2bce0400 1448 }
a19cbfb3 1449 d->guest_primary.surface = d->ram->create_surface;
5ff4e36c 1450 qxl_create_guest_primary(d, 0, async);
a19cbfb3
GH
1451 break;
1452 case QXL_IO_DESTROY_PRIMARY:
2bce0400 1453 if (val != 0) {
0a530548 1454 qxl_set_guest_bug(d, "QXL_IO_DESTROY_PRIMARY (async=%d): val != 0",
5ff4e36c
AL
1455 async);
1456 goto cancel_async;
1457 }
5ff4e36c 1458 if (!qxl_destroy_primary(d, async)) {
c480bb7d
AL
1459 trace_qxl_io_destroy_primary_ignored(d->id,
1460 qxl_mode_to_string(d->mode));
5ff4e36c 1461 goto cancel_async;
2bce0400 1462 }
a19cbfb3
GH
1463 break;
1464 case QXL_IO_DESTROY_SURFACE_WAIT:
5ff4e36c 1465 if (val >= NUM_SURFACES) {
0a530548 1466 qxl_set_guest_bug(d, "QXL_IO_DESTROY_SURFACE (async=%d):"
5f8daf2e 1467 "%" PRIu64 " >= NUM_SURFACES", async, val);
5ff4e36c
AL
1468 goto cancel_async;
1469 }
1470 qxl_spice_destroy_surface_wait(d, val, async);
a19cbfb3 1471 break;
3e16b9c5
AL
1472 case QXL_IO_FLUSH_RELEASE: {
1473 QXLReleaseRing *ring = &d->ram->release_ring;
1474 if (ring->prod - ring->cons + 1 == ring->num_items) {
1475 fprintf(stderr,
1476 "ERROR: no flush, full release ring [p%d,%dc]\n",
1477 ring->prod, ring->cons);
1478 }
1479 qxl_push_free_res(d, 1 /* flush */);
3e16b9c5
AL
1480 break;
1481 }
1482 case QXL_IO_FLUSH_SURFACES_ASYNC:
3e16b9c5
AL
1483 qxl_spice_flush_surfaces_async(d);
1484 break;
a19cbfb3 1485 case QXL_IO_DESTROY_ALL_SURFACES:
5ff4e36c
AL
1486 d->mode = QXL_MODE_UNDEFINED;
1487 qxl_spice_destroy_surfaces(d, async);
a19cbfb3
GH
1488 break;
1489 default:
0a530548 1490 qxl_set_guest_bug(d, "%s: unexpected ioport=0x%x\n", __func__, io_port);
a19cbfb3 1491 }
5ff4e36c
AL
1492 return;
1493cancel_async:
5ff4e36c
AL
1494 if (async) {
1495 qxl_send_events(d, QXL_INTERRUPT_IO_CMD);
1496 qemu_mutex_lock(&d->async_lock);
1497 d->current_async = QXL_UNDEFINED_IO;
1498 qemu_mutex_unlock(&d->async_lock);
1499 }
a19cbfb3
GH
1500}
1501
b1950430
AK
1502static uint64_t ioport_read(void *opaque, target_phys_addr_t addr,
1503 unsigned size)
a19cbfb3
GH
1504{
1505 PCIQXLDevice *d = opaque;
1506
c480bb7d 1507 trace_qxl_io_read_unexpected(d->id);
a19cbfb3
GH
1508 return 0xff;
1509}
1510
b1950430
AK
1511static const MemoryRegionOps qxl_io_ops = {
1512 .read = ioport_read,
1513 .write = ioport_write,
1514 .valid = {
1515 .min_access_size = 1,
1516 .max_access_size = 1,
1517 },
1518};
a19cbfb3
GH
1519
1520static void pipe_read(void *opaque)
1521{
1522 PCIQXLDevice *d = opaque;
1523 char dummy;
1524 int len;
1525
1526 do {
1527 len = read(d->pipe[0], &dummy, sizeof(dummy));
1528 } while (len == sizeof(dummy));
40010aea 1529 qxl_update_irq(d);
a19cbfb3
GH
1530}
1531
a19cbfb3
GH
1532static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
1533{
1534 uint32_t old_pending;
1535 uint32_t le_events = cpu_to_le32(events);
1536
1537 assert(d->ssd.running);
1538 old_pending = __sync_fetch_and_or(&d->ram->int_pending, le_events);
1539 if ((old_pending & le_events) == le_events) {
1540 return;
1541 }
691f5c7b 1542 if (qemu_thread_is_self(&d->main)) {
40010aea 1543 qxl_update_irq(d);
a19cbfb3
GH
1544 } else {
1545 if (write(d->pipe[1], d, 1) != 1) {
75fe0d7b 1546 dprint(d, 1, "%s: write to pipe failed\n", __func__);
a19cbfb3
GH
1547 }
1548 }
1549}
1550
1551static void init_pipe_signaling(PCIQXLDevice *d)
1552{
aa3db423
AL
1553 if (pipe(d->pipe) < 0) {
1554 fprintf(stderr, "%s:%s: qxl pipe creation failed\n",
1555 __FILE__, __func__);
1556 exit(1);
1557 }
1558 fcntl(d->pipe[0], F_SETFL, O_NONBLOCK);
1559 fcntl(d->pipe[1], F_SETFL, O_NONBLOCK);
1560 fcntl(d->pipe[0], F_SETOWN, getpid());
1561
1562 qemu_thread_get_self(&d->main);
1563 qemu_set_fd_handler(d->pipe[0], pipe_read, NULL, d);
a19cbfb3
GH
1564}
1565
1566/* graphics console */
1567
1568static void qxl_hw_update(void *opaque)
1569{
1570 PCIQXLDevice *qxl = opaque;
1571 VGACommonState *vga = &qxl->vga;
1572
1573 switch (qxl->mode) {
1574 case QXL_MODE_VGA:
1575 vga->update(vga);
1576 break;
1577 case QXL_MODE_COMPAT:
1578 case QXL_MODE_NATIVE:
1579 qxl_render_update(qxl);
1580 break;
1581 default:
1582 break;
1583 }
1584}
1585
1586static void qxl_hw_invalidate(void *opaque)
1587{
1588 PCIQXLDevice *qxl = opaque;
1589 VGACommonState *vga = &qxl->vga;
1590
1591 vga->invalidate(vga);
1592}
1593
45efb161 1594static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch)
a19cbfb3
GH
1595{
1596 PCIQXLDevice *qxl = opaque;
1597 VGACommonState *vga = &qxl->vga;
1598
1599 switch (qxl->mode) {
1600 case QXL_MODE_COMPAT:
1601 case QXL_MODE_NATIVE:
1602 qxl_render_update(qxl);
1603 ppm_save(filename, qxl->ssd.ds->surface);
1604 break;
1605 case QXL_MODE_VGA:
45efb161 1606 vga->screen_dump(vga, filename, cswitch);
a19cbfb3
GH
1607 break;
1608 default:
1609 break;
1610 }
1611}
1612
1613static void qxl_hw_text_update(void *opaque, console_ch_t *chardata)
1614{
1615 PCIQXLDevice *qxl = opaque;
1616 VGACommonState *vga = &qxl->vga;
1617
1618 if (qxl->mode == QXL_MODE_VGA) {
1619 vga->text_update(vga, chardata);
1620 return;
1621 }
1622}
1623
e25139b3
YH
1624static void qxl_dirty_surfaces(PCIQXLDevice *qxl)
1625{
1626 intptr_t vram_start;
1627 int i;
1628
2aa9e85c 1629 if (qxl->mode != QXL_MODE_NATIVE && qxl->mode != QXL_MODE_COMPAT) {
e25139b3
YH
1630 return;
1631 }
1632
1633 /* dirty the primary surface */
1634 qxl_set_dirty(&qxl->vga.vram, qxl->shadow_rom.draw_area_offset,
1635 qxl->shadow_rom.surface0_area_size);
1636
1637 vram_start = (intptr_t)memory_region_get_ram_ptr(&qxl->vram_bar);
1638
1639 /* dirty the off-screen surfaces */
1640 for (i = 0; i < NUM_SURFACES; i++) {
1641 QXLSurfaceCmd *cmd;
1642 intptr_t surface_offset;
1643 int surface_size;
1644
1645 if (qxl->guest_surfaces.cmds[i] == 0) {
1646 continue;
1647 }
1648
1649 cmd = qxl_phys2virt(qxl, qxl->guest_surfaces.cmds[i],
1650 MEMSLOT_GROUP_GUEST);
fae2afb1 1651 assert(cmd);
e25139b3
YH
1652 assert(cmd->type == QXL_SURFACE_CMD_CREATE);
1653 surface_offset = (intptr_t)qxl_phys2virt(qxl,
1654 cmd->u.surface_create.data,
1655 MEMSLOT_GROUP_GUEST);
fae2afb1 1656 assert(surface_offset);
e25139b3
YH
1657 surface_offset -= vram_start;
1658 surface_size = cmd->u.surface_create.height *
1659 abs(cmd->u.surface_create.stride);
c480bb7d 1660 trace_qxl_surfaces_dirty(qxl->id, i, (int)surface_offset, surface_size);
e25139b3
YH
1661 qxl_set_dirty(&qxl->vram_bar, surface_offset, surface_size);
1662 }
1663}
1664
1dfb4dd9
LC
1665static void qxl_vm_change_state_handler(void *opaque, int running,
1666 RunState state)
a19cbfb3
GH
1667{
1668 PCIQXLDevice *qxl = opaque;
1dfb4dd9 1669 qemu_spice_vm_change_state_handler(&qxl->ssd, running, state);
a19cbfb3 1670
efbf2950
YH
1671 if (running) {
1672 /*
1673 * if qxl_send_events was called from spice server context before
40010aea 1674 * migration ended, qxl_update_irq for these events might not have been
efbf2950
YH
1675 * called
1676 */
40010aea 1677 qxl_update_irq(qxl);
e25139b3
YH
1678 } else {
1679 /* make sure surfaces are saved before migration */
1680 qxl_dirty_surfaces(qxl);
a19cbfb3
GH
1681 }
1682}
1683
1684/* display change listener */
1685
1686static void display_update(struct DisplayState *ds, int x, int y, int w, int h)
1687{
1688 if (qxl0->mode == QXL_MODE_VGA) {
1689 qemu_spice_display_update(&qxl0->ssd, x, y, w, h);
1690 }
1691}
1692
1693static void display_resize(struct DisplayState *ds)
1694{
1695 if (qxl0->mode == QXL_MODE_VGA) {
1696 qemu_spice_display_resize(&qxl0->ssd);
1697 }
1698}
1699
1700static void display_refresh(struct DisplayState *ds)
1701{
1702 if (qxl0->mode == QXL_MODE_VGA) {
1703 qemu_spice_display_refresh(&qxl0->ssd);
bb5a8cd5
AL
1704 } else {
1705 qemu_mutex_lock(&qxl0->ssd.lock);
1706 qemu_spice_cursor_refresh_unlocked(&qxl0->ssd);
1707 qemu_mutex_unlock(&qxl0->ssd.lock);
a19cbfb3
GH
1708 }
1709}
1710
1711static DisplayChangeListener display_listener = {
1712 .dpy_update = display_update,
1713 .dpy_resize = display_resize,
1714 .dpy_refresh = display_refresh,
1715};
1716
a974192c
GH
1717static void qxl_init_ramsize(PCIQXLDevice *qxl, uint32_t ram_min_mb)
1718{
1719 /* vga ram (bar 0) */
017438ee
GH
1720 if (qxl->ram_size_mb != -1) {
1721 qxl->vga.vram_size = qxl->ram_size_mb * 1024 * 1024;
1722 }
a974192c
GH
1723 if (qxl->vga.vram_size < ram_min_mb * 1024 * 1024) {
1724 qxl->vga.vram_size = ram_min_mb * 1024 * 1024;
1725 }
1726
6f2b175a
GH
1727 /* vram32 (surfaces, 32bit, bar 1) */
1728 if (qxl->vram32_size_mb != -1) {
1729 qxl->vram32_size = qxl->vram32_size_mb * 1024 * 1024;
1730 }
1731 if (qxl->vram32_size < 4096) {
1732 qxl->vram32_size = 4096;
1733 }
1734
1735 /* vram (surfaces, 64bit, bar 4+5) */
017438ee
GH
1736 if (qxl->vram_size_mb != -1) {
1737 qxl->vram_size = qxl->vram_size_mb * 1024 * 1024;
1738 }
6f2b175a
GH
1739 if (qxl->vram_size < qxl->vram32_size) {
1740 qxl->vram_size = qxl->vram32_size;
a974192c 1741 }
6f2b175a 1742
a974192c 1743 if (qxl->revision == 1) {
6f2b175a 1744 qxl->vram32_size = 4096;
a974192c
GH
1745 qxl->vram_size = 4096;
1746 }
a974192c 1747 qxl->vga.vram_size = msb_mask(qxl->vga.vram_size * 2 - 1);
6f2b175a 1748 qxl->vram32_size = msb_mask(qxl->vram32_size * 2 - 1);
a974192c
GH
1749 qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1);
1750}
1751
a19cbfb3
GH
1752static int qxl_init_common(PCIQXLDevice *qxl)
1753{
1754 uint8_t* config = qxl->pci.config;
a19cbfb3
GH
1755 uint32_t pci_device_rev;
1756 uint32_t io_size;
1757
1758 qxl->mode = QXL_MODE_UNDEFINED;
1759 qxl->generation = 1;
1760 qxl->num_memslots = NUM_MEMSLOTS;
1761 qxl->num_surfaces = NUM_SURFACES;
14898cf6 1762 qemu_mutex_init(&qxl->track_lock);
5ff4e36c
AL
1763 qemu_mutex_init(&qxl->async_lock);
1764 qxl->current_async = QXL_UNDEFINED_IO;
087e6a42 1765 qxl->guest_bug = 0;
a19cbfb3
GH
1766
1767 switch (qxl->revision) {
1768 case 1: /* spice 0.4 -- qxl-1 */
a19cbfb3 1769 pci_device_rev = QXL_REVISION_STABLE_V04;
3f6297b9 1770 io_size = 8;
a19cbfb3
GH
1771 break;
1772 case 2: /* spice 0.6 -- qxl-2 */
a19cbfb3 1773 pci_device_rev = QXL_REVISION_STABLE_V06;
3f6297b9 1774 io_size = 16;
a19cbfb3 1775 break;
9197a7c8 1776 case 3: /* qxl-3 */
9197a7c8
GH
1777 default:
1778 pci_device_rev = QXL_DEFAULT_REVISION;
3f6297b9 1779 io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1);
9197a7c8 1780 break;
a19cbfb3
GH
1781 }
1782
a19cbfb3
GH
1783 pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev);
1784 pci_set_byte(&config[PCI_INTERRUPT_PIN], 1);
1785
1786 qxl->rom_size = qxl_rom_size();
c5705a77
AK
1787 memory_region_init_ram(&qxl->rom_bar, "qxl.vrom", qxl->rom_size);
1788 vmstate_register_ram(&qxl->rom_bar, &qxl->pci.qdev);
a19cbfb3
GH
1789 init_qxl_rom(qxl);
1790 init_qxl_ram(qxl);
1791
c5705a77
AK
1792 memory_region_init_ram(&qxl->vram_bar, "qxl.vram", qxl->vram_size);
1793 vmstate_register_ram(&qxl->vram_bar, &qxl->pci.qdev);
6f2b175a
GH
1794 memory_region_init_alias(&qxl->vram32_bar, "qxl.vram32", &qxl->vram_bar,
1795 0, qxl->vram32_size);
a19cbfb3 1796
b1950430
AK
1797 memory_region_init_io(&qxl->io_bar, &qxl_io_ops, qxl,
1798 "qxl-ioports", io_size);
1799 if (qxl->id == 0) {
1800 vga_dirty_log_start(&qxl->vga);
1801 }
1802
1803
e824b2cc
AK
1804 pci_register_bar(&qxl->pci, QXL_IO_RANGE_INDEX,
1805 PCI_BASE_ADDRESS_SPACE_IO, &qxl->io_bar);
a19cbfb3 1806
e824b2cc
AK
1807 pci_register_bar(&qxl->pci, QXL_ROM_RANGE_INDEX,
1808 PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->rom_bar);
a19cbfb3 1809
e824b2cc
AK
1810 pci_register_bar(&qxl->pci, QXL_RAM_RANGE_INDEX,
1811 PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vga.vram);
a19cbfb3 1812
e824b2cc 1813 pci_register_bar(&qxl->pci, QXL_VRAM_RANGE_INDEX,
6f2b175a
GH
1814 PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vram32_bar);
1815
1816 if (qxl->vram32_size < qxl->vram_size) {
1817 /*
1818 * Make the 64bit vram bar show up only in case it is
1819 * configured to be larger than the 32bit vram bar.
1820 */
1821 pci_register_bar(&qxl->pci, QXL_VRAM64_RANGE_INDEX,
1822 PCI_BASE_ADDRESS_SPACE_MEMORY |
1823 PCI_BASE_ADDRESS_MEM_TYPE_64 |
1824 PCI_BASE_ADDRESS_MEM_PREFETCH,
1825 &qxl->vram_bar);
1826 }
1827
1828 /* print pci bar details */
1829 dprint(qxl, 1, "ram/%s: %d MB [region 0]\n",
1830 qxl->id == 0 ? "pri" : "sec",
1831 qxl->vga.vram_size / (1024*1024));
1832 dprint(qxl, 1, "vram/32: %d MB [region 1]\n",
1833 qxl->vram32_size / (1024*1024));
1834 dprint(qxl, 1, "vram/64: %d MB %s\n",
1835 qxl->vram_size / (1024*1024),
1836 qxl->vram32_size < qxl->vram_size ? "[region 4]" : "[unmapped]");
a19cbfb3
GH
1837
1838 qxl->ssd.qxl.base.sif = &qxl_interface.base;
1839 qxl->ssd.qxl.id = qxl->id;
1840 qemu_spice_add_interface(&qxl->ssd.qxl.base);
1841 qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl);
1842
1843 init_pipe_signaling(qxl);
1844 qxl_reset_state(qxl);
1845
81fb6f15
AL
1846 qxl->update_area_bh = qemu_bh_new(qxl_render_update_area_bh, qxl);
1847
a19cbfb3
GH
1848 return 0;
1849}
1850
1851static int qxl_init_primary(PCIDevice *dev)
1852{
1853 PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
1854 VGACommonState *vga = &qxl->vga;
f67ab77a 1855 PortioList *qxl_vga_port_list = g_new(PortioList, 1);
a19cbfb3
GH
1856
1857 qxl->id = 0;
a974192c 1858 qxl_init_ramsize(qxl, 32);
4a1e244e
GH
1859 vga->vram_size_mb = qxl->vga.vram_size >> 20;
1860 vga_common_init(vga);
0a039dc7 1861 vga_init(vga, pci_address_space(dev), pci_address_space_io(dev), false);
f67ab77a
GH
1862 portio_list_init(qxl_vga_port_list, qxl_vga_portio_list, vga, "vga");
1863 portio_list_add(qxl_vga_port_list, pci_address_space_io(dev), 0x3b0);
a19cbfb3
GH
1864
1865 vga->ds = graphic_console_init(qxl_hw_update, qxl_hw_invalidate,
1866 qxl_hw_screen_dump, qxl_hw_text_update, qxl);
a963f876 1867 qemu_spice_display_init_common(&qxl->ssd, vga->ds);
a19cbfb3
GH
1868
1869 qxl0 = qxl;
1870 register_displaychangelistener(vga->ds, &display_listener);
1871
a19cbfb3
GH
1872 return qxl_init_common(qxl);
1873}
1874
1875static int qxl_init_secondary(PCIDevice *dev)
1876{
1877 static int device_id = 1;
1878 PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
a19cbfb3
GH
1879
1880 qxl->id = device_id++;
a974192c 1881 qxl_init_ramsize(qxl, 16);
c5705a77
AK
1882 memory_region_init_ram(&qxl->vga.vram, "qxl.vgavram", qxl->vga.vram_size);
1883 vmstate_register_ram(&qxl->vga.vram, &qxl->pci.qdev);
b1950430 1884 qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram);
a19cbfb3 1885
a19cbfb3
GH
1886 return qxl_init_common(qxl);
1887}
1888
1889static void qxl_pre_save(void *opaque)
1890{
1891 PCIQXLDevice* d = opaque;
1892 uint8_t *ram_start = d->vga.vram_ptr;
1893
c480bb7d 1894 trace_qxl_pre_save(d->id);
a19cbfb3
GH
1895 if (d->last_release == NULL) {
1896 d->last_release_offset = 0;
1897 } else {
1898 d->last_release_offset = (uint8_t *)d->last_release - ram_start;
1899 }
1900 assert(d->last_release_offset < d->vga.vram_size);
1901}
1902
1903static int qxl_pre_load(void *opaque)
1904{
1905 PCIQXLDevice* d = opaque;
1906
c480bb7d 1907 trace_qxl_pre_load(d->id);
a19cbfb3
GH
1908 qxl_hard_reset(d, 1);
1909 qxl_exit_vga_mode(d);
a19cbfb3
GH
1910 return 0;
1911}
1912
54825d2e
AL
1913static void qxl_create_memslots(PCIQXLDevice *d)
1914{
1915 int i;
1916
1917 for (i = 0; i < NUM_MEMSLOTS; i++) {
1918 if (!d->guest_slots[i].active) {
1919 continue;
1920 }
54825d2e
AL
1921 qxl_add_memslot(d, i, 0, QXL_SYNC);
1922 }
1923}
1924
a19cbfb3
GH
1925static int qxl_post_load(void *opaque, int version)
1926{
1927 PCIQXLDevice* d = opaque;
1928 uint8_t *ram_start = d->vga.vram_ptr;
1929 QXLCommandExt *cmds;
54825d2e 1930 int in, out, newmode;
a19cbfb3 1931
a19cbfb3
GH
1932 assert(d->last_release_offset < d->vga.vram_size);
1933 if (d->last_release_offset == 0) {
1934 d->last_release = NULL;
1935 } else {
1936 d->last_release = (QXLReleaseInfo *)(ram_start + d->last_release_offset);
1937 }
1938
1939 d->modes = (QXLModes*)((uint8_t*)d->rom + d->rom->modes_offset);
1940
c480bb7d 1941 trace_qxl_post_load(d->id, qxl_mode_to_string(d->mode));
a19cbfb3
GH
1942 newmode = d->mode;
1943 d->mode = QXL_MODE_UNDEFINED;
54825d2e 1944
a19cbfb3
GH
1945 switch (newmode) {
1946 case QXL_MODE_UNDEFINED:
1947 break;
1948 case QXL_MODE_VGA:
54825d2e 1949 qxl_create_memslots(d);
a19cbfb3
GH
1950 qxl_enter_vga_mode(d);
1951 break;
1952 case QXL_MODE_NATIVE:
54825d2e 1953 qxl_create_memslots(d);
5ff4e36c 1954 qxl_create_guest_primary(d, 1, QXL_SYNC);
a19cbfb3
GH
1955
1956 /* replay surface-create and cursor-set commands */
7267c094 1957 cmds = g_malloc0(sizeof(QXLCommandExt) * (NUM_SURFACES + 1));
a19cbfb3
GH
1958 for (in = 0, out = 0; in < NUM_SURFACES; in++) {
1959 if (d->guest_surfaces.cmds[in] == 0) {
1960 continue;
1961 }
1962 cmds[out].cmd.data = d->guest_surfaces.cmds[in];
1963 cmds[out].cmd.type = QXL_CMD_SURFACE;
1964 cmds[out].group_id = MEMSLOT_GROUP_GUEST;
1965 out++;
1966 }
30f6da66
YH
1967 if (d->guest_cursor) {
1968 cmds[out].cmd.data = d->guest_cursor;
1969 cmds[out].cmd.type = QXL_CMD_CURSOR;
1970 cmds[out].group_id = MEMSLOT_GROUP_GUEST;
1971 out++;
1972 }
aee32bf3 1973 qxl_spice_loadvm_commands(d, cmds, out);
7267c094 1974 g_free(cmds);
a19cbfb3
GH
1975
1976 break;
1977 case QXL_MODE_COMPAT:
54825d2e
AL
1978 /* note: no need to call qxl_create_memslots, qxl_set_mode
1979 * creates the mem slot. */
a19cbfb3
GH
1980 qxl_set_mode(d, d->shadow_rom.mode, 1);
1981 break;
1982 }
a19cbfb3
GH
1983 return 0;
1984}
1985
b67737a6 1986#define QXL_SAVE_VERSION 21
a19cbfb3
GH
1987
1988static VMStateDescription qxl_memslot = {
1989 .name = "qxl-memslot",
1990 .version_id = QXL_SAVE_VERSION,
1991 .minimum_version_id = QXL_SAVE_VERSION,
1992 .fields = (VMStateField[]) {
1993 VMSTATE_UINT64(slot.mem_start, struct guest_slots),
1994 VMSTATE_UINT64(slot.mem_end, struct guest_slots),
1995 VMSTATE_UINT32(active, struct guest_slots),
1996 VMSTATE_END_OF_LIST()
1997 }
1998};
1999
2000static VMStateDescription qxl_surface = {
2001 .name = "qxl-surface",
2002 .version_id = QXL_SAVE_VERSION,
2003 .minimum_version_id = QXL_SAVE_VERSION,
2004 .fields = (VMStateField[]) {
2005 VMSTATE_UINT32(width, QXLSurfaceCreate),
2006 VMSTATE_UINT32(height, QXLSurfaceCreate),
2007 VMSTATE_INT32(stride, QXLSurfaceCreate),
2008 VMSTATE_UINT32(format, QXLSurfaceCreate),
2009 VMSTATE_UINT32(position, QXLSurfaceCreate),
2010 VMSTATE_UINT32(mouse_mode, QXLSurfaceCreate),
2011 VMSTATE_UINT32(flags, QXLSurfaceCreate),
2012 VMSTATE_UINT32(type, QXLSurfaceCreate),
2013 VMSTATE_UINT64(mem, QXLSurfaceCreate),
2014 VMSTATE_END_OF_LIST()
2015 }
2016};
2017
a19cbfb3
GH
2018static VMStateDescription qxl_vmstate = {
2019 .name = "qxl",
2020 .version_id = QXL_SAVE_VERSION,
2021 .minimum_version_id = QXL_SAVE_VERSION,
2022 .pre_save = qxl_pre_save,
2023 .pre_load = qxl_pre_load,
2024 .post_load = qxl_post_load,
2025 .fields = (VMStateField []) {
2026 VMSTATE_PCI_DEVICE(pci, PCIQXLDevice),
2027 VMSTATE_STRUCT(vga, PCIQXLDevice, 0, vmstate_vga_common, VGACommonState),
2028 VMSTATE_UINT32(shadow_rom.mode, PCIQXLDevice),
2029 VMSTATE_UINT32(num_free_res, PCIQXLDevice),
2030 VMSTATE_UINT32(last_release_offset, PCIQXLDevice),
2031 VMSTATE_UINT32(mode, PCIQXLDevice),
2032 VMSTATE_UINT32(ssd.unique, PCIQXLDevice),
b67737a6
GH
2033 VMSTATE_INT32_EQUAL(num_memslots, PCIQXLDevice),
2034 VMSTATE_STRUCT_ARRAY(guest_slots, PCIQXLDevice, NUM_MEMSLOTS, 0,
2035 qxl_memslot, struct guest_slots),
2036 VMSTATE_STRUCT(guest_primary.surface, PCIQXLDevice, 0,
2037 qxl_surface, QXLSurfaceCreate),
2038 VMSTATE_INT32_EQUAL(num_surfaces, PCIQXLDevice),
2039 VMSTATE_ARRAY(guest_surfaces.cmds, PCIQXLDevice, NUM_SURFACES, 0,
2040 vmstate_info_uint64, uint64_t),
2041 VMSTATE_UINT64(guest_cursor, PCIQXLDevice),
a19cbfb3
GH
2042 VMSTATE_END_OF_LIST()
2043 },
a19cbfb3
GH
2044};
2045
78e60ba5
GH
2046static Property qxl_properties[] = {
2047 DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size,
2048 64 * 1024 * 1024),
6f2b175a 2049 DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram32_size,
78e60ba5
GH
2050 64 * 1024 * 1024),
2051 DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision,
2052 QXL_DEFAULT_REVISION),
2053 DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0),
2054 DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0),
2055 DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0),
017438ee 2056 DEFINE_PROP_UINT32("ram_size_mb", PCIQXLDevice, ram_size_mb, -1),
79ce3567
AL
2057 DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, -1),
2058 DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1),
78e60ba5
GH
2059 DEFINE_PROP_END_OF_LIST(),
2060};
2061
40021f08
AL
2062static void qxl_primary_class_init(ObjectClass *klass, void *data)
2063{
39bffca2 2064 DeviceClass *dc = DEVICE_CLASS(klass);
40021f08
AL
2065 PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
2066
2067 k->no_hotplug = 1;
2068 k->init = qxl_init_primary;
2069 k->romfile = "vgabios-qxl.bin";
2070 k->vendor_id = REDHAT_PCI_VENDOR_ID;
2071 k->device_id = QXL_DEVICE_ID_STABLE;
2072 k->class_id = PCI_CLASS_DISPLAY_VGA;
39bffca2
AL
2073 dc->desc = "Spice QXL GPU (primary, vga compatible)";
2074 dc->reset = qxl_reset_handler;
2075 dc->vmsd = &qxl_vmstate;
2076 dc->props = qxl_properties;
40021f08
AL
2077}
2078
39bffca2
AL
2079static TypeInfo qxl_primary_info = {
2080 .name = "qxl-vga",
2081 .parent = TYPE_PCI_DEVICE,
2082 .instance_size = sizeof(PCIQXLDevice),
2083 .class_init = qxl_primary_class_init,
a19cbfb3
GH
2084};
2085
40021f08
AL
2086static void qxl_secondary_class_init(ObjectClass *klass, void *data)
2087{
39bffca2 2088 DeviceClass *dc = DEVICE_CLASS(klass);
40021f08
AL
2089 PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
2090
2091 k->init = qxl_init_secondary;
2092 k->vendor_id = REDHAT_PCI_VENDOR_ID;
2093 k->device_id = QXL_DEVICE_ID_STABLE;
2094 k->class_id = PCI_CLASS_DISPLAY_OTHER;
39bffca2
AL
2095 dc->desc = "Spice QXL GPU (secondary)";
2096 dc->reset = qxl_reset_handler;
2097 dc->vmsd = &qxl_vmstate;
2098 dc->props = qxl_properties;
40021f08
AL
2099}
2100
39bffca2
AL
2101static TypeInfo qxl_secondary_info = {
2102 .name = "qxl",
2103 .parent = TYPE_PCI_DEVICE,
2104 .instance_size = sizeof(PCIQXLDevice),
2105 .class_init = qxl_secondary_class_init,
a19cbfb3
GH
2106};
2107
83f7d43a 2108static void qxl_register_types(void)
a19cbfb3 2109{
39bffca2
AL
2110 type_register_static(&qxl_primary_info);
2111 type_register_static(&qxl_secondary_info);
a19cbfb3
GH
2112}
2113
83f7d43a 2114type_init(qxl_register_types)
This page took 0.546895 seconds and 4 git commands to generate.