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