2 * display support for mdev based vgpu devices
4 * Copyright Red Hat, Inc. 2017
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
13 #include "qemu/osdep.h"
14 #include <linux/vfio.h>
15 #include <sys/ioctl.h>
17 #include "qemu/error-report.h"
18 #include "hw/display/edid.h"
19 #include "ui/console.h"
20 #include "qapi/error.h"
24 #ifndef DRM_PLANE_TYPE_PRIMARY
25 # define DRM_PLANE_TYPE_PRIMARY 1
26 # define DRM_PLANE_TYPE_CURSOR 2
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)))
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)))
40 static void vfio_display_edid_link_up(void *opaque
)
42 VFIOPCIDevice
*vdev
= opaque
;
43 VFIODisplay
*dpy
= vdev
->dpy
;
44 int fd
= vdev
->vbasedev
.fd
;
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
)) {
50 trace_vfio_display_edid_link_up();
54 trace_vfio_display_edid_write_error();
57 static void vfio_display_edid_update(VFIOPCIDevice
*vdev
, bool enabled
,
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
,
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
)) {
74 trace_vfio_display_edid_link_down();
80 if (edid
.maxx
&& edid
.prefx
> edid
.maxx
) {
81 edid
.prefx
= edid
.maxx
;
83 if (edid
.maxy
&& edid
.prefy
> edid
.maxy
) {
84 edid
.prefy
= edid
.maxy
;
86 qemu_edid_generate(dpy
->edid_blob
,
87 dpy
->edid_regs
->edid_max_size
,
89 trace_vfio_display_edid_update(edid
.prefx
, edid
.prefy
);
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
)) {
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
) {
101 timer_mod(dpy
->edid_link_timer
,
102 qemu_clock_get_ms(QEMU_CLOCK_REALTIME
) + 100);
106 trace_vfio_display_edid_write_error();
110 static void vfio_display_edid_ui_info(void *opaque
, uint32_t idx
,
113 VFIOPCIDevice
*vdev
= opaque
;
114 VFIODisplay
*dpy
= vdev
->dpy
;
116 if (!dpy
->edid_regs
) {
120 if (info
->width
&& info
->height
) {
121 vfio_display_edid_update(vdev
, true, info
->width
, info
->height
);
123 vfio_display_edid_update(vdev
, false, 0, 0);
127 static bool vfio_display_edid_init(VFIOPCIDevice
*vdev
, Error
**errp
)
129 VFIODisplay
*dpy
= vdev
->dpy
;
130 int fd
= vdev
->vbasedev
.fd
;
133 ret
= vfio_get_dev_region_info(&vdev
->vbasedev
,
134 VFIO_REGION_TYPE_GFX
,
135 VFIO_REGION_SUBTYPE_GFX_EDID
,
138 /* Failed to get GFX edid info, allow to go through without edid. */
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
)) {
147 if (pread_field(fd
, dpy
->edid_info
, dpy
->edid_regs
, edid_max_size
)) {
150 if (pread_field(fd
, dpy
->edid_info
, dpy
->edid_regs
, max_xres
)) {
153 if (pread_field(fd
, dpy
->edid_info
, dpy
->edid_regs
, max_yres
)) {
157 dpy
->edid_blob
= g_malloc0(dpy
->edid_regs
->edid_max_size
);
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
;
163 if (!vdev
->display_yres
) {
164 vdev
->display_yres
= dpy
->edid_regs
->max_yres
;
167 dpy
->edid_link_timer
= timer_new_ms(QEMU_CLOCK_REALTIME
,
168 vfio_display_edid_link_up
, vdev
);
170 vfio_display_edid_update(vdev
, true, 0, 0);
174 error_setg(errp
, "vfio: failed to read GFX edid field");
175 trace_vfio_display_edid_write_error();
176 g_free(dpy
->edid_info
);
177 g_free(dpy
->edid_regs
);
178 dpy
->edid_info
= NULL
;
179 dpy
->edid_regs
= NULL
;
183 static void vfio_display_edid_exit(VFIODisplay
*dpy
)
185 if (!dpy
->edid_regs
) {
189 g_free(dpy
->edid_info
);
190 g_free(dpy
->edid_regs
);
191 g_free(dpy
->edid_blob
);
192 timer_free(dpy
->edid_link_timer
);
195 static void vfio_display_update_cursor(VFIODMABuf
*dmabuf
,
196 struct vfio_device_gfx_plane_info
*plane
)
198 if (dmabuf
->pos_x
!= plane
->x_pos
|| dmabuf
->pos_y
!= plane
->y_pos
) {
199 dmabuf
->pos_x
= plane
->x_pos
;
200 dmabuf
->pos_y
= plane
->y_pos
;
201 dmabuf
->pos_updates
++;
203 if (dmabuf
->hot_x
!= plane
->x_hot
|| dmabuf
->hot_y
!= plane
->y_hot
) {
204 dmabuf
->hot_x
= plane
->x_hot
;
205 dmabuf
->hot_y
= plane
->y_hot
;
206 dmabuf
->hot_updates
++;
210 static VFIODMABuf
*vfio_display_get_dmabuf(VFIOPCIDevice
*vdev
,
213 VFIODisplay
*dpy
= vdev
->dpy
;
214 struct vfio_device_gfx_plane_info plane
;
218 memset(&plane
, 0, sizeof(plane
));
219 plane
.argsz
= sizeof(plane
);
220 plane
.flags
= VFIO_GFX_PLANE_TYPE_DMABUF
;
221 plane
.drm_plane_type
= plane_type
;
222 ret
= ioctl(vdev
->vbasedev
.fd
, VFIO_DEVICE_QUERY_GFX_PLANE
, &plane
);
226 if (!plane
.drm_format
|| !plane
.size
) {
230 QTAILQ_FOREACH(dmabuf
, &dpy
->dmabuf
.bufs
, next
) {
231 if (dmabuf
->dmabuf_id
== plane
.dmabuf_id
) {
232 /* found in list, move to head, return it */
233 QTAILQ_REMOVE(&dpy
->dmabuf
.bufs
, dmabuf
, next
);
234 QTAILQ_INSERT_HEAD(&dpy
->dmabuf
.bufs
, dmabuf
, next
);
235 if (plane_type
== DRM_PLANE_TYPE_CURSOR
) {
236 vfio_display_update_cursor(dmabuf
, &plane
);
242 fd
= ioctl(vdev
->vbasedev
.fd
, VFIO_DEVICE_GET_GFX_DMABUF
, &plane
.dmabuf_id
);
247 dmabuf
= g_new0(VFIODMABuf
, 1);
248 dmabuf
->dmabuf_id
= plane
.dmabuf_id
;
249 dmabuf
->buf
= qemu_dmabuf_new(plane
.width
, plane
.height
,
250 plane
.stride
, 0, 0, plane
.width
,
251 plane
.height
, plane
.drm_format
,
252 plane
.drm_format_mod
, fd
, false, false);
254 if (plane_type
== DRM_PLANE_TYPE_CURSOR
) {
255 vfio_display_update_cursor(dmabuf
, &plane
);
258 QTAILQ_INSERT_HEAD(&dpy
->dmabuf
.bufs
, dmabuf
, next
);
262 static void vfio_display_free_one_dmabuf(VFIODisplay
*dpy
, VFIODMABuf
*dmabuf
)
264 QTAILQ_REMOVE(&dpy
->dmabuf
.bufs
, dmabuf
, next
);
266 qemu_dmabuf_close(dmabuf
->buf
);
267 dpy_gl_release_dmabuf(dpy
->con
, dmabuf
->buf
);
268 g_clear_pointer(&dmabuf
->buf
, qemu_dmabuf_free
);
272 static void vfio_display_free_dmabufs(VFIOPCIDevice
*vdev
)
274 VFIODisplay
*dpy
= vdev
->dpy
;
275 VFIODMABuf
*dmabuf
, *tmp
;
278 QTAILQ_FOREACH_SAFE(dmabuf
, &dpy
->dmabuf
.bufs
, next
, tmp
) {
283 assert(dmabuf
!= dpy
->dmabuf
.primary
);
284 vfio_display_free_one_dmabuf(dpy
, dmabuf
);
288 static void vfio_display_dmabuf_update(void *opaque
)
290 VFIOPCIDevice
*vdev
= opaque
;
291 VFIODisplay
*dpy
= vdev
->dpy
;
292 VFIODMABuf
*primary
, *cursor
;
293 uint32_t width
, height
;
294 bool free_bufs
= false, new_cursor
= false;
296 primary
= vfio_display_get_dmabuf(vdev
, DRM_PLANE_TYPE_PRIMARY
);
297 if (primary
== NULL
) {
299 ramfb_display_update(dpy
->con
, dpy
->ramfb
);
304 width
= qemu_dmabuf_get_width(primary
->buf
);
305 height
= qemu_dmabuf_get_height(primary
->buf
);
307 if (dpy
->dmabuf
.primary
!= primary
) {
308 dpy
->dmabuf
.primary
= primary
;
309 qemu_console_resize(dpy
->con
, width
, height
);
310 dpy_gl_scanout_dmabuf(dpy
->con
, primary
->buf
);
314 cursor
= vfio_display_get_dmabuf(vdev
, DRM_PLANE_TYPE_CURSOR
);
315 if (dpy
->dmabuf
.cursor
!= cursor
) {
316 dpy
->dmabuf
.cursor
= cursor
;
321 if (cursor
&& (new_cursor
|| cursor
->hot_updates
)) {
322 bool have_hot
= (cursor
->hot_x
!= 0xffffffff &&
323 cursor
->hot_y
!= 0xffffffff);
324 dpy_gl_cursor_dmabuf(dpy
->con
, cursor
->buf
, have_hot
,
325 cursor
->hot_x
, cursor
->hot_y
);
326 cursor
->hot_updates
= 0;
327 } else if (!cursor
&& new_cursor
) {
328 dpy_gl_cursor_dmabuf(dpy
->con
, NULL
, false, 0, 0);
331 if (cursor
&& cursor
->pos_updates
) {
332 dpy_gl_cursor_position(dpy
->con
,
335 cursor
->pos_updates
= 0;
338 dpy_gl_update(dpy
->con
, 0, 0, width
, height
);
341 vfio_display_free_dmabufs(vdev
);
345 static int vfio_display_get_flags(void *opaque
)
347 return GRAPHIC_FLAGS_GL
| GRAPHIC_FLAGS_DMABUF
;
350 static const GraphicHwOps vfio_display_dmabuf_ops
= {
351 .get_flags
= vfio_display_get_flags
,
352 .gfx_update
= vfio_display_dmabuf_update
,
353 .ui_info
= vfio_display_edid_ui_info
,
356 static bool vfio_display_dmabuf_init(VFIOPCIDevice
*vdev
, Error
**errp
)
358 if (!display_opengl
) {
359 error_setg(errp
, "vfio-display-dmabuf: opengl not available");
363 vdev
->dpy
= g_new0(VFIODisplay
, 1);
364 vdev
->dpy
->con
= graphic_console_init(DEVICE(vdev
), 0,
365 &vfio_display_dmabuf_ops
,
367 if (vdev
->enable_ramfb
) {
368 vdev
->dpy
->ramfb
= ramfb_setup(errp
);
369 if (!vdev
->dpy
->ramfb
) {
373 return vfio_display_edid_init(vdev
, errp
);
376 static void vfio_display_dmabuf_exit(VFIODisplay
*dpy
)
380 if (QTAILQ_EMPTY(&dpy
->dmabuf
.bufs
)) {
384 while ((dmabuf
= QTAILQ_FIRST(&dpy
->dmabuf
.bufs
)) != NULL
) {
385 vfio_display_free_one_dmabuf(dpy
, dmabuf
);
389 /* ---------------------------------------------------------------------- */
390 void vfio_display_reset(VFIOPCIDevice
*vdev
)
392 if (!vdev
|| !vdev
->dpy
|| !vdev
->dpy
->con
||
393 !vdev
->dpy
->dmabuf
.primary
) {
397 dpy_gl_scanout_disable(vdev
->dpy
->con
);
398 vfio_display_dmabuf_exit(vdev
->dpy
);
399 dpy_gfx_update_full(vdev
->dpy
->con
);
402 static void vfio_display_region_update(void *opaque
)
404 VFIOPCIDevice
*vdev
= opaque
;
405 VFIODisplay
*dpy
= vdev
->dpy
;
406 struct vfio_device_gfx_plane_info plane
= {
407 .argsz
= sizeof(plane
),
408 .flags
= VFIO_GFX_PLANE_TYPE_REGION
410 pixman_format_code_t format
;
413 ret
= ioctl(vdev
->vbasedev
.fd
, VFIO_DEVICE_QUERY_GFX_PLANE
, &plane
);
415 error_report("ioctl VFIO_DEVICE_QUERY_GFX_PLANE: %s",
419 if (!plane
.drm_format
|| !plane
.size
) {
421 ramfb_display_update(dpy
->con
, dpy
->ramfb
);
422 dpy
->region
.surface
= NULL
;
426 format
= qemu_drm_format_to_pixman(plane
.drm_format
);
431 if (dpy
->region
.buffer
.size
&&
432 dpy
->region
.buffer
.nr
!= plane
.region_index
) {
434 vfio_region_exit(&dpy
->region
.buffer
);
435 vfio_region_finalize(&dpy
->region
.buffer
);
436 dpy
->region
.surface
= NULL
;
439 if (dpy
->region
.surface
&&
440 (surface_width(dpy
->region
.surface
) != plane
.width
||
441 surface_height(dpy
->region
.surface
) != plane
.height
||
442 surface_format(dpy
->region
.surface
) != format
)) {
444 dpy
->region
.surface
= NULL
;
447 if (!dpy
->region
.buffer
.size
) {
449 ret
= vfio_region_setup(OBJECT(vdev
), &vdev
->vbasedev
,
454 error_report("%s: vfio_region_setup(%d): %s",
455 __func__
, plane
.region_index
, strerror(-ret
));
458 ret
= vfio_region_mmap(&dpy
->region
.buffer
);
460 error_report("%s: vfio_region_mmap(%d): %s", __func__
,
461 plane
.region_index
, strerror(-ret
));
464 assert(dpy
->region
.buffer
.mmaps
[0].mmap
!= NULL
);
467 if (dpy
->region
.surface
== NULL
) {
469 dpy
->region
.surface
= qemu_create_displaysurface_from
470 (plane
.width
, plane
.height
, format
,
471 plane
.stride
, dpy
->region
.buffer
.mmaps
[0].mmap
);
472 dpy_gfx_replace_surface(dpy
->con
, dpy
->region
.surface
);
475 /* full screen update */
476 dpy_gfx_update(dpy
->con
, 0, 0,
477 surface_width(dpy
->region
.surface
),
478 surface_height(dpy
->region
.surface
));
482 vfio_region_exit(&dpy
->region
.buffer
);
483 vfio_region_finalize(&dpy
->region
.buffer
);
486 static const GraphicHwOps vfio_display_region_ops
= {
487 .gfx_update
= vfio_display_region_update
,
490 static bool vfio_display_region_init(VFIOPCIDevice
*vdev
, Error
**errp
)
492 vdev
->dpy
= g_new0(VFIODisplay
, 1);
493 vdev
->dpy
->con
= graphic_console_init(DEVICE(vdev
), 0,
494 &vfio_display_region_ops
,
496 if (vdev
->enable_ramfb
) {
497 vdev
->dpy
->ramfb
= ramfb_setup(errp
);
498 if (!vdev
->dpy
->ramfb
) {
505 static void vfio_display_region_exit(VFIODisplay
*dpy
)
507 if (!dpy
->region
.buffer
.size
) {
511 vfio_region_exit(&dpy
->region
.buffer
);
512 vfio_region_finalize(&dpy
->region
.buffer
);
515 /* ---------------------------------------------------------------------- */
517 bool vfio_display_probe(VFIOPCIDevice
*vdev
, Error
**errp
)
519 struct vfio_device_gfx_plane_info probe
;
522 memset(&probe
, 0, sizeof(probe
));
523 probe
.argsz
= sizeof(probe
);
524 probe
.flags
= VFIO_GFX_PLANE_TYPE_PROBE
| VFIO_GFX_PLANE_TYPE_DMABUF
;
525 ret
= ioctl(vdev
->vbasedev
.fd
, VFIO_DEVICE_QUERY_GFX_PLANE
, &probe
);
527 return vfio_display_dmabuf_init(vdev
, errp
);
530 memset(&probe
, 0, sizeof(probe
));
531 probe
.argsz
= sizeof(probe
);
532 probe
.flags
= VFIO_GFX_PLANE_TYPE_PROBE
| VFIO_GFX_PLANE_TYPE_REGION
;
533 ret
= ioctl(vdev
->vbasedev
.fd
, VFIO_DEVICE_QUERY_GFX_PLANE
, &probe
);
535 return vfio_display_region_init(vdev
, errp
);
538 if (vdev
->display
== ON_OFF_AUTO_AUTO
) {
539 /* not an error in automatic mode */
543 error_setg(errp
, "vfio: device doesn't support any (known) display method");
547 void vfio_display_finalize(VFIOPCIDevice
*vdev
)
553 graphic_console_close(vdev
->dpy
->con
);
554 vfio_display_dmabuf_exit(vdev
->dpy
);
555 vfio_display_region_exit(vdev
->dpy
);
556 vfio_display_edid_exit(vdev
->dpy
);
560 static bool migrate_needed(void *opaque
)
562 VFIODisplay
*dpy
= opaque
;
563 bool ramfb_exists
= dpy
->ramfb
!= NULL
;
565 /* see vfio_display_migration_needed() */
566 assert(ramfb_exists
);
570 const VMStateDescription vfio_display_vmstate
= {
571 .name
= "VFIODisplay",
573 .minimum_version_id
= 1,
574 .needed
= migrate_needed
,
575 .fields
= (const VMStateField
[]) {
576 VMSTATE_STRUCT_POINTER(ramfb
, VFIODisplay
, ramfb_vmstate
, RAMFBState
),
577 VMSTATE_END_OF_LIST(),