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