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