qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

display.c (15998B)


      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 
     39 static 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 
     52 err:
     53     trace_vfio_display_edid_write_error();
     54 }
     55 
     56 static 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 
    104 err:
    105     trace_vfio_display_edid_write_error();
    106     return;
    107 }
    108 
    109 static void 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;
    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 
    126 static void vfio_display_edid_init(VFIOPCIDevice *vdev)
    127 {
    128     VFIODisplay *dpy = vdev->dpy;
    129     int fd = vdev->vbasedev.fd;
    130     int ret;
    131 
    132     ret = vfio_get_dev_region_info(&vdev->vbasedev,
    133                                    VFIO_REGION_TYPE_GFX,
    134                                    VFIO_REGION_SUBTYPE_GFX_EDID,
    135                                    &dpy->edid_info);
    136     if (ret) {
    137         return;
    138     }
    139 
    140     trace_vfio_display_edid_available();
    141     dpy->edid_regs = g_new0(struct vfio_region_gfx_edid, 1);
    142     if (pread_field(fd, dpy->edid_info, dpy->edid_regs, edid_offset)) {
    143         goto err;
    144     }
    145     if (pread_field(fd, dpy->edid_info, dpy->edid_regs, edid_max_size)) {
    146         goto err;
    147     }
    148     if (pread_field(fd, dpy->edid_info, dpy->edid_regs, max_xres)) {
    149         goto err;
    150     }
    151     if (pread_field(fd, dpy->edid_info, dpy->edid_regs, max_yres)) {
    152         goto err;
    153     }
    154 
    155     dpy->edid_blob = g_malloc0(dpy->edid_regs->edid_max_size);
    156 
    157     /* if xres + yres properties are unset use the maximum resolution */
    158     if (!vdev->display_xres) {
    159         vdev->display_xres = dpy->edid_regs->max_xres;
    160     }
    161     if (!vdev->display_yres) {
    162         vdev->display_yres = dpy->edid_regs->max_yres;
    163     }
    164 
    165     dpy->edid_link_timer = timer_new_ms(QEMU_CLOCK_REALTIME,
    166                                         vfio_display_edid_link_up, vdev);
    167 
    168     vfio_display_edid_update(vdev, true, 0, 0);
    169     return;
    170 
    171 err:
    172     trace_vfio_display_edid_write_error();
    173     g_free(dpy->edid_regs);
    174     dpy->edid_regs = NULL;
    175     return;
    176 }
    177 
    178 static void vfio_display_edid_exit(VFIODisplay *dpy)
    179 {
    180     if (!dpy->edid_regs) {
    181         return;
    182     }
    183 
    184     g_free(dpy->edid_regs);
    185     g_free(dpy->edid_blob);
    186     timer_free(dpy->edid_link_timer);
    187 }
    188 
    189 static void vfio_display_update_cursor(VFIODMABuf *dmabuf,
    190                                        struct vfio_device_gfx_plane_info *plane)
    191 {
    192     if (dmabuf->pos_x != plane->x_pos || dmabuf->pos_y != plane->y_pos) {
    193         dmabuf->pos_x      = plane->x_pos;
    194         dmabuf->pos_y      = plane->y_pos;
    195         dmabuf->pos_updates++;
    196     }
    197     if (dmabuf->hot_x != plane->x_hot || dmabuf->hot_y != plane->y_hot) {
    198         dmabuf->hot_x      = plane->x_hot;
    199         dmabuf->hot_y      = plane->y_hot;
    200         dmabuf->hot_updates++;
    201     }
    202 }
    203 
    204 static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev,
    205                                            uint32_t plane_type)
    206 {
    207     VFIODisplay *dpy = vdev->dpy;
    208     struct vfio_device_gfx_plane_info plane;
    209     VFIODMABuf *dmabuf;
    210     int fd, ret;
    211 
    212     memset(&plane, 0, sizeof(plane));
    213     plane.argsz = sizeof(plane);
    214     plane.flags = VFIO_GFX_PLANE_TYPE_DMABUF;
    215     plane.drm_plane_type = plane_type;
    216     ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane);
    217     if (ret < 0) {
    218         return NULL;
    219     }
    220     if (!plane.drm_format || !plane.size) {
    221         return NULL;
    222     }
    223 
    224     QTAILQ_FOREACH(dmabuf, &dpy->dmabuf.bufs, next) {
    225         if (dmabuf->dmabuf_id == plane.dmabuf_id) {
    226             /* found in list, move to head, return it */
    227             QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next);
    228             QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next);
    229             if (plane_type == DRM_PLANE_TYPE_CURSOR) {
    230                 vfio_display_update_cursor(dmabuf, &plane);
    231             }
    232             return dmabuf;
    233         }
    234     }
    235 
    236     fd = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_GFX_DMABUF, &plane.dmabuf_id);
    237     if (fd < 0) {
    238         return NULL;
    239     }
    240 
    241     dmabuf = g_new0(VFIODMABuf, 1);
    242     dmabuf->dmabuf_id  = plane.dmabuf_id;
    243     dmabuf->buf.width  = plane.width;
    244     dmabuf->buf.height = plane.height;
    245     dmabuf->buf.stride = plane.stride;
    246     dmabuf->buf.fourcc = plane.drm_format;
    247     dmabuf->buf.modifier = plane.drm_format_mod;
    248     dmabuf->buf.fd     = fd;
    249     if (plane_type == DRM_PLANE_TYPE_CURSOR) {
    250         vfio_display_update_cursor(dmabuf, &plane);
    251     }
    252 
    253     QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next);
    254     return dmabuf;
    255 }
    256 
    257 static void vfio_display_free_one_dmabuf(VFIODisplay *dpy, VFIODMABuf *dmabuf)
    258 {
    259     QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next);
    260     dpy_gl_release_dmabuf(dpy->con, &dmabuf->buf);
    261     close(dmabuf->buf.fd);
    262     g_free(dmabuf);
    263 }
    264 
    265 static void vfio_display_free_dmabufs(VFIOPCIDevice *vdev)
    266 {
    267     VFIODisplay *dpy = vdev->dpy;
    268     VFIODMABuf *dmabuf, *tmp;
    269     uint32_t keep = 5;
    270 
    271     QTAILQ_FOREACH_SAFE(dmabuf, &dpy->dmabuf.bufs, next, tmp) {
    272         if (keep > 0) {
    273             keep--;
    274             continue;
    275         }
    276         assert(dmabuf != dpy->dmabuf.primary);
    277         vfio_display_free_one_dmabuf(dpy, dmabuf);
    278     }
    279 }
    280 
    281 static void vfio_display_dmabuf_update(void *opaque)
    282 {
    283     VFIOPCIDevice *vdev = opaque;
    284     VFIODisplay *dpy = vdev->dpy;
    285     VFIODMABuf *primary, *cursor;
    286     bool free_bufs = false, new_cursor = false;
    287 
    288     primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY);
    289     if (primary == NULL) {
    290         if (dpy->ramfb) {
    291             ramfb_display_update(dpy->con, dpy->ramfb);
    292         }
    293         return;
    294     }
    295 
    296     if (dpy->dmabuf.primary != primary) {
    297         dpy->dmabuf.primary = primary;
    298         qemu_console_resize(dpy->con,
    299                             primary->buf.width, primary->buf.height);
    300         dpy_gl_scanout_dmabuf(dpy->con, &primary->buf);
    301         free_bufs = true;
    302     }
    303 
    304     cursor = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_CURSOR);
    305     if (dpy->dmabuf.cursor != cursor) {
    306         dpy->dmabuf.cursor = cursor;
    307         new_cursor = true;
    308         free_bufs = true;
    309     }
    310 
    311     if (cursor && (new_cursor || cursor->hot_updates)) {
    312         bool have_hot = (cursor->hot_x != 0xffffffff &&
    313                          cursor->hot_y != 0xffffffff);
    314         dpy_gl_cursor_dmabuf(dpy->con, &cursor->buf, have_hot,
    315                              cursor->hot_x, cursor->hot_y);
    316         cursor->hot_updates = 0;
    317     } else if (!cursor && new_cursor) {
    318         dpy_gl_cursor_dmabuf(dpy->con, NULL, false, 0, 0);
    319     }
    320 
    321     if (cursor && cursor->pos_updates) {
    322         dpy_gl_cursor_position(dpy->con,
    323                                cursor->pos_x,
    324                                cursor->pos_y);
    325         cursor->pos_updates = 0;
    326     }
    327 
    328     dpy_gl_update(dpy->con, 0, 0, primary->buf.width, primary->buf.height);
    329 
    330     if (free_bufs) {
    331         vfio_display_free_dmabufs(vdev);
    332     }
    333 }
    334 
    335 static int vfio_display_get_flags(void *opaque)
    336 {
    337     return GRAPHIC_FLAGS_GL | GRAPHIC_FLAGS_DMABUF;
    338 }
    339 
    340 static const GraphicHwOps vfio_display_dmabuf_ops = {
    341     .get_flags  = vfio_display_get_flags,
    342     .gfx_update = vfio_display_dmabuf_update,
    343     .ui_info    = vfio_display_edid_ui_info,
    344 };
    345 
    346 static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp)
    347 {
    348     if (!display_opengl) {
    349         error_setg(errp, "vfio-display-dmabuf: opengl not available");
    350         return -1;
    351     }
    352 
    353     vdev->dpy = g_new0(VFIODisplay, 1);
    354     vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0,
    355                                           &vfio_display_dmabuf_ops,
    356                                           vdev);
    357     if (vdev->enable_ramfb) {
    358         vdev->dpy->ramfb = ramfb_setup(errp);
    359     }
    360     vfio_display_edid_init(vdev);
    361     return 0;
    362 }
    363 
    364 static void vfio_display_dmabuf_exit(VFIODisplay *dpy)
    365 {
    366     VFIODMABuf *dmabuf;
    367 
    368     if (QTAILQ_EMPTY(&dpy->dmabuf.bufs)) {
    369         return;
    370     }
    371 
    372     while ((dmabuf = QTAILQ_FIRST(&dpy->dmabuf.bufs)) != NULL) {
    373         vfio_display_free_one_dmabuf(dpy, dmabuf);
    374     }
    375 }
    376 
    377 /* ---------------------------------------------------------------------- */
    378 void vfio_display_reset(VFIOPCIDevice *vdev)
    379 {
    380     if (!vdev || !vdev->dpy || !vdev->dpy->con ||
    381         !vdev->dpy->dmabuf.primary) {
    382         return;
    383     }
    384 
    385     dpy_gl_scanout_disable(vdev->dpy->con);
    386     vfio_display_dmabuf_exit(vdev->dpy);
    387     dpy_gfx_update_full(vdev->dpy->con);
    388 }
    389 
    390 static void vfio_display_region_update(void *opaque)
    391 {
    392     VFIOPCIDevice *vdev = opaque;
    393     VFIODisplay *dpy = vdev->dpy;
    394     struct vfio_device_gfx_plane_info plane = {
    395         .argsz = sizeof(plane),
    396         .flags = VFIO_GFX_PLANE_TYPE_REGION
    397     };
    398     pixman_format_code_t format;
    399     int ret;
    400 
    401     ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane);
    402     if (ret < 0) {
    403         error_report("ioctl VFIO_DEVICE_QUERY_GFX_PLANE: %s",
    404                      strerror(errno));
    405         return;
    406     }
    407     if (!plane.drm_format || !plane.size) {
    408         if (dpy->ramfb) {
    409             ramfb_display_update(dpy->con, dpy->ramfb);
    410             dpy->region.surface = NULL;
    411         }
    412         return;
    413     }
    414     format = qemu_drm_format_to_pixman(plane.drm_format);
    415     if (!format) {
    416         return;
    417     }
    418 
    419     if (dpy->region.buffer.size &&
    420         dpy->region.buffer.nr != plane.region_index) {
    421         /* region changed */
    422         vfio_region_exit(&dpy->region.buffer);
    423         vfio_region_finalize(&dpy->region.buffer);
    424         dpy->region.surface = NULL;
    425     }
    426 
    427     if (dpy->region.surface &&
    428         (surface_width(dpy->region.surface) != plane.width ||
    429          surface_height(dpy->region.surface) != plane.height ||
    430          surface_format(dpy->region.surface) != format)) {
    431         /* size changed */
    432         dpy->region.surface = NULL;
    433     }
    434 
    435     if (!dpy->region.buffer.size) {
    436         /* mmap region */
    437         ret = vfio_region_setup(OBJECT(vdev), &vdev->vbasedev,
    438                                 &dpy->region.buffer,
    439                                 plane.region_index,
    440                                 "display");
    441         if (ret != 0) {
    442             error_report("%s: vfio_region_setup(%d): %s",
    443                          __func__, plane.region_index, strerror(-ret));
    444             goto err;
    445         }
    446         ret = vfio_region_mmap(&dpy->region.buffer);
    447         if (ret != 0) {
    448             error_report("%s: vfio_region_mmap(%d): %s", __func__,
    449                          plane.region_index, strerror(-ret));
    450             goto err;
    451         }
    452         assert(dpy->region.buffer.mmaps[0].mmap != NULL);
    453     }
    454 
    455     if (dpy->region.surface == NULL) {
    456         /* create surface */
    457         dpy->region.surface = qemu_create_displaysurface_from
    458             (plane.width, plane.height, format,
    459              plane.stride, dpy->region.buffer.mmaps[0].mmap);
    460         dpy_gfx_replace_surface(dpy->con, dpy->region.surface);
    461     }
    462 
    463     /* full screen update */
    464     dpy_gfx_update(dpy->con, 0, 0,
    465                    surface_width(dpy->region.surface),
    466                    surface_height(dpy->region.surface));
    467     return;
    468 
    469 err:
    470     vfio_region_exit(&dpy->region.buffer);
    471     vfio_region_finalize(&dpy->region.buffer);
    472 }
    473 
    474 static const GraphicHwOps vfio_display_region_ops = {
    475     .gfx_update = vfio_display_region_update,
    476 };
    477 
    478 static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp)
    479 {
    480     vdev->dpy = g_new0(VFIODisplay, 1);
    481     vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0,
    482                                           &vfio_display_region_ops,
    483                                           vdev);
    484     if (vdev->enable_ramfb) {
    485         vdev->dpy->ramfb = ramfb_setup(errp);
    486     }
    487     return 0;
    488 }
    489 
    490 static void vfio_display_region_exit(VFIODisplay *dpy)
    491 {
    492     if (!dpy->region.buffer.size) {
    493         return;
    494     }
    495 
    496     vfio_region_exit(&dpy->region.buffer);
    497     vfio_region_finalize(&dpy->region.buffer);
    498 }
    499 
    500 /* ---------------------------------------------------------------------- */
    501 
    502 int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp)
    503 {
    504     struct vfio_device_gfx_plane_info probe;
    505     int ret;
    506 
    507     memset(&probe, 0, sizeof(probe));
    508     probe.argsz = sizeof(probe);
    509     probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_DMABUF;
    510     ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe);
    511     if (ret == 0) {
    512         return vfio_display_dmabuf_init(vdev, errp);
    513     }
    514 
    515     memset(&probe, 0, sizeof(probe));
    516     probe.argsz = sizeof(probe);
    517     probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_REGION;
    518     ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe);
    519     if (ret == 0) {
    520         return vfio_display_region_init(vdev, errp);
    521     }
    522 
    523     if (vdev->display == ON_OFF_AUTO_AUTO) {
    524         /* not an error in automatic mode */
    525         return 0;
    526     }
    527 
    528     error_setg(errp, "vfio: device doesn't support any (known) display method");
    529     return -1;
    530 }
    531 
    532 void vfio_display_finalize(VFIOPCIDevice *vdev)
    533 {
    534     if (!vdev->dpy) {
    535         return;
    536     }
    537 
    538     graphic_console_close(vdev->dpy->con);
    539     vfio_display_dmabuf_exit(vdev->dpy);
    540     vfio_display_region_exit(vdev->dpy);
    541     vfio_display_edid_exit(vdev->dpy);
    542     g_free(vdev->dpy);
    543 }