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