Home | History | Annotate | Line # | Download | only in src
      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  */
     75 static 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 
     88 static 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 
    102 static 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 
    111 static 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 
    119 static 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 
    133 void 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 */
    143 static 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 */
    173 static 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 */
    184 static 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 */
    221 static 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 */
    252 static 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 */
    279 static 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 */
    290 static 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 */
    298 static 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 
    311 static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token)
    312 {
    313 }
    314 
    315 static 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 
    337 void 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 
    351 void 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