cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

qxl-render.c (10823B)


      1/*
      2 * qxl local rendering (aka display on sdl/vnc)
      3 *
      4 * Copyright (C) 2010 Red Hat, Inc.
      5 *
      6 * maintained by Gerd Hoffmann <kraxel@redhat.com>
      7 *
      8 * This program is free software; you can redistribute it and/or
      9 * modify it under the terms of the GNU General Public License as
     10 * published by the Free Software Foundation; either version 2 or
     11 * (at your option) version 3 of the License.
     12 *
     13 * This program is distributed in the hope that it will be useful,
     14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16 * GNU General Public License for more details.
     17 *
     18 * You should have received a copy of the GNU General Public License
     19 * along with this program; if not, see <http://www.gnu.org/licenses/>.
     20 */
     21
     22#include "qemu/osdep.h"
     23#include "qxl.h"
     24#include "sysemu/runstate.h"
     25#include "trace.h"
     26
     27static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect)
     28{
     29    DisplaySurface *surface = qemu_console_surface(qxl->vga.con);
     30    uint8_t *dst = surface_data(surface);
     31    uint8_t *src;
     32    int len, i;
     33
     34    if (is_buffer_shared(surface)) {
     35        return;
     36    }
     37    trace_qxl_render_blit(qxl->guest_primary.qxl_stride,
     38            rect->left, rect->right, rect->top, rect->bottom);
     39    src = qxl->guest_primary.data;
     40    if (qxl->guest_primary.qxl_stride < 0) {
     41        /* qxl surface is upside down, walk src scanlines
     42         * in reverse order to flip it */
     43        src += (qxl->guest_primary.surface.height - rect->top - 1) *
     44            qxl->guest_primary.abs_stride;
     45    } else {
     46        src += rect->top * qxl->guest_primary.abs_stride;
     47    }
     48    dst += rect->top  * qxl->guest_primary.abs_stride;
     49    src += rect->left * qxl->guest_primary.bytes_pp;
     50    dst += rect->left * qxl->guest_primary.bytes_pp;
     51    len  = (rect->right - rect->left) * qxl->guest_primary.bytes_pp;
     52
     53    for (i = rect->top; i < rect->bottom; i++) {
     54        memcpy(dst, src, len);
     55        dst += qxl->guest_primary.abs_stride;
     56        src += qxl->guest_primary.qxl_stride;
     57    }
     58}
     59
     60void qxl_render_resize(PCIQXLDevice *qxl)
     61{
     62    QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
     63
     64    qxl->guest_primary.qxl_stride = sc->stride;
     65    qxl->guest_primary.abs_stride = abs(sc->stride);
     66    qxl->guest_primary.resized++;
     67    switch (sc->format) {
     68    case SPICE_SURFACE_FMT_16_555:
     69        qxl->guest_primary.bytes_pp = 2;
     70        qxl->guest_primary.bits_pp = 15;
     71        break;
     72    case SPICE_SURFACE_FMT_16_565:
     73        qxl->guest_primary.bytes_pp = 2;
     74        qxl->guest_primary.bits_pp = 16;
     75        break;
     76    case SPICE_SURFACE_FMT_32_xRGB:
     77    case SPICE_SURFACE_FMT_32_ARGB:
     78        qxl->guest_primary.bytes_pp = 4;
     79        qxl->guest_primary.bits_pp = 32;
     80        break;
     81    default:
     82        fprintf(stderr, "%s: unhandled format: %x\n", __func__,
     83                qxl->guest_primary.surface.format);
     84        qxl->guest_primary.bytes_pp = 4;
     85        qxl->guest_primary.bits_pp = 32;
     86        break;
     87    }
     88}
     89
     90static void qxl_set_rect_to_surface(PCIQXLDevice *qxl, QXLRect *area)
     91{
     92    area->left   = 0;
     93    area->right  = qxl->guest_primary.surface.width;
     94    area->top    = 0;
     95    area->bottom = qxl->guest_primary.surface.height;
     96}
     97
     98static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
     99{
    100    VGACommonState *vga = &qxl->vga;
    101    DisplaySurface *surface;
    102    int width = qxl->guest_head0_width ?: qxl->guest_primary.surface.width;
    103    int height = qxl->guest_head0_height ?: qxl->guest_primary.surface.height;
    104    int i;
    105
    106    if (qxl->guest_primary.resized) {
    107        qxl->guest_primary.resized = 0;
    108        qxl->guest_primary.data = qxl_phys2virt(qxl,
    109                                                qxl->guest_primary.surface.mem,
    110                                                MEMSLOT_GROUP_GUEST);
    111        if (!qxl->guest_primary.data) {
    112            goto end;
    113        }
    114        qxl_set_rect_to_surface(qxl, &qxl->dirty[0]);
    115        qxl->num_dirty_rects = 1;
    116        trace_qxl_render_guest_primary_resized(
    117               width,
    118               height,
    119               qxl->guest_primary.qxl_stride,
    120               qxl->guest_primary.bytes_pp,
    121               qxl->guest_primary.bits_pp);
    122        if (qxl->guest_primary.qxl_stride > 0) {
    123            pixman_format_code_t format =
    124                qemu_default_pixman_format(qxl->guest_primary.bits_pp, true);
    125            surface = qemu_create_displaysurface_from
    126                (width,
    127                 height,
    128                 format,
    129                 qxl->guest_primary.abs_stride,
    130                 qxl->guest_primary.data);
    131        } else {
    132            surface = qemu_create_displaysurface
    133                (width,
    134                 height);
    135        }
    136        dpy_gfx_replace_surface(vga->con, surface);
    137    }
    138
    139    if (!qxl->guest_primary.data) {
    140        goto end;
    141    }
    142    for (i = 0; i < qxl->num_dirty_rects; i++) {
    143        if (qemu_spice_rect_is_empty(qxl->dirty+i)) {
    144            break;
    145        }
    146        if (qxl->dirty[i].left < 0 ||
    147            qxl->dirty[i].top < 0 ||
    148            qxl->dirty[i].left > qxl->dirty[i].right ||
    149            qxl->dirty[i].top > qxl->dirty[i].bottom ||
    150            qxl->dirty[i].right > width ||
    151            qxl->dirty[i].bottom > height) {
    152            continue;
    153        }
    154        qxl_blit(qxl, qxl->dirty+i);
    155        dpy_gfx_update(vga->con,
    156                       qxl->dirty[i].left, qxl->dirty[i].top,
    157                       qxl->dirty[i].right - qxl->dirty[i].left,
    158                       qxl->dirty[i].bottom - qxl->dirty[i].top);
    159    }
    160    qxl->num_dirty_rects = 0;
    161
    162end:
    163    if (qxl->render_update_cookie_num == 0) {
    164        graphic_hw_update_done(qxl->ssd.dcl.con);
    165    }
    166}
    167
    168/*
    169 * use ssd.lock to protect render_update_cookie_num.
    170 * qxl_render_update is called by io thread or vcpu thread, and the completion
    171 * callbacks are called by spice_server thread, deferring to bh called from the
    172 * io thread.
    173 */
    174void qxl_render_update(PCIQXLDevice *qxl)
    175{
    176    QXLCookie *cookie;
    177
    178    qemu_mutex_lock(&qxl->ssd.lock);
    179
    180    if (!runstate_is_running() || !qxl->guest_primary.commands ||
    181        qxl->mode == QXL_MODE_UNDEFINED) {
    182        qxl_render_update_area_unlocked(qxl);
    183        qemu_mutex_unlock(&qxl->ssd.lock);
    184        graphic_hw_update_done(qxl->ssd.dcl.con);
    185        return;
    186    }
    187
    188    qxl->guest_primary.commands = 0;
    189    qxl->render_update_cookie_num++;
    190    qemu_mutex_unlock(&qxl->ssd.lock);
    191    cookie = qxl_cookie_new(QXL_COOKIE_TYPE_RENDER_UPDATE_AREA,
    192                            0);
    193    qxl_set_rect_to_surface(qxl, &cookie->u.render.area);
    194    qxl_spice_update_area(qxl, 0, &cookie->u.render.area, NULL,
    195                          0, 1 /* clear_dirty_region */, QXL_ASYNC, cookie);
    196}
    197
    198void qxl_render_update_area_bh(void *opaque)
    199{
    200    PCIQXLDevice *qxl = opaque;
    201
    202    qemu_mutex_lock(&qxl->ssd.lock);
    203    qxl_render_update_area_unlocked(qxl);
    204    qemu_mutex_unlock(&qxl->ssd.lock);
    205}
    206
    207void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie)
    208{
    209    qemu_mutex_lock(&qxl->ssd.lock);
    210    trace_qxl_render_update_area_done(cookie);
    211    qemu_bh_schedule(qxl->update_area_bh);
    212    qxl->render_update_cookie_num--;
    213    qemu_mutex_unlock(&qxl->ssd.lock);
    214    g_free(cookie);
    215}
    216
    217static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl,
    218                              QXLDataChunk *chunk, uint32_t group_id)
    219{
    220    uint32_t max_chunks = 32;
    221    size_t offset = 0;
    222    size_t bytes;
    223
    224    for (;;) {
    225        bytes = MIN(size - offset, chunk->data_size);
    226        memcpy(dest + offset, chunk->data, bytes);
    227        offset += bytes;
    228        if (offset == size) {
    229            return;
    230        }
    231        chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id);
    232        if (!chunk) {
    233            return;
    234        }
    235        max_chunks--;
    236        if (max_chunks == 0) {
    237            return;
    238        }
    239    }
    240}
    241
    242static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor,
    243                              uint32_t group_id)
    244{
    245    QEMUCursor *c;
    246    uint8_t *and_mask, *xor_mask;
    247    size_t size;
    248
    249    c = cursor_alloc(cursor->header.width, cursor->header.height);
    250    c->hot_x = cursor->header.hot_spot_x;
    251    c->hot_y = cursor->header.hot_spot_y;
    252    switch (cursor->header.type) {
    253    case SPICE_CURSOR_TYPE_MONO:
    254        /* Assume that the full cursor is available in a single chunk. */
    255        size = 2 * cursor_get_mono_bpl(c) * c->height;
    256        if (size != cursor->data_size) {
    257            fprintf(stderr, "%s: bad monochrome cursor %ux%u with size %u\n",
    258                    __func__, c->width, c->height, cursor->data_size);
    259            goto fail;
    260        }
    261        and_mask = cursor->chunk.data;
    262        xor_mask = and_mask + cursor_get_mono_bpl(c) * c->height;
    263        cursor_set_mono(c, 0xffffff, 0x000000, xor_mask, 1, and_mask);
    264        if (qxl->debug > 2) {
    265            cursor_print_ascii_art(c, "qxl/mono");
    266        }
    267        break;
    268    case SPICE_CURSOR_TYPE_ALPHA:
    269        size = sizeof(uint32_t) * cursor->header.width * cursor->header.height;
    270        qxl_unpack_chunks(c->data, size, qxl, &cursor->chunk, group_id);
    271        if (qxl->debug > 2) {
    272            cursor_print_ascii_art(c, "qxl/alpha");
    273        }
    274        break;
    275    default:
    276        fprintf(stderr, "%s: not implemented: type %d\n",
    277                __func__, cursor->header.type);
    278        goto fail;
    279    }
    280    return c;
    281
    282fail:
    283    cursor_put(c);
    284    return NULL;
    285}
    286
    287
    288/* called from spice server thread context only */
    289int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext)
    290{
    291    QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
    292    QXLCursor *cursor;
    293    QEMUCursor *c;
    294
    295    if (!cmd) {
    296        return 1;
    297    }
    298
    299    if (!dpy_cursor_define_supported(qxl->vga.con)) {
    300        return 0;
    301    }
    302
    303    if (qxl->debug > 1 && cmd->type != QXL_CURSOR_MOVE) {
    304        fprintf(stderr, "%s", __func__);
    305        qxl_log_cmd_cursor(qxl, cmd, ext->group_id);
    306        fprintf(stderr, "\n");
    307    }
    308    switch (cmd->type) {
    309    case QXL_CURSOR_SET:
    310        cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id);
    311        if (!cursor) {
    312            return 1;
    313        }
    314        c = qxl_cursor(qxl, cursor, ext->group_id);
    315        if (c == NULL) {
    316            c = cursor_builtin_left_ptr();
    317        }
    318        qemu_mutex_lock(&qxl->ssd.lock);
    319        if (qxl->ssd.cursor) {
    320            cursor_put(qxl->ssd.cursor);
    321        }
    322        qxl->ssd.cursor = c;
    323        qxl->ssd.mouse_x = cmd->u.set.position.x;
    324        qxl->ssd.mouse_y = cmd->u.set.position.y;
    325        qemu_mutex_unlock(&qxl->ssd.lock);
    326        qemu_bh_schedule(qxl->ssd.cursor_bh);
    327        break;
    328    case QXL_CURSOR_MOVE:
    329        qemu_mutex_lock(&qxl->ssd.lock);
    330        qxl->ssd.mouse_x = cmd->u.position.x;
    331        qxl->ssd.mouse_y = cmd->u.position.y;
    332        qemu_mutex_unlock(&qxl->ssd.lock);
    333        qemu_bh_schedule(qxl->ssd.cursor_bh);
    334        break;
    335    }
    336    return 0;
    337}