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

display.c (16013B)


      1/*
      2 * display support for mdev based vgpu devices
      3 *
      4 * Copyright Red Hat, Inc. 2017
      5 *
      6 * Authors:
      7 *    Gerd Hoffmann
      8 *
      9 * This work is licensed under the terms of the GNU GPL, version 2.  See
     10 * the COPYING file in the top-level directory.
     11 */
     12
     13#include "qemu/osdep.h"
     14#include <linux/vfio.h>
     15#include <sys/ioctl.h>
     16
     17#include "hw/display/edid.h"
     18#include "ui/console.h"
     19#include "qapi/error.h"
     20#include "pci.h"
     21#include "trace.h"
     22
     23#ifndef DRM_PLANE_TYPE_PRIMARY
     24# define DRM_PLANE_TYPE_PRIMARY 1
     25# define DRM_PLANE_TYPE_CURSOR  2
     26#endif
     27
     28#define pread_field(_fd, _reg, _ptr, _fld)                              \
     29    (sizeof(_ptr->_fld) !=                                              \
     30     pread(_fd, &(_ptr->_fld), sizeof(_ptr->_fld),                      \
     31           _reg->offset + offsetof(typeof(*_ptr), _fld)))
     32
     33#define pwrite_field(_fd, _reg, _ptr, _fld)                             \
     34    (sizeof(_ptr->_fld) !=                                              \
     35     pwrite(_fd, &(_ptr->_fld), sizeof(_ptr->_fld),                     \
     36            _reg->offset + offsetof(typeof(*_ptr), _fld)))
     37
     38
     39static void vfio_display_edid_link_up(void *opaque)
     40{
     41    VFIOPCIDevice *vdev = opaque;
     42    VFIODisplay *dpy = vdev->dpy;
     43    int fd = vdev->vbasedev.fd;
     44
     45    dpy->edid_regs->link_state = VFIO_DEVICE_GFX_LINK_STATE_UP;
     46    if (pwrite_field(fd, dpy->edid_info, dpy->edid_regs, link_state)) {
     47        goto err;
     48    }
     49    trace_vfio_display_edid_link_up();
     50    return;
     51
     52err:
     53    trace_vfio_display_edid_write_error();
     54}
     55
     56static void vfio_display_edid_update(VFIOPCIDevice *vdev, bool enabled,
     57                                     int prefx, int prefy)
     58{
     59    VFIODisplay *dpy = vdev->dpy;
     60    int fd = vdev->vbasedev.fd;
     61    qemu_edid_info edid = {
     62        .maxx  = dpy->edid_regs->max_xres,
     63        .maxy  = dpy->edid_regs->max_yres,
     64        .prefx = prefx ?: vdev->display_xres,
     65        .prefy = prefy ?: vdev->display_yres,
     66    };
     67
     68    timer_del(dpy->edid_link_timer);
     69    dpy->edid_regs->link_state = VFIO_DEVICE_GFX_LINK_STATE_DOWN;
     70    if (pwrite_field(fd, dpy->edid_info, dpy->edid_regs, link_state)) {
     71        goto err;
     72    }
     73    trace_vfio_display_edid_link_down();
     74
     75    if (!enabled) {
     76        return;
     77    }
     78
     79    if (edid.maxx && edid.prefx > edid.maxx) {
     80        edid.prefx = edid.maxx;
     81    }
     82    if (edid.maxy && edid.prefy > edid.maxy) {
     83        edid.prefy = edid.maxy;
     84    }
     85    qemu_edid_generate(dpy->edid_blob,
     86                       dpy->edid_regs->edid_max_size,
     87                       &edid);
     88    trace_vfio_display_edid_update(edid.prefx, edid.prefy);
     89
     90    dpy->edid_regs->edid_size = qemu_edid_size(dpy->edid_blob);
     91    if (pwrite_field(fd, dpy->edid_info, dpy->edid_regs, edid_size)) {
     92        goto err;
     93    }
     94    if (pwrite(fd, dpy->edid_blob, dpy->edid_regs->edid_size,
     95               dpy->edid_info->offset + dpy->edid_regs->edid_offset)
     96        != dpy->edid_regs->edid_size) {
     97        goto err;
     98    }
     99
    100    timer_mod(dpy->edid_link_timer,
    101              qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 100);
    102    return;
    103
    104err:
    105    trace_vfio_display_edid_write_error();
    106    return;
    107}
    108
    109static int vfio_display_edid_ui_info(void *opaque, uint32_t idx,
    110                                     QemuUIInfo *info)
    111{
    112    VFIOPCIDevice *vdev = opaque;
    113    VFIODisplay *dpy = vdev->dpy;
    114
    115    if (!dpy->edid_regs) {
    116        return 0;
    117    }
    118
    119    if (info->width && info->height) {
    120        vfio_display_edid_update(vdev, true, info->width, info->height);
    121    } else {
    122        vfio_display_edid_update(vdev, false, 0, 0);
    123    }
    124
    125    return 0;
    126}
    127
    128static void vfio_display_edid_init(VFIOPCIDevice *vdev)
    129{
    130    VFIODisplay *dpy = vdev->dpy;
    131    int fd = vdev->vbasedev.fd;
    132    int ret;
    133
    134    ret = vfio_get_dev_region_info(&vdev->vbasedev,
    135                                   VFIO_REGION_TYPE_GFX,
    136                                   VFIO_REGION_SUBTYPE_GFX_EDID,
    137                                   &dpy->edid_info);
    138    if (ret) {
    139        return;
    140    }
    141
    142    trace_vfio_display_edid_available();
    143    dpy->edid_regs = g_new0(struct vfio_region_gfx_edid, 1);
    144    if (pread_field(fd, dpy->edid_info, dpy->edid_regs, edid_offset)) {
    145        goto err;
    146    }
    147    if (pread_field(fd, dpy->edid_info, dpy->edid_regs, edid_max_size)) {
    148        goto err;
    149    }
    150    if (pread_field(fd, dpy->edid_info, dpy->edid_regs, max_xres)) {
    151        goto err;
    152    }
    153    if (pread_field(fd, dpy->edid_info, dpy->edid_regs, max_yres)) {
    154        goto err;
    155    }
    156
    157    dpy->edid_blob = g_malloc0(dpy->edid_regs->edid_max_size);
    158
    159    /* if xres + yres properties are unset use the maximum resolution */
    160    if (!vdev->display_xres) {
    161        vdev->display_xres = dpy->edid_regs->max_xres;
    162    }
    163    if (!vdev->display_yres) {
    164        vdev->display_yres = dpy->edid_regs->max_yres;
    165    }
    166
    167    dpy->edid_link_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
    168                                        vfio_display_edid_link_up, vdev);
    169
    170    vfio_display_edid_update(vdev, true, 0, 0);
    171    return;
    172
    173err:
    174    trace_vfio_display_edid_write_error();
    175    g_free(dpy->edid_regs);
    176    dpy->edid_regs = NULL;
    177    return;
    178}
    179
    180static void vfio_display_edid_exit(VFIODisplay *dpy)
    181{
    182    if (!dpy->edid_regs) {
    183        return;
    184    }
    185
    186    g_free(dpy->edid_regs);
    187    g_free(dpy->edid_blob);
    188    timer_free(dpy->edid_link_timer);
    189}
    190
    191static void vfio_display_update_cursor(VFIODMABuf *dmabuf,
    192                                       struct vfio_device_gfx_plane_info *plane)
    193{
    194    if (dmabuf->pos_x != plane->x_pos || dmabuf->pos_y != plane->y_pos) {
    195        dmabuf->pos_x      = plane->x_pos;
    196        dmabuf->pos_y      = plane->y_pos;
    197        dmabuf->pos_updates++;
    198    }
    199    if (dmabuf->hot_x != plane->x_hot || dmabuf->hot_y != plane->y_hot) {
    200        dmabuf->hot_x      = plane->x_hot;
    201        dmabuf->hot_y      = plane->y_hot;
    202        dmabuf->hot_updates++;
    203    }
    204}
    205
    206static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev,
    207                                           uint32_t plane_type)
    208{
    209    VFIODisplay *dpy = vdev->dpy;
    210    struct vfio_device_gfx_plane_info plane;
    211    VFIODMABuf *dmabuf;
    212    int fd, ret;
    213
    214    memset(&plane, 0, sizeof(plane));
    215    plane.argsz = sizeof(plane);
    216    plane.flags = VFIO_GFX_PLANE_TYPE_DMABUF;
    217    plane.drm_plane_type = plane_type;
    218    ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane);
    219    if (ret < 0) {
    220        return NULL;
    221    }
    222    if (!plane.drm_format || !plane.size) {
    223        return NULL;
    224    }
    225
    226    QTAILQ_FOREACH(dmabuf, &dpy->dmabuf.bufs, next) {
    227        if (dmabuf->dmabuf_id == plane.dmabuf_id) {
    228            /* found in list, move to head, return it */
    229            QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next);
    230            QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next);
    231            if (plane_type == DRM_PLANE_TYPE_CURSOR) {
    232                vfio_display_update_cursor(dmabuf, &plane);
    233            }
    234            return dmabuf;
    235        }
    236    }
    237
    238    fd = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_GFX_DMABUF, &plane.dmabuf_id);
    239    if (fd < 0) {
    240        return NULL;
    241    }
    242
    243    dmabuf = g_new0(VFIODMABuf, 1);
    244    dmabuf->dmabuf_id  = plane.dmabuf_id;
    245    dmabuf->buf.width  = plane.width;
    246    dmabuf->buf.height = plane.height;
    247    dmabuf->buf.stride = plane.stride;
    248    dmabuf->buf.fourcc = plane.drm_format;
    249    dmabuf->buf.modifier = plane.drm_format_mod;
    250    dmabuf->buf.fd     = fd;
    251    if (plane_type == DRM_PLANE_TYPE_CURSOR) {
    252        vfio_display_update_cursor(dmabuf, &plane);
    253    }
    254
    255    QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next);
    256    return dmabuf;
    257}
    258
    259static void vfio_display_free_one_dmabuf(VFIODisplay *dpy, VFIODMABuf *dmabuf)
    260{
    261    QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next);
    262    dpy_gl_release_dmabuf(dpy->con, &dmabuf->buf);
    263    close(dmabuf->buf.fd);
    264    g_free(dmabuf);
    265}
    266
    267static void vfio_display_free_dmabufs(VFIOPCIDevice *vdev)
    268{
    269    VFIODisplay *dpy = vdev->dpy;
    270    VFIODMABuf *dmabuf, *tmp;
    271    uint32_t keep = 5;
    272
    273    QTAILQ_FOREACH_SAFE(dmabuf, &dpy->dmabuf.bufs, next, tmp) {
    274        if (keep > 0) {
    275            keep--;
    276            continue;
    277        }
    278        assert(dmabuf != dpy->dmabuf.primary);
    279        vfio_display_free_one_dmabuf(dpy, dmabuf);
    280    }
    281}
    282
    283static void vfio_display_dmabuf_update(void *opaque)
    284{
    285    VFIOPCIDevice *vdev = opaque;
    286    VFIODisplay *dpy = vdev->dpy;
    287    VFIODMABuf *primary, *cursor;
    288    bool free_bufs = false, new_cursor = false;
    289
    290    primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY);
    291    if (primary == NULL) {
    292        if (dpy->ramfb) {
    293            ramfb_display_update(dpy->con, dpy->ramfb);
    294        }
    295        return;
    296    }
    297
    298    if (dpy->dmabuf.primary != primary) {
    299        dpy->dmabuf.primary = primary;
    300        qemu_console_resize(dpy->con,
    301                            primary->buf.width, primary->buf.height);
    302        dpy_gl_scanout_dmabuf(dpy->con, &primary->buf);
    303        free_bufs = true;
    304    }
    305
    306    cursor = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_CURSOR);
    307    if (dpy->dmabuf.cursor != cursor) {
    308        dpy->dmabuf.cursor = cursor;
    309        new_cursor = true;
    310        free_bufs = true;
    311    }
    312
    313    if (cursor && (new_cursor || cursor->hot_updates)) {
    314        bool have_hot = (cursor->hot_x != 0xffffffff &&
    315                         cursor->hot_y != 0xffffffff);
    316        dpy_gl_cursor_dmabuf(dpy->con, &cursor->buf, have_hot,
    317                             cursor->hot_x, cursor->hot_y);
    318        cursor->hot_updates = 0;
    319    } else if (!cursor && new_cursor) {
    320        dpy_gl_cursor_dmabuf(dpy->con, NULL, false, 0, 0);
    321    }
    322
    323    if (cursor && cursor->pos_updates) {
    324        dpy_gl_cursor_position(dpy->con,
    325                               cursor->pos_x,
    326                               cursor->pos_y);
    327        cursor->pos_updates = 0;
    328    }
    329
    330    dpy_gl_update(dpy->con, 0, 0, primary->buf.width, primary->buf.height);
    331
    332    if (free_bufs) {
    333        vfio_display_free_dmabufs(vdev);
    334    }
    335}
    336
    337static int vfio_display_get_flags(void *opaque)
    338{
    339    return GRAPHIC_FLAGS_GL | GRAPHIC_FLAGS_DMABUF;
    340}
    341
    342static const GraphicHwOps vfio_display_dmabuf_ops = {
    343    .get_flags  = vfio_display_get_flags,
    344    .gfx_update = vfio_display_dmabuf_update,
    345    .ui_info    = vfio_display_edid_ui_info,
    346};
    347
    348static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp)
    349{
    350    if (!display_opengl) {
    351        error_setg(errp, "vfio-display-dmabuf: opengl not available");
    352        return -1;
    353    }
    354
    355    vdev->dpy = g_new0(VFIODisplay, 1);
    356    vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0,
    357                                          &vfio_display_dmabuf_ops,
    358                                          vdev);
    359    if (vdev->enable_ramfb) {
    360        vdev->dpy->ramfb = ramfb_setup(errp);
    361    }
    362    vfio_display_edid_init(vdev);
    363    return 0;
    364}
    365
    366static void vfio_display_dmabuf_exit(VFIODisplay *dpy)
    367{
    368    VFIODMABuf *dmabuf;
    369
    370    if (QTAILQ_EMPTY(&dpy->dmabuf.bufs)) {
    371        return;
    372    }
    373
    374    while ((dmabuf = QTAILQ_FIRST(&dpy->dmabuf.bufs)) != NULL) {
    375        vfio_display_free_one_dmabuf(dpy, dmabuf);
    376    }
    377}
    378
    379/* ---------------------------------------------------------------------- */
    380void vfio_display_reset(VFIOPCIDevice *vdev)
    381{
    382    if (!vdev || !vdev->dpy || !vdev->dpy->con ||
    383        !vdev->dpy->dmabuf.primary) {
    384        return;
    385    }
    386
    387    dpy_gl_scanout_disable(vdev->dpy->con);
    388    vfio_display_dmabuf_exit(vdev->dpy);
    389    dpy_gfx_update_full(vdev->dpy->con);
    390}
    391
    392static void vfio_display_region_update(void *opaque)
    393{
    394    VFIOPCIDevice *vdev = opaque;
    395    VFIODisplay *dpy = vdev->dpy;
    396    struct vfio_device_gfx_plane_info plane = {
    397        .argsz = sizeof(plane),
    398        .flags = VFIO_GFX_PLANE_TYPE_REGION
    399    };
    400    pixman_format_code_t format;
    401    int ret;
    402
    403    ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane);
    404    if (ret < 0) {
    405        error_report("ioctl VFIO_DEVICE_QUERY_GFX_PLANE: %s",
    406                     strerror(errno));
    407        return;
    408    }
    409    if (!plane.drm_format || !plane.size) {
    410        if (dpy->ramfb) {
    411            ramfb_display_update(dpy->con, dpy->ramfb);
    412            dpy->region.surface = NULL;
    413        }
    414        return;
    415    }
    416    format = qemu_drm_format_to_pixman(plane.drm_format);
    417    if (!format) {
    418        return;
    419    }
    420
    421    if (dpy->region.buffer.size &&
    422        dpy->region.buffer.nr != plane.region_index) {
    423        /* region changed */
    424        vfio_region_exit(&dpy->region.buffer);
    425        vfio_region_finalize(&dpy->region.buffer);
    426        dpy->region.surface = NULL;
    427    }
    428
    429    if (dpy->region.surface &&
    430        (surface_width(dpy->region.surface) != plane.width ||
    431         surface_height(dpy->region.surface) != plane.height ||
    432         surface_format(dpy->region.surface) != format)) {
    433        /* size changed */
    434        dpy->region.surface = NULL;
    435    }
    436
    437    if (!dpy->region.buffer.size) {
    438        /* mmap region */
    439        ret = vfio_region_setup(OBJECT(vdev), &vdev->vbasedev,
    440                                &dpy->region.buffer,
    441                                plane.region_index,
    442                                "display");
    443        if (ret != 0) {
    444            error_report("%s: vfio_region_setup(%d): %s",
    445                         __func__, plane.region_index, strerror(-ret));
    446            goto err;
    447        }
    448        ret = vfio_region_mmap(&dpy->region.buffer);
    449        if (ret != 0) {
    450            error_report("%s: vfio_region_mmap(%d): %s", __func__,
    451                         plane.region_index, strerror(-ret));
    452            goto err;
    453        }
    454        assert(dpy->region.buffer.mmaps[0].mmap != NULL);
    455    }
    456
    457    if (dpy->region.surface == NULL) {
    458        /* create surface */
    459        dpy->region.surface = qemu_create_displaysurface_from
    460            (plane.width, plane.height, format,
    461             plane.stride, dpy->region.buffer.mmaps[0].mmap);
    462        dpy_gfx_replace_surface(dpy->con, dpy->region.surface);
    463    }
    464
    465    /* full screen update */
    466    dpy_gfx_update(dpy->con, 0, 0,
    467                   surface_width(dpy->region.surface),
    468                   surface_height(dpy->region.surface));
    469    return;
    470
    471err:
    472    vfio_region_exit(&dpy->region.buffer);
    473    vfio_region_finalize(&dpy->region.buffer);
    474}
    475
    476static const GraphicHwOps vfio_display_region_ops = {
    477    .gfx_update = vfio_display_region_update,
    478};
    479
    480static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp)
    481{
    482    vdev->dpy = g_new0(VFIODisplay, 1);
    483    vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0,
    484                                          &vfio_display_region_ops,
    485                                          vdev);
    486    if (vdev->enable_ramfb) {
    487        vdev->dpy->ramfb = ramfb_setup(errp);
    488    }
    489    return 0;
    490}
    491
    492static void vfio_display_region_exit(VFIODisplay *dpy)
    493{
    494    if (!dpy->region.buffer.size) {
    495        return;
    496    }
    497
    498    vfio_region_exit(&dpy->region.buffer);
    499    vfio_region_finalize(&dpy->region.buffer);
    500}
    501
    502/* ---------------------------------------------------------------------- */
    503
    504int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp)
    505{
    506    struct vfio_device_gfx_plane_info probe;
    507    int ret;
    508
    509    memset(&probe, 0, sizeof(probe));
    510    probe.argsz = sizeof(probe);
    511    probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_DMABUF;
    512    ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe);
    513    if (ret == 0) {
    514        return vfio_display_dmabuf_init(vdev, errp);
    515    }
    516
    517    memset(&probe, 0, sizeof(probe));
    518    probe.argsz = sizeof(probe);
    519    probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_REGION;
    520    ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe);
    521    if (ret == 0) {
    522        return vfio_display_region_init(vdev, errp);
    523    }
    524
    525    if (vdev->display == ON_OFF_AUTO_AUTO) {
    526        /* not an error in automatic mode */
    527        return 0;
    528    }
    529
    530    error_setg(errp, "vfio: device doesn't support any (known) display method");
    531    return -1;
    532}
    533
    534void vfio_display_finalize(VFIOPCIDevice *vdev)
    535{
    536    if (!vdev->dpy) {
    537        return;
    538    }
    539
    540    graphic_console_close(vdev->dpy->con);
    541    vfio_display_dmabuf_exit(vdev->dpy);
    542    vfio_display_region_exit(vdev->dpy);
    543    vfio_display_edid_exit(vdev->dpy);
    544    g_free(vdev->dpy);
    545}