qemu

Форк
0
/
display.c 
579 строк · 16.7 Кб
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 bool vfio_display_edid_init(VFIOPCIDevice *vdev, Error **errp)
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
        /* Failed to get GFX edid info, allow to go through without edid. */
139
        return true;
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 true;
172

173
err:
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;
180
    return false;
181
}
182

183
static void vfio_display_edid_exit(VFIODisplay *dpy)
184
{
185
    if (!dpy->edid_regs) {
186
        return;
187
    }
188

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);
193
}
194

195
static void vfio_display_update_cursor(VFIODMABuf *dmabuf,
196
                                       struct vfio_device_gfx_plane_info *plane)
197
{
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++;
202
    }
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++;
207
    }
208
}
209

210
static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev,
211
                                           uint32_t plane_type)
212
{
213
    VFIODisplay *dpy = vdev->dpy;
214
    struct vfio_device_gfx_plane_info plane;
215
    VFIODMABuf *dmabuf;
216
    int fd, ret;
217

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);
223
    if (ret < 0) {
224
        return NULL;
225
    }
226
    if (!plane.drm_format || !plane.size) {
227
        return NULL;
228
    }
229

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);
237
            }
238
            return dmabuf;
239
        }
240
    }
241

242
    fd = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_GFX_DMABUF, &plane.dmabuf_id);
243
    if (fd < 0) {
244
        return NULL;
245
    }
246

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);
253

254
    if (plane_type == DRM_PLANE_TYPE_CURSOR) {
255
        vfio_display_update_cursor(dmabuf, &plane);
256
    }
257

258
    QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next);
259
    return dmabuf;
260
}
261

262
static void vfio_display_free_one_dmabuf(VFIODisplay *dpy, VFIODMABuf *dmabuf)
263
{
264
    QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next);
265

266
    qemu_dmabuf_close(dmabuf->buf);
267
    dpy_gl_release_dmabuf(dpy->con, dmabuf->buf);
268
    g_clear_pointer(&dmabuf->buf, qemu_dmabuf_free);
269
    g_free(dmabuf);
270
}
271

272
static void vfio_display_free_dmabufs(VFIOPCIDevice *vdev)
273
{
274
    VFIODisplay *dpy = vdev->dpy;
275
    VFIODMABuf *dmabuf, *tmp;
276
    uint32_t keep = 5;
277

278
    QTAILQ_FOREACH_SAFE(dmabuf, &dpy->dmabuf.bufs, next, tmp) {
279
        if (keep > 0) {
280
            keep--;
281
            continue;
282
        }
283
        assert(dmabuf != dpy->dmabuf.primary);
284
        vfio_display_free_one_dmabuf(dpy, dmabuf);
285
    }
286
}
287

288
static void vfio_display_dmabuf_update(void *opaque)
289
{
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;
295

296
    primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY);
297
    if (primary == NULL) {
298
        if (dpy->ramfb) {
299
            ramfb_display_update(dpy->con, dpy->ramfb);
300
        }
301
        return;
302
    }
303

304
    width = qemu_dmabuf_get_width(primary->buf);
305
    height = qemu_dmabuf_get_height(primary->buf);
306

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);
311
        free_bufs = true;
312
    }
313

314
    cursor = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_CURSOR);
315
    if (dpy->dmabuf.cursor != cursor) {
316
        dpy->dmabuf.cursor = cursor;
317
        new_cursor = true;
318
        free_bufs = true;
319
    }
320

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);
329
    }
330

331
    if (cursor && cursor->pos_updates) {
332
        dpy_gl_cursor_position(dpy->con,
333
                               cursor->pos_x,
334
                               cursor->pos_y);
335
        cursor->pos_updates = 0;
336
    }
337

338
    dpy_gl_update(dpy->con, 0, 0, width, height);
339

340
    if (free_bufs) {
341
        vfio_display_free_dmabufs(vdev);
342
    }
343
}
344

345
static int vfio_display_get_flags(void *opaque)
346
{
347
    return GRAPHIC_FLAGS_GL | GRAPHIC_FLAGS_DMABUF;
348
}
349

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,
354
};
355

356
static bool vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp)
357
{
358
    if (!display_opengl) {
359
        error_setg(errp, "vfio-display-dmabuf: opengl not available");
360
        return false;
361
    }
362

363
    vdev->dpy = g_new0(VFIODisplay, 1);
364
    vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0,
365
                                          &vfio_display_dmabuf_ops,
366
                                          vdev);
367
    if (vdev->enable_ramfb) {
368
        vdev->dpy->ramfb = ramfb_setup(errp);
369
        if (!vdev->dpy->ramfb) {
370
            return false;
371
        }
372
    }
373
    return vfio_display_edid_init(vdev, errp);
374
}
375

376
static void vfio_display_dmabuf_exit(VFIODisplay *dpy)
377
{
378
    VFIODMABuf *dmabuf;
379

380
    if (QTAILQ_EMPTY(&dpy->dmabuf.bufs)) {
381
        return;
382
    }
383

384
    while ((dmabuf = QTAILQ_FIRST(&dpy->dmabuf.bufs)) != NULL) {
385
        vfio_display_free_one_dmabuf(dpy, dmabuf);
386
    }
387
}
388

