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 "sysemu/sysemu.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 int 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 0; 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 return 0; 127 } 128 129 static void vfio_display_edid_init(VFIOPCIDevice *vdev) 130 { 131 VFIODisplay *dpy = vdev->dpy; 132 int fd = vdev->vbasedev.fd; 133 int ret; 134 135 ret = vfio_get_dev_region_info(&vdev->vbasedev, 136 VFIO_REGION_TYPE_GFX, 137 VFIO_REGION_SUBTYPE_GFX_EDID, 138 &dpy->edid_info); 139 if (ret) { 140 return; 141 } 142 143 trace_vfio_display_edid_available(); 144 dpy->edid_regs = g_new0(struct vfio_region_gfx_edid, 1); 145 if (pread_field(fd, dpy->edid_info, dpy->edid_regs, edid_offset)) { 146 goto err; 147 } 148 if (pread_field(fd, dpy->edid_info, dpy->edid_regs, edid_max_size)) { 149 goto err; 150 } 151 if (pread_field(fd, dpy->edid_info, dpy->edid_regs, max_xres)) { 152 goto err; 153 } 154 if (pread_field(fd, dpy->edid_info, dpy->edid_regs, max_yres)) { 155 goto err; 156 } 157 158 dpy->edid_blob = g_malloc0(dpy->edid_regs->edid_max_size); 159 160 /* if xres + yres properties are unset use the maximum resolution */ 161 if (!vdev->display_xres) { 162 vdev->display_xres = dpy->edid_regs->max_xres; 163 } 164 if (!vdev->display_yres) { 165 vdev->display_yres = dpy->edid_regs->max_yres; 166 } 167 168 dpy->edid_link_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 169 vfio_display_edid_link_up, vdev); 170 171 vfio_display_edid_update(vdev, true, 0, 0); 172 return; 173 174 err: 175 trace_vfio_display_edid_write_error(); 176 g_free(dpy->edid_regs); 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_regs); 188 g_free(dpy->edid_blob); 189 timer_del(dpy->edid_link_timer); 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.width = plane.width; 248 dmabuf->buf.height = plane.height; 249 dmabuf->buf.stride = plane.stride; 250 dmabuf->buf.fourcc = plane.drm_format; 251 dmabuf->buf.fd = fd; 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 dpy_gl_release_dmabuf(dpy->con, &dmabuf->buf); 264 close(dmabuf->buf.fd); 265 g_free(dmabuf); 266 } 267 268 static void vfio_display_free_dmabufs(VFIOPCIDevice *vdev) 269 { 270 VFIODisplay *dpy = vdev->dpy; 271 VFIODMABuf *dmabuf, *tmp; 272 uint32_t keep = 5; 273 274 QTAILQ_FOREACH_SAFE(dmabuf, &dpy->dmabuf.bufs, next, tmp) { 275 if (keep > 0) { 276 keep--; 277 continue; 278 } 279 assert(dmabuf != dpy->dmabuf.primary); 280 vfio_display_free_one_dmabuf(dpy, dmabuf); 281 } 282 } 283 284 static void vfio_display_dmabuf_update(void *opaque) 285 { 286 VFIOPCIDevice *vdev = opaque; 287 VFIODisplay *dpy = vdev->dpy; 288 VFIODMABuf *primary, *cursor; 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 if (dpy->dmabuf.primary != primary) { 300 dpy->dmabuf.primary = primary; 301 qemu_console_resize(dpy->con, 302 primary->buf.width, primary->buf.height); 303 dpy_gl_scanout_dmabuf(dpy->con, &primary->buf); 304 free_bufs = true; 305 } 306 307 cursor = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_CURSOR); 308 if (dpy->dmabuf.cursor != cursor) { 309 dpy->dmabuf.cursor = cursor; 310 new_cursor = true; 311 free_bufs = true; 312 } 313 314 if (cursor && (new_cursor || cursor->hot_updates)) { 315 bool have_hot = (cursor->hot_x != 0xffffffff && 316 cursor->hot_y != 0xffffffff); 317 dpy_gl_cursor_dmabuf(dpy->con, &cursor->buf, have_hot, 318 cursor->hot_x, cursor->hot_y); 319 cursor->hot_updates = 0; 320 } else if (!cursor && new_cursor) { 321 dpy_gl_cursor_dmabuf(dpy->con, NULL, false, 0, 0); 322 } 323 324 if (cursor && cursor->pos_updates) { 325 dpy_gl_cursor_position(dpy->con, 326 cursor->pos_x, 327 cursor->pos_y); 328 cursor->pos_updates = 0; 329 } 330 331 dpy_gl_update(dpy->con, 0, 0, primary->buf.width, primary->buf.height); 332 333 if (free_bufs) { 334 vfio_display_free_dmabufs(vdev); 335 } 336 } 337 338 static const GraphicHwOps vfio_display_dmabuf_ops = { 339 .gfx_update = vfio_display_dmabuf_update, 340 .ui_info = vfio_display_edid_ui_info, 341 }; 342 343 static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) 344 { 345 if (!display_opengl) { 346 error_setg(errp, "vfio-display-dmabuf: opengl not available"); 347 return -1; 348 } 349 350 vdev->dpy = g_new0(VFIODisplay, 1); 351 vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, 352 &vfio_display_dmabuf_ops, 353 vdev); 354 if (vdev->enable_ramfb) { 355 vdev->dpy->ramfb = ramfb_setup(errp); 356 } 357 vfio_display_edid_init(vdev); 358 return 0; 359 } 360 361 static void vfio_display_dmabuf_exit(VFIODisplay *dpy) 362 { 363 VFIODMABuf *dmabuf; 364 365 if (QTAILQ_EMPTY(&dpy->dmabuf.bufs)) { 366 return; 367 } 368 369 while ((dmabuf = QTAILQ_FIRST(&dpy->dmabuf.bufs)) != NULL) { 370 vfio_display_free_one_dmabuf(dpy, dmabuf); 371 } 372 } 373 374 /* ---------------------------------------------------------------------- */ 375 void vfio_display_reset(VFIOPCIDevice *vdev) 376 { 377 if (!vdev || !vdev->dpy || !vdev->dpy->con || 378 !vdev->dpy->dmabuf.primary) { 379 return; 380 } 381 382 dpy_gl_scanout_disable(vdev->dpy->con); 383 vfio_display_dmabuf_exit(vdev->dpy); 384 dpy_gfx_update_full(vdev->dpy->con); 385 } 386 387 static void vfio_display_region_update(void *opaque) 388 { 389 VFIOPCIDevice *vdev = opaque; 390 VFIODisplay *dpy = vdev->dpy; 391 struct vfio_device_gfx_plane_info plane = { 392 .argsz = sizeof(plane), 393 .flags = VFIO_GFX_PLANE_TYPE_REGION 394 }; 395 pixman_format_code_t format; 396 int ret; 397 398 ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); 399 if (ret < 0) { 400 error_report("ioctl VFIO_DEVICE_QUERY_GFX_PLANE: %s", 401 strerror(errno)); 402 return; 403 } 404 if (!plane.drm_format || !plane.size) { 405 if (dpy->ramfb) { 406 ramfb_display_update(dpy->con, dpy->ramfb); 407 } 408 return; 409 } 410 format = qemu_drm_format_to_pixman(plane.drm_format); 411 if (!format) { 412 return; 413 } 414 415 if (dpy->region.buffer.size && 416 dpy->region.buffer.nr != plane.region_index) { 417 /* region changed */ 418 vfio_region_exit(&dpy->region.buffer); 419 vfio_region_finalize(&dpy->region.buffer); 420 dpy->region.surface = NULL; 421 } 422 423 if (dpy->region.surface && 424 (surface_width(dpy->region.surface) != plane.width || 425 surface_height(dpy->region.surface) != plane.height || 426 surface_format(dpy->region.surface) != format)) { 427 /* size changed */ 428 dpy->region.surface = NULL; 429 } 430 431 if (!dpy->region.buffer.size) { 432 /* mmap region */ 433 ret = vfio_region_setup(OBJECT(vdev), &vdev->vbasedev, 434 &dpy->region.buffer, 435 plane.region_index, 436 "display"); 437 if (ret != 0) { 438 error_report("%s: vfio_region_setup(%d): %s", 439 __func__, plane.region_index, strerror(-ret)); 440 goto err; 441 } 442 ret = vfio_region_mmap(&dpy->region.buffer); 443 if (ret != 0) { 444 error_report("%s: vfio_region_mmap(%d): %s", __func__, 445 plane.region_index, strerror(-ret)); 446 goto err; 447 } 448 assert(dpy->region.buffer.mmaps[0].mmap != NULL); 449 } 450 451 if (dpy->region.surface == NULL) { 452 /* create surface */ 453 dpy->region.surface = qemu_create_displaysurface_from 454 (plane.width, plane.height, format, 455 plane.stride, dpy->region.buffer.mmaps[0].mmap); 456 dpy_gfx_replace_surface(dpy->con, dpy->region.surface); 457 } 458 459 /* full screen update */ 460 dpy_gfx_update(dpy->con, 0, 0, 461 surface_width(dpy->region.surface), 462 surface_height(dpy->region.surface)); 463 return; 464 465 err: 466 vfio_region_exit(&dpy->region.buffer); 467 vfio_region_finalize(&dpy->region.buffer); 468 } 469 470 static const GraphicHwOps vfio_display_region_ops = { 471 .gfx_update = vfio_display_region_update, 472 }; 473 474 static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) 475 { 476 vdev->dpy = g_new0(VFIODisplay, 1); 477 vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, 478 &vfio_display_region_ops, 479 vdev); 480 if (vdev->enable_ramfb) { 481 vdev->dpy->ramfb = ramfb_setup(errp); 482 } 483 return 0; 484 } 485 486 static void vfio_display_region_exit(VFIODisplay *dpy) 487 { 488 if (!dpy->region.buffer.size) { 489 return; 490 } 491 492 vfio_region_exit(&dpy->region.buffer); 493 vfio_region_finalize(&dpy->region.buffer); 494 } 495 496 /* ---------------------------------------------------------------------- */ 497 498 int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) 499 { 500 struct vfio_device_gfx_plane_info probe; 501 int ret; 502 503 memset(&probe, 0, sizeof(probe)); 504 probe.argsz = sizeof(probe); 505 probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_DMABUF; 506 ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); 507 if (ret == 0) { 508 return vfio_display_dmabuf_init(vdev, errp); 509 } 510 511 memset(&probe, 0, sizeof(probe)); 512 probe.argsz = sizeof(probe); 513 probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_REGION; 514 ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); 515 if (ret == 0) { 516 return vfio_display_region_init(vdev, errp); 517 } 518 519 if (vdev->display == ON_OFF_AUTO_AUTO) { 520 /* not an error in automatic mode */ 521 return 0; 522 } 523 524 error_setg(errp, "vfio: device doesn't support any (known) display method"); 525 return -1; 526 } 527 528 void vfio_display_finalize(VFIOPCIDevice *vdev) 529 { 530 if (!vdev->dpy) { 531 return; 532 } 533 534 graphic_console_close(vdev->dpy->con); 535 vfio_display_dmabuf_exit(vdev->dpy); 536 vfio_display_region_exit(vdev->dpy); 537 vfio_display_edid_exit(vdev->dpy); 538 g_free(vdev->dpy); 539 } 540