1d514b0f3Smrg/* 2d514b0f3Smrg * Copyright 2011 Red Hat, Inc. 3d514b0f3Smrg * 4d514b0f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a 5d514b0f3Smrg * copy of this software and associated documentation files (the "Software"), 6d514b0f3Smrg * to deal in the Software without restriction, including without limitation 7d514b0f3Smrg * on the rights to use, copy, modify, merge, publish, distribute, sub 8d514b0f3Smrg * license, and/or sell copies of the Software, and to permit persons to whom 9d514b0f3Smrg * the Software is furnished to do so, subject to the following conditions: 10d514b0f3Smrg * 11d514b0f3Smrg * The above copyright notice and this permission notice (including the next 12d514b0f3Smrg * paragraph) shall be included in all copies or substantial portions of the 13d514b0f3Smrg * Software. 14d514b0f3Smrg * 15d514b0f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16d514b0f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17d514b0f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 18d514b0f3Smrg * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19d514b0f3Smrg * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20d514b0f3Smrg * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21d514b0f3Smrg */ 22d514b0f3Smrg#ifdef HAVE_CONFIG_H 23d514b0f3Smrg#include "config.h" 24d514b0f3Smrg#endif 25d514b0f3Smrg 26d514b0f3Smrg#include <spice.h> 27d514b0f3Smrg 28d514b0f3Smrg#include "qxl.h" 29d514b0f3Smrg#include "spiceqxl_display.h" 30d514b0f3Smrg 31d514b0f3Smrg#ifndef container_of 32d514b0f3Smrg#define container_of(ptr, type, member) ({ \ 33d514b0f3Smrg const typeof(((type *) 0)->member) *__mptr = (ptr); \ 34d514b0f3Smrg (type *) ((char *) __mptr - offsetof(type, member));}) 35d514b0f3Smrg#endif 36d514b0f3Smrg 37d514b0f3Smrg/* TODO: these is copied from qemu/hw/qxl.c . It shouldn't be there 38d514b0f3Smrg * either, these ugly undef just remove the definitions from spice-protocol/spice/ipc_ring.h 39d514b0f3Smrg * What should happen is using one definition, or a rename, and both in spice-protocol (because 40d514b0f3Smrg * all the others are there). 41d514b0f3Smrg * Practically speaking the only difference between the two is extra checking in this version, 42d514b0f3Smrg * and usage (this one takes an extra parameter, the previous is meant to be used by assignment) */ 43d514b0f3Smrg#undef SPICE_RING_PROD_ITEM 44d514b0f3Smrg#define SPICE_RING_PROD_ITEM(r, ret) { \ 45d514b0f3Smrg typeof(r) start = r; \ 46d514b0f3Smrg typeof(r) end = r + 1; \ 47d514b0f3Smrg uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r); \ 48d514b0f3Smrg typeof(&(r)->items[prod]) m_item = &(r)->items[prod]; \ 49d514b0f3Smrg if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \ 50d514b0f3Smrg abort(); \ 51d514b0f3Smrg } \ 52d514b0f3Smrg ret = &m_item->el; \ 53d514b0f3Smrg } 54d514b0f3Smrg 55d514b0f3Smrg#undef SPICE_RING_CONS_ITEM 56d514b0f3Smrg#define SPICE_RING_CONS_ITEM(r, ret) { \ 57d514b0f3Smrg typeof(r) start = r; \ 58d514b0f3Smrg typeof(r) end = r + 1; \ 59d514b0f3Smrg uint32_t cons = (r)->cons & SPICE_RING_INDEX_MASK(r); \ 60d514b0f3Smrg typeof(&(r)->items[cons]) m_item = &(r)->items[cons]; \ 61d514b0f3Smrg if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \ 62d514b0f3Smrg abort(); \ 63d514b0f3Smrg } \ 64d514b0f3Smrg ret = &m_item->el; \ 65d514b0f3Smrg } 66d514b0f3Smrg 67d514b0f3Smrg 68d514b0f3Smrg 69d514b0f3Smrg/* XSpice: 70d514b0f3Smrg * We only need a single static identity slot. 71d514b0f3Smrg * We actually need no slots, but less changes if we use one. 72d514b0f3Smrg * We currently add it during attache_worker - should not be called more 73d514b0f3Smrg * then once during lifetime (but we don't check) 74d514b0f3Smrg */ 75d514b0f3Smrgstatic QXLDevMemSlot slot = { 76d514b0f3Smrg.slot_group_id = MEMSLOT_GROUP, 77d514b0f3Smrg.slot_id = 0, 78d514b0f3Smrg.generation = 0, 79d514b0f3Smrg.virt_start = 0, 80d514b0f3Smrg.virt_end = ~0, 81d514b0f3Smrg.addr_delta = 0, 82d514b0f3Smrg.qxl_ram_size = ~0, 83d514b0f3Smrg}; 84d514b0f3Smrg 85d514b0f3Smrg// TODO - real dprint, this is just to get it compiling 86d514b0f3Smrg#define dprint(qxl, lvl, fmt, ...) printf(fmt, __VA_ARGS__) 87d514b0f3Smrg 88d514b0f3Smrgstatic void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker) 89d514b0f3Smrg{ 90d514b0f3Smrg static int count = 0; 91d514b0f3Smrg qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); 92d514b0f3Smrg 93d514b0f3Smrg if (++count > 1) { 94d514b0f3Smrg dprint(qxl, 0, "%s ignored\n", __FUNCTION__); 95d514b0f3Smrg return; 96d514b0f3Smrg } 97d514b0f3Smrg dprint(qxl, 1, "%s:\n", __FUNCTION__); 98d514b0f3Smrg spice_qxl_add_memslot(sin, &slot); 99d514b0f3Smrg qxl->worker = qxl_worker; 100d514b0f3Smrg} 101d514b0f3Smrg 102d514b0f3Smrgstatic void interface_set_compression_level(QXLInstance *sin, int level) 103d514b0f3Smrg{ 104d514b0f3Smrg qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); 105d514b0f3Smrg 106d514b0f3Smrg dprint(qxl, 1, "%s: %d\n", __FUNCTION__, level); 107d514b0f3Smrg qxl->shadow_rom.compression_level = level; 108d514b0f3Smrg qxl->rom->compression_level = level; 109d514b0f3Smrg} 110d514b0f3Smrg 111d514b0f3Smrgstatic void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time) 112d514b0f3Smrg{ 113d514b0f3Smrg qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); 114d514b0f3Smrg 115d514b0f3Smrg qxl->shadow_rom.mm_clock = mm_time; 116d514b0f3Smrg qxl->rom->mm_clock = mm_time; 117d514b0f3Smrg} 118d514b0f3Smrg 119d514b0f3Smrgstatic void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) 120d514b0f3Smrg{ 121d514b0f3Smrg qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); 122d514b0f3Smrg 123d514b0f3Smrg dprint(qxl, 1, "%s:\n", __FUNCTION__); 124d514b0f3Smrg info->memslot_gen_bits = MEMSLOT_GENERATION_BITS; 125d514b0f3Smrg info->memslot_id_bits = MEMSLOT_SLOT_BITS; 126d514b0f3Smrg info->num_memslots = NUM_MEMSLOTS; 127d514b0f3Smrg info->num_memslots_groups = NUM_MEMSLOTS_GROUPS; 128d514b0f3Smrg info->internal_groupslot_id = 0; 129d514b0f3Smrg info->qxl_ram_size = qxl->shadow_rom.num_pages << TARGET_PAGE_BITS; 130d514b0f3Smrg info->n_surfaces = NUM_SURFACES; 131d514b0f3Smrg} 132d514b0f3Smrg 133d514b0f3Smrgvoid qxl_send_events(qxl_screen_t *qxl, int events) 134d514b0f3Smrg{ 135d514b0f3Smrg#if 0 136d514b0f3Smrg ErrorF("qxl_send_events %d\n", events); 137d514b0f3Smrg qxl_garbage_collect(qxl); 138d514b0f3Smrg#endif 139d514b0f3Smrg /* we should trigger a garbage collection, but via a pipe. TODO */ 140d514b0f3Smrg} 141d514b0f3Smrg 142d514b0f3Smrg/* called from spice server thread context only */ 143d514b0f3Smrgstatic int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) 144d514b0f3Smrg{ 145d514b0f3Smrg qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); 146d514b0f3Smrg QXLRam *ram = get_ram_header(qxl); 147d514b0f3Smrg QXLCommandRing *ring; 148d514b0f3Smrg QXLCommand *cmd; 149d514b0f3Smrg int notify; 150d514b0f3Smrg 151d514b0f3Smrg dprint(qxl, 2, "%s: %s\n", __FUNCTION__, 152d514b0f3Smrg qxl->cmdflags ? "compat" : "native"); 153d514b0f3Smrg ring = &ram->cmd_ring; 154d514b0f3Smrg if (SPICE_RING_IS_EMPTY(ring)) { 155d514b0f3Smrg return FALSE; 156d514b0f3Smrg } 157d514b0f3Smrg SPICE_RING_CONS_ITEM(ring, cmd); 158d514b0f3Smrg ext->cmd = *cmd; 159d514b0f3Smrg ext->group_id = MEMSLOT_GROUP; 160d514b0f3Smrg ext->flags = qxl->cmdflags; 161d514b0f3Smrg SPICE_RING_POP(ring, notify); 162d514b0f3Smrg if (notify) { 163d514b0f3Smrg qxl_send_events(qxl, QXL_INTERRUPT_DISPLAY); 164d514b0f3Smrg } 165d514b0f3Smrg qxl->guest_primary.commands++; 166d514b0f3Smrg // TODO: re-enable, useful 167d514b0f3Smrg //qxl_track_command(qxl, ext); 168d514b0f3Smrg //qxl_log_command(qxl, "cmd", ext); 169d514b0f3Smrg return TRUE; 170d514b0f3Smrg} 171d514b0f3Smrg 172d514b0f3Smrg/* called from spice server thread context only */ 173d514b0f3Smrgstatic int interface_req_cmd_notification(QXLInstance *sin) 174d514b0f3Smrg{ 175d514b0f3Smrg qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); 176d514b0f3Smrg QXLRam *header = get_ram_header(qxl); 177d514b0f3Smrg int wait = 1; 178d514b0f3Smrg 179d514b0f3Smrg SPICE_RING_CONS_WAIT(&header->cmd_ring, wait); 180d514b0f3Smrg return wait; 181d514b0f3Smrg} 182d514b0f3Smrg 183d514b0f3Smrg/* called from spice server thread context only */ 184d514b0f3Smrgstatic inline void qxl_push_free_res(qxl_screen_t *qxl, int flush) 185d514b0f3Smrg{ 186d514b0f3Smrg QXLRam *header = get_ram_header(qxl); 187d514b0f3Smrg QXLReleaseRing *ring = &header->release_ring; 188d514b0f3Smrg uint64_t *item; 189d514b0f3Smrg int notify; 190d514b0f3Smrg 191d514b0f3Smrg#define QXL_FREE_BUNCH_SIZE 32 192d514b0f3Smrg 193d514b0f3Smrg if (ring->prod - ring->cons + 1 == ring->num_items) { 194d514b0f3Smrg /* ring full -- can't push */ 195d514b0f3Smrg return; 196d514b0f3Smrg } 197d514b0f3Smrg if (!flush && qxl->oom_running) { 198d514b0f3Smrg /* collect everything from oom handler before pushing */ 199d514b0f3Smrg return; 200d514b0f3Smrg } 201d514b0f3Smrg if (!flush && qxl->num_free_res < QXL_FREE_BUNCH_SIZE) { 202d514b0f3Smrg /* collect a bit more before pushing */ 203d514b0f3Smrg return; 204d514b0f3Smrg } 205d514b0f3Smrg 206d514b0f3Smrg SPICE_RING_PUSH(ring, notify); 207d514b0f3Smrg dprint(qxl, 2, "free: push %d items, notify %s, ring %d/%d [%d,%d]\n", 208d514b0f3Smrg qxl->num_free_res, notify ? "yes" : "no", 209d514b0f3Smrg ring->prod - ring->cons, ring->num_items, 210d514b0f3Smrg ring->prod, ring->cons); 211d514b0f3Smrg if (notify) { 212d514b0f3Smrg qxl_send_events(qxl, QXL_INTERRUPT_DISPLAY); 213d514b0f3Smrg } 214d514b0f3Smrg SPICE_RING_PROD_ITEM(ring, item); 215d514b0f3Smrg *item = 0; 216d514b0f3Smrg qxl->num_free_res = 0; 217d514b0f3Smrg qxl->last_release = NULL; 218d514b0f3Smrg} 219d514b0f3Smrg 220d514b0f3Smrg/* called from spice server thread context only */ 221d514b0f3Smrgstatic void interface_release_resource(QXLInstance *sin, 222d514b0f3Smrg struct QXLReleaseInfoExt ext) 223d514b0f3Smrg{ 224d514b0f3Smrg qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); 225d514b0f3Smrg QXLRam *ram = get_ram_header(qxl); 226d514b0f3Smrg QXLReleaseRing *ring; 227d514b0f3Smrg uint64_t *item, id; 228d514b0f3Smrg 229d514b0f3Smrg /* 230d514b0f3Smrg * ext->info points into guest-visible memory 231d514b0f3Smrg * pci bar 0, $command.release_info 232d514b0f3Smrg */ 233d514b0f3Smrg ring = &ram->release_ring; 234d514b0f3Smrg SPICE_RING_PROD_ITEM(ring, item); 235d514b0f3Smrg if (*item == 0) { 236d514b0f3Smrg /* stick head into the ring */ 237d514b0f3Smrg id = ext.info->id; 238d514b0f3Smrg ext.info->next = 0; 239d514b0f3Smrg *item = id; 240d514b0f3Smrg } else { 241d514b0f3Smrg /* append item to the list */ 242d514b0f3Smrg qxl->last_release->next = ext.info->id; 243d514b0f3Smrg ext.info->next = 0; 244d514b0f3Smrg } 245d514b0f3Smrg qxl->last_release = ext.info; 246d514b0f3Smrg qxl->num_free_res++; 247d514b0f3Smrg dprint(qxl, 3, "%4d\r", qxl->num_free_res); 248d514b0f3Smrg qxl_push_free_res(qxl, 0); 249d514b0f3Smrg} 250d514b0f3Smrg 251d514b0f3Smrg/* called from spice server thread context only */ 252d514b0f3Smrgstatic int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext) 253d514b0f3Smrg{ 254d514b0f3Smrg qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); 255d514b0f3Smrg QXLCursorRing *ring; 256d514b0f3Smrg QXLCommand *cmd; 257d514b0f3Smrg QXLRam *ram = get_ram_header(qxl); 258d514b0f3Smrg int notify; 259d514b0f3Smrg 260d514b0f3Smrg ring = &ram->cursor_ring; 261d514b0f3Smrg if (SPICE_RING_IS_EMPTY(ring)) { 262d514b0f3Smrg return FALSE; 263d514b0f3Smrg } 264d514b0f3Smrg SPICE_RING_CONS_ITEM(ring, cmd); 265d514b0f3Smrg ext->cmd = *cmd; 266d514b0f3Smrg ext->group_id = MEMSLOT_GROUP; 267d514b0f3Smrg ext->flags = qxl->cmdflags; 268d514b0f3Smrg SPICE_RING_POP(ring, notify); 269d514b0f3Smrg if (notify) { 270d514b0f3Smrg qxl_send_events(qxl, QXL_INTERRUPT_CURSOR); 271d514b0f3Smrg } 272d514b0f3Smrg qxl->guest_primary.commands++; 273d514b0f3Smrg //qxl_track_command(qxl, ext); // TODO - copy me 274d514b0f3Smrg //qxl_log_command(qxl, "csr", ext); // TODO - copy me 275d514b0f3Smrg return TRUE; 276d514b0f3Smrg} 277d514b0f3Smrg 278d514b0f3Smrg/* called from spice server thread context only */ 279d514b0f3Smrgstatic int interface_req_cursor_notification(QXLInstance *sin) 280d514b0f3Smrg{ 281d514b0f3Smrg qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); 282d514b0f3Smrg QXLRam *ram = get_ram_header(qxl); 283d514b0f3Smrg int wait = 1; 284d514b0f3Smrg 285d514b0f3Smrg SPICE_RING_CONS_WAIT(&ram->cursor_ring, wait); 286d514b0f3Smrg return wait; 287d514b0f3Smrg} 288d514b0f3Smrg 289d514b0f3Smrg/* called from spice server thread context */ 290d514b0f3Smrgstatic void __attribute__ ((__noreturn__)) 291d514b0f3Smrg interface_notify_update(QXLInstance *sin, uint32_t update_id) 292d514b0f3Smrg{ 293d514b0f3Smrg fprintf(stderr, "%s: abort()\n", __FUNCTION__); 294d514b0f3Smrg abort(); 295d514b0f3Smrg} 296d514b0f3Smrg 297d514b0f3Smrg/* called from spice server thread context only */ 298d514b0f3Smrgstatic int interface_flush_resources(QXLInstance *sin) 299d514b0f3Smrg{ 300d514b0f3Smrg qxl_screen_t *qxl = container_of(sin, qxl_screen_t, display_sin); 301d514b0f3Smrg int ret; 302d514b0f3Smrg 303d514b0f3Smrg dprint(qxl, 1, "free: guest flush (have %d)\n", qxl->num_free_res); 304d514b0f3Smrg ret = qxl->num_free_res; 305d514b0f3Smrg if (ret) { 306d514b0f3Smrg qxl_push_free_res(qxl, 1); 307d514b0f3Smrg } 308d514b0f3Smrg return ret; 309d514b0f3Smrg} 310d514b0f3Smrg 311d514b0f3Smrgstatic void interface_async_complete(QXLInstance *sin, uint64_t cookie_token) 312d514b0f3Smrg{ 313d514b0f3Smrg} 314d514b0f3Smrg 315d514b0f3Smrgstatic const QXLInterface qxl_interface = { 316d514b0f3Smrg .base.type = SPICE_INTERFACE_QXL, 317d514b0f3Smrg .base.description = "qxl gpu", 318d514b0f3Smrg .base.major_version = SPICE_INTERFACE_QXL_MAJOR, 319d514b0f3Smrg .base.minor_version = SPICE_INTERFACE_QXL_MINOR, 320d514b0f3Smrg 321d514b0f3Smrg .attache_worker = interface_attach_worker, 322d514b0f3Smrg .set_compression_level = interface_set_compression_level, 323d514b0f3Smrg .set_mm_time = interface_set_mm_time, 324d514b0f3Smrg .get_init_info = interface_get_init_info, 325d514b0f3Smrg 326d514b0f3Smrg /* the callbacks below are called from spice server thread context */ 327d514b0f3Smrg .get_command = interface_get_command, 328d514b0f3Smrg .req_cmd_notification = interface_req_cmd_notification, 329d514b0f3Smrg .release_resource = interface_release_resource, 330d514b0f3Smrg .get_cursor_command = interface_get_cursor_command, 331d514b0f3Smrg .req_cursor_notification = interface_req_cursor_notification, 332d514b0f3Smrg .notify_update = interface_notify_update, 333d514b0f3Smrg .flush_resources = interface_flush_resources, 334d514b0f3Smrg .async_complete = interface_async_complete, 335d514b0f3Smrg}; 336d514b0f3Smrg 337d514b0f3Smrgvoid qxl_add_spice_display_interface(qxl_screen_t *qxl) 338d514b0f3Smrg{ 339d514b0f3Smrg /* use this function to initialize the parts of qxl_screen_t 340d514b0f3Smrg * that were added directly from qemu/hw/qxl.c */ 341d514b0f3Smrg qxl->cmdflags = 0; 342d514b0f3Smrg qxl->oom_running = 0; 343d514b0f3Smrg qxl->num_free_res = 0; 344d514b0f3Smrg 345d514b0f3Smrg qxl->display_sin.base.sif = &qxl_interface.base; 346d514b0f3Smrg qxl->display_sin.id = 0; 347d514b0f3Smrg qxl->display_sin.st = (struct QXLState*)qxl; 348d514b0f3Smrg spice_server_add_interface(qxl->spice_server, &qxl->display_sin.base); 349d514b0f3Smrg} 350d514b0f3Smrg 351d514b0f3Smrgvoid spiceqxl_display_monitors_config(qxl_screen_t *qxl) 352d514b0f3Smrg{ 353d514b0f3Smrg spice_qxl_monitors_config_async(&qxl->display_sin, physical_address(qxl, qxl->monitors_config, 0), 354d514b0f3Smrg MEMSLOT_GROUP, 0); 355d514b0f3Smrg} 356