389
/* ---------------------------------------------------------------------- */
390
void vfio_display_reset(VFIOPCIDevice *vdev)
391
{
392
    if (!vdev || !vdev->dpy || !vdev->dpy->con ||
393
        !vdev->dpy->dmabuf.primary) {
394
        return;
395
    }
396

397
    dpy_gl_scanout_disable(vdev->dpy->con);
398
    vfio_display_dmabuf_exit(vdev->dpy);
399
    dpy_gfx_update_full(vdev->dpy->con);
400
}
401

402
static void vfio_display_region_update(void *opaque)
403
{
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
409
    };
410
    pixman_format_code_t format;
411
    int ret;
412

413
    ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane);
414
    if (ret < 0) {
415
        error_report("ioctl VFIO_DEVICE_QUERY_GFX_PLANE: %s",
416
                     strerror(errno));
417
        return;
418
    }
419
    if (!plane.drm_format || !plane.size) {
420
        if (dpy->ramfb) {
421
            ramfb_display_update(dpy->con, dpy->ramfb);
422
            dpy->region.surface = NULL;
423
        }
424
        return;
425
    }
426
    format = qemu_drm_format_to_pixman(plane.drm_format);
427
    if (!format) {
428
        return;
429
    }
430

431
    if (dpy->region.buffer.size &&
432
        dpy->region.buffer.nr != plane.region_index) {
433
        /* region changed */
434
        vfio_region_exit(&dpy->region.buffer);
435
        vfio_region_finalize(&dpy->region.buffer);
436
        dpy->region.surface = NULL;
437
    }
438

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)) {
443
        /* size changed */
444
        dpy->region.surface = NULL;
445
    }
446

447
    if (!dpy->region.buffer.size) {
448
        /* mmap region */
449
        ret = vfio_region_setup(OBJECT(vdev), &vdev->vbasedev,
450
                                &dpy->region.buffer,
451
                                plane.region_index,
452
                                "display");
453
        if (ret != 0) {
454
            error_report("%s: vfio_region_setup(%d): %s",
455
                         __func__, plane.region_index, strerror(-ret));
456
            goto err;
457
        }
458
        ret = vfio_region_mmap(&dpy->region.buffer);
459
        if (ret != 0) {
460
            error_report("%s: vfio_region_mmap(%d): %s", __func__,
461
                         plane.region_index, strerror(-ret));
462
            goto err;
463
        }
464
        assert(dpy->region.buffer.mmaps[0].mmap != NULL);
465
    }
466

467
    if (dpy->region.surface == NULL) {
468
        /* create surface */
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);
473
    }
474

475
    /* full screen update */
476
    dpy_gfx_update(dpy->con, 0, 0,
477
                   surface_width(dpy->region.surface),
478
                   surface_height(dpy->region.surface));
479
    return;
480

481
err:
482
    vfio_region_exit(&dpy->region.buffer);
483
    vfio_region_finalize(&dpy->region.buffer);
484
}
485

486
static const GraphicHwOps vfio_display_region_ops = {
487
    .gfx_update = vfio_display_region_update,
488
};
489

490
static bool vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp)
491
{
492
    vdev->dpy = g_new0(VFIODisplay, 1);
493
    vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0,
494
                                          &vfio_display_region_ops,
495
                                          vdev);
496
    if (vdev->enable_ramfb) {
497
        vdev->dpy->ramfb = ramfb_setup(errp);
498
        if (!vdev->dpy->ramfb) {
499
            return false;
500
        }
501
    }
502
    return true;
503
}
504

505
static void vfio_display_region_exit(VFIODisplay *dpy)
506
{
507
    if (!dpy->region.buffer.size) {
508
        return;
509
    }
510

511
    vfio_region_exit(&dpy->region.buffer);
512
    vfio_region_finalize(&dpy->region.buffer);
513
}
514

515
/* ---------------------------------------------------------------------- */
516

517
bool vfio_display_probe(VFIOPCIDevice *vdev, Error **errp)
518
{
519
    struct vfio_device_gfx_plane_info probe;
520
    int ret;
521

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);
526
    if (ret == 0) {
527
        return vfio_display_dmabuf_init(vdev, errp);
528
    }
529

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);
534
    if (ret == 0) {
535
        return vfio_display_region_init(vdev, errp);
536
    }
537

538
    if (vdev->display == ON_OFF_AUTO_AUTO) {
539
        /* not an error in automatic mode */
540
        return true;
541
    }
542

543
    error_setg(errp, "vfio: device doesn't support any (known) display method");
544
    return false;
545
}
546

547
void vfio_display_finalize(VFIOPCIDevice *vdev)
548
{
549
    if (!vdev->dpy) {
550
        return;
551
    }
552

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);
557
    g_free(vdev->dpy);
558
}
559

560
static bool migrate_needed(void *opaque)
561
{
562
    VFIODisplay *dpy = opaque;
563
    bool ramfb_exists = dpy->ramfb != NULL;
564

565
    /* see vfio_display_migration_needed() */
566
    assert(ramfb_exists);
567
    return ramfb_exists;
568
}
569

570
const VMStateDescription vfio_display_vmstate = {
571
    .name = "VFIODisplay",
572
    .version_id = 1,
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(),
578
    }
579
};
580

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.