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