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