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