13
#include "qemu/osdep.h"
14
#include <linux/vfio.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,
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);
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) {
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);
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);
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);
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) {
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;
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(),