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 = qemu_dmabuf_new(plane.width, plane.height, 245 plane.stride, 0, 0, plane.width, 246 plane.height, plane.drm_format, 247 plane.drm_format_mod, fd, false, false); 248 249 if (plane_type == DRM_PLANE_TYPE_CURSOR) { 250 vfio_display_update_cursor(dmabuf, &plane); 251 } 252 253 QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next); 254 return dmabuf; 255 } 256 257 static void vfio_display_free_one_dmabuf(VFIODisplay *dpy, VFIODMABuf *dmabuf) 258 { 259 QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); 260 261 qemu_dmabuf_close(dmabuf->buf); 262 dpy_gl_release_dmabuf(dpy->con, dmabuf->buf); 263 g_clear_pointer(&dmabuf->buf, qemu_dmabuf_free); 264 g_free(dmabuf); 265 } 266 267 static 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 283 static void vfio_display_dmabuf_update(void *opaque) 284 { 285 VFIOPCIDevice *vdev = opaque; 286 VFIODisplay *dpy = vdev->dpy; 287 VFIODMABuf *primary, *cursor; 288 uint32_t width, height; 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 width = qemu_dmabuf_get_width(primary->buf); 300 height = qemu_dmabuf_get_height(primary->buf); 301 302 if (dpy->dmabuf.primary != primary) { 303 dpy->dmabuf.primary = primary; 304 qemu_console_resize(dpy->con, width, height); 305 dpy_gl_scanout_dmabuf(dpy->con, primary->buf); 306 free_bufs = true; 307 } 308 309 cursor = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_CURSOR); 310 if (dpy->dmabuf.cursor != cursor) { 311 dpy->dmabuf.cursor = cursor; 312 new_cursor = true; 313 free_bufs = true; 314 } 315 316 if (cursor && (new_cursor || cursor->hot_updates)) { 317 bool have_hot = (cursor->hot_x != 0xffffffff && 318 cursor->hot_y != 0xffffffff); 319 dpy_gl_cursor_dmabuf(dpy->con, cursor->buf, have_hot, 320 cursor->hot_x, cursor->hot_y); 321 cursor->hot_updates = 0; 322 } else if (!cursor && new_cursor) { 323 dpy_gl_cursor_dmabuf(dpy->con, NULL, false, 0, 0); 324 } 325 326 if (cursor && cursor->pos_updates) { 327 dpy_gl_cursor_position(dpy->con, 328 cursor->pos_x, 329 cursor->pos_y); 330 cursor->pos_updates = 0; 331 } 332 333 dpy_gl_update(dpy->con, 0, 0, width, height); 334 335 if (free_bufs) { 336 vfio_display_free_dmabufs(vdev); 337 } 338 } 339 340 static int vfio_display_get_flags(void *opaque) 341 { 342 return GRAPHIC_FLAGS_GL | GRAPHIC_FLAGS_DMABUF; 343 } 344 345 static const GraphicHwOps vfio_display_dmabuf_ops = { 346 .get_flags = vfio_display_get_flags, 347 .gfx_update = vfio_display_dmabuf_update, 348 .ui_info = vfio_display_edid_ui_info, 349 }; 350 351 static bool vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) 352 { 353 if (!display_opengl) { 354 error_setg(errp, "vfio-display-dmabuf: opengl not available"); 355 return false; 356 } 357 358 vdev->dpy = g_new0(VFIODisplay, 1); 359 vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, 360 &vfio_display_dmabuf_ops, 361 vdev); 362 if (vdev->enable_ramfb) { 363 vdev->dpy->ramfb = ramfb_setup(errp); 364 if (!vdev->dpy->ramfb) { 365 return false; 366 } 367 } 368 vfio_display_edid_init(vdev); 369 return true; 370 } 371 372 static void vfio_display_dmabuf_exit(VFIODisplay *dpy) 373 { 374 VFIODMABuf *dmabuf; 375 376 if (QTAILQ_EMPTY(&dpy->dmabuf.bufs)) { 377 return; 378 } 379 380 while ((dmabuf = QTAILQ_FIRST(&dpy->dmabuf.bufs)) != NULL) { 381 vfio_display_free_one_dmabuf(dpy, dmabuf); 382 } 383 } 384 385 /* ---------------------------------------------------------------------- */ 386 void vfio_display_reset(VFIOPCIDevice *vdev) 387 { 388 if (!vdev || !vdev->dpy || !vdev->dpy->con || 389 !vdev->dpy->dmabuf.primary) { 390 return; 391 } 392 393 dpy_gl_scanout_disable(vdev->dpy->con); 394 vfio_display_dmabuf_exit(vdev->dpy); 395 dpy_gfx_update_full(vdev->dpy->con); 396 } 397 398 static void vfio_display_region_update(void *opaque) 399 { 400 VFIOPCIDevice *vdev = opaque; 401 VFIODisplay *dpy = vdev->dpy; 402 struct vfio_device_gfx_plane_info plane = { 403 .argsz = sizeof(plane), 404 .flags = VFIO_GFX_PLANE_TYPE_REGION 405 }; 406 pixman_format_code_t format; 407 int ret; 408 409 ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); 410 if (ret < 0) { 411 error_report("ioctl VFIO_DEVICE_QUERY_GFX_PLANE: %s", 412 strerror(errno)); 413 return; 414 } 415 if (!plane.drm_format || !plane.size) { 416 if (dpy->ramfb) { 417 ramfb_display_update(dpy->con, dpy->ramfb); 418 dpy->region.surface = NULL; 419 } 420 return; 421 } 422 format = qemu_drm_format_to_pixman(plane.drm_format); 423 if (!format) { 424 return; 425 } 426 427 if (dpy->region.buffer.size && 428 dpy->region.buffer.nr != plane.region_index) { 429 /* region changed */ 430 vfio_region_exit(&dpy->region.buffer); 431 vfio_region_finalize(&dpy->region.buffer); 432 dpy->region.surface = NULL; 433 } 434 435 if (dpy->region.surface && 436 (surface_width(dpy->region.surface) != plane.width || 437 surface_height(dpy->region.surface) != plane.height || 438 surface_format(dpy->region.surface) != format)) { 439 /* size changed */ 440 dpy->region.surface = NULL; 441 } 442 443 if (!dpy->region.buffer.size) { 444 /* mmap region */ 445 ret = vfio_region_setup(OBJECT(vdev), &vdev->vbasedev, 446 &dpy->region.buffer, 447 plane.region_index, 448 "display"); 449 if (ret != 0) { 450 error_report("%s: vfio_region_setup(%d): %s", 451 __func__, plane.region_index, strerror(-ret)); 452 goto err; 453 } 454 ret = vfio_region_mmap(&dpy->region.buffer); 455 if (ret != 0) { 456 error_report("%s: vfio_region_mmap(%d): %s", __func__, 457 plane.region_index, strerror(-ret)); 458 goto err; 459 } 460 assert(dpy->region.buffer.mmaps[0].mmap != NULL); 461 } 462 463 if (dpy->region.surface == NULL) { 464 /* create surface */ 465 dpy->region.surface = qemu_create_displaysurface_from 466 (plane.width, plane.height, format, 467 plane.stride, dpy->region.buffer.mmaps[0].mmap); 468 dpy_gfx_replace_surface(dpy->con, dpy->region.surface); 469 } 470 471 /* full screen update */ 472 dpy_gfx_update(dpy->con, 0, 0, 473 surface_width(dpy->region.surface), 474 surface_height(dpy->region.surface)); 475 return; 476 477 err: 478 vfio_region_exit(&dpy->region.buffer); 479 vfio_region_finalize(&dpy->region.buffer); 480 } 481 482 static const GraphicHwOps vfio_display_region_ops = { 483 .gfx_update = vfio_display_region_update, 484 }; 485 486 static bool vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) 487 { 488 vdev->dpy = g_new0(VFIODisplay, 1); 489 vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, 490 &vfio_display_region_ops, 491 vdev); 492 if (vdev->enable_ramfb) { 493 vdev->dpy->ramfb = ramfb_setup(errp); 494 if (!vdev->dpy->ramfb) { 495 return false; 496 } 497 } 498 return true; 499 } 500 501 static void vfio_display_region_exit(VFIODisplay *dpy) 502 { 503 if (!dpy->region.buffer.size) { 504 return; 505 } 506 507 vfio_region_exit(&dpy->region.buffer); 508 vfio_region_finalize(&dpy->region.buffer); 509 } 510 511 /* ---------------------------------------------------------------------- */ 512 513 bool vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) 514 { 515 struct vfio_device_gfx_plane_info probe; 516 int ret; 517 518 memset(&probe, 0, sizeof(probe)); 519 probe.argsz = sizeof(probe); 520 probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_DMABUF; 521 ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); 522 if (ret == 0) { 523 return vfio_display_dmabuf_init(vdev, errp); 524 } 525 526 memset(&probe, 0, sizeof(probe)); 527 probe.argsz = sizeof(probe); 528 probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_REGION; 529 ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); 530 if (ret == 0) { 531 return vfio_display_region_init(vdev, errp); 532 } 533 534 if (vdev->display == ON_OFF_AUTO_AUTO) { 535 /* not an error in automatic mode */ 536 return true; 537 } 538 539 error_setg(errp, "vfio: device doesn't support any (known) display method"); 540 return false; 541 } 542 543 void vfio_display_finalize(VFIOPCIDevice *vdev) 544 { 545 if (!vdev->dpy) { 546 return; 547 } 548 549 graphic_console_close(vdev->dpy->con); 550 vfio_display_dmabuf_exit(vdev->dpy); 551 vfio_display_region_exit(vdev->dpy); 552 vfio_display_edid_exit(vdev->dpy); 553 g_free(vdev->dpy); 554 } 555 556 static bool migrate_needed(void *opaque) 557 { 558 VFIODisplay *dpy = opaque; 559 bool ramfb_exists = dpy->ramfb != NULL; 560 561 /* see vfio_display_migration_needed() */ 562 assert(ramfb_exists); 563 return ramfb_exists; 564 } 565 566 const VMStateDescription vfio_display_vmstate = { 567 .name = "VFIODisplay", 568 .version_id = 1, 569 .minimum_version_id = 1, 570 .needed = migrate_needed, 571 .fields = (const VMStateField[]) { 572 VMSTATE_STRUCT_POINTER(ramfb, VFIODisplay, ramfb_vmstate, RAMFBState), 573 VMSTATE_END_OF_LIST(), 574 } 575 }; 576