1 /* 2 * Virtio vhost-user GPU Device 3 * 4 * Copyright Red Hat, Inc. 2013-2018 5 * 6 * Authors: 7 * Dave Airlie <airlied@redhat.com> 8 * Gerd Hoffmann <kraxel@redhat.com> 9 * Marc-André Lureau <marcandre.lureau@redhat.com> 10 * 11 * This work is licensed under the terms of the GNU GPL, version 2 or later. 12 * See the COPYING file in the top-level directory. 13 */ 14 #include "qemu/osdep.h" 15 #include "qemu/drm.h" 16 #include "qapi/error.h" 17 #include "qemu/sockets.h" 18 19 #include <pixman.h> 20 #include <glib-unix.h> 21 22 #include "vugpu.h" 23 #include "hw/virtio/virtio-gpu-bswap.h" 24 #include "hw/virtio/virtio-gpu-pixman.h" 25 #include "virgl.h" 26 #include "vugbm.h" 27 28 enum { 29 VHOST_USER_GPU_MAX_QUEUES = 2, 30 }; 31 32 struct virtio_gpu_simple_resource { 33 uint32_t resource_id; 34 uint32_t width; 35 uint32_t height; 36 uint32_t format; 37 struct iovec *iov; 38 unsigned int iov_cnt; 39 uint32_t scanout_bitmask; 40 pixman_image_t *image; 41 struct vugbm_buffer buffer; 42 QTAILQ_ENTRY(virtio_gpu_simple_resource) next; 43 }; 44 45 static gboolean opt_print_caps; 46 static int opt_fdnum = -1; 47 static char *opt_socket_path; 48 static char *opt_render_node; 49 static gboolean opt_virgl; 50 51 static void vg_handle_ctrl(VuDev *dev, int qidx); 52 53 static const char * 54 vg_cmd_to_string(int cmd) 55 { 56 #define CMD(cmd) [cmd] = #cmd 57 static const char *vg_cmd_str[] = { 58 CMD(VIRTIO_GPU_UNDEFINED), 59 60 /* 2d commands */ 61 CMD(VIRTIO_GPU_CMD_GET_DISPLAY_INFO), 62 CMD(VIRTIO_GPU_CMD_RESOURCE_CREATE_2D), 63 CMD(VIRTIO_GPU_CMD_RESOURCE_UNREF), 64 CMD(VIRTIO_GPU_CMD_SET_SCANOUT), 65 CMD(VIRTIO_GPU_CMD_RESOURCE_FLUSH), 66 CMD(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D), 67 CMD(VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING), 68 CMD(VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING), 69 CMD(VIRTIO_GPU_CMD_GET_CAPSET_INFO), 70 CMD(VIRTIO_GPU_CMD_GET_CAPSET), 71 72 /* 3d commands */ 73 CMD(VIRTIO_GPU_CMD_CTX_CREATE), 74 CMD(VIRTIO_GPU_CMD_CTX_DESTROY), 75 CMD(VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE), 76 CMD(VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE), 77 CMD(VIRTIO_GPU_CMD_RESOURCE_CREATE_3D), 78 CMD(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D), 79 CMD(VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D), 80 CMD(VIRTIO_GPU_CMD_SUBMIT_3D), 81 82 /* cursor commands */ 83 CMD(VIRTIO_GPU_CMD_UPDATE_CURSOR), 84 CMD(VIRTIO_GPU_CMD_MOVE_CURSOR), 85 }; 86 #undef REQ 87 88 if (cmd >= 0 && cmd < G_N_ELEMENTS(vg_cmd_str)) { 89 return vg_cmd_str[cmd]; 90 } else { 91 return "unknown"; 92 } 93 } 94 95 static int 96 vg_sock_fd_read(int sock, void *buf, ssize_t buflen) 97 { 98 int ret; 99 100 do { 101 ret = read(sock, buf, buflen); 102 } while (ret < 0 && (errno == EINTR || errno == EAGAIN)); 103 104 g_warn_if_fail(ret == buflen); 105 return ret; 106 } 107 108 static void 109 vg_sock_fd_close(VuGpu *g) 110 { 111 if (g->sock_fd >= 0) { 112 close(g->sock_fd); 113 g->sock_fd = -1; 114 } 115 } 116 117 static gboolean 118 source_wait_cb(gint fd, GIOCondition condition, gpointer user_data) 119 { 120 VuGpu *g = user_data; 121 122 if (!vg_recv_msg(g, VHOST_USER_GPU_DMABUF_UPDATE, 0, NULL)) { 123 return G_SOURCE_CONTINUE; 124 } 125 126 /* resume */ 127 g->wait_in = 0; 128 vg_handle_ctrl(&g->dev.parent, 0); 129 130 return G_SOURCE_REMOVE; 131 } 132 133 void 134 vg_wait_ok(VuGpu *g) 135 { 136 assert(g->wait_in == 0); 137 g->wait_in = g_unix_fd_add(g->sock_fd, G_IO_IN | G_IO_HUP, 138 source_wait_cb, g); 139 } 140 141 static int 142 vg_sock_fd_write(int sock, const void *buf, ssize_t buflen, int fd) 143 { 144 ssize_t ret; 145 struct iovec iov = { 146 .iov_base = (void *)buf, 147 .iov_len = buflen, 148 }; 149 struct msghdr msg = { 150 .msg_iov = &iov, 151 .msg_iovlen = 1, 152 }; 153 union { 154 struct cmsghdr cmsghdr; 155 char control[CMSG_SPACE(sizeof(int))]; 156 } cmsgu; 157 struct cmsghdr *cmsg; 158 159 if (fd != -1) { 160 msg.msg_control = cmsgu.control; 161 msg.msg_controllen = sizeof(cmsgu.control); 162 163 cmsg = CMSG_FIRSTHDR(&msg); 164 cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 165 cmsg->cmsg_level = SOL_SOCKET; 166 cmsg->cmsg_type = SCM_RIGHTS; 167 168 *((int *)CMSG_DATA(cmsg)) = fd; 169 } 170 171 do { 172 ret = sendmsg(sock, &msg, 0); 173 } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); 174 175 g_warn_if_fail(ret == buflen); 176 return ret; 177 } 178 179 void 180 vg_send_msg(VuGpu *vg, const VhostUserGpuMsg *msg, int fd) 181 { 182 if (vg_sock_fd_write(vg->sock_fd, msg, 183 VHOST_USER_GPU_HDR_SIZE + msg->size, fd) < 0) { 184 vg_sock_fd_close(vg); 185 } 186 } 187 188 bool 189 vg_recv_msg(VuGpu *g, uint32_t expect_req, uint32_t expect_size, 190 gpointer payload) 191 { 192 uint32_t req, flags, size; 193 194 if (vg_sock_fd_read(g->sock_fd, &req, sizeof(req)) < 0 || 195 vg_sock_fd_read(g->sock_fd, &flags, sizeof(flags)) < 0 || 196 vg_sock_fd_read(g->sock_fd, &size, sizeof(size)) < 0) { 197 goto err; 198 } 199 200 g_return_val_if_fail(req == expect_req, false); 201 g_return_val_if_fail(flags & VHOST_USER_GPU_MSG_FLAG_REPLY, false); 202 g_return_val_if_fail(size == expect_size, false); 203 204 if (size && vg_sock_fd_read(g->sock_fd, payload, size) != size) { 205 goto err; 206 } 207 208 return true; 209 210 err: 211 vg_sock_fd_close(g); 212 return false; 213 } 214 215 static struct virtio_gpu_simple_resource * 216 virtio_gpu_find_resource(VuGpu *g, uint32_t resource_id) 217 { 218 struct virtio_gpu_simple_resource *res; 219 220 QTAILQ_FOREACH(res, &g->reslist, next) { 221 if (res->resource_id == resource_id) { 222 return res; 223 } 224 } 225 return NULL; 226 } 227 228 void 229 vg_ctrl_response(VuGpu *g, 230 struct virtio_gpu_ctrl_command *cmd, 231 struct virtio_gpu_ctrl_hdr *resp, 232 size_t resp_len) 233 { 234 size_t s; 235 236 if (cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE) { 237 resp->flags |= VIRTIO_GPU_FLAG_FENCE; 238 resp->fence_id = cmd->cmd_hdr.fence_id; 239 resp->ctx_id = cmd->cmd_hdr.ctx_id; 240 } 241 virtio_gpu_ctrl_hdr_bswap(resp); 242 s = iov_from_buf(cmd->elem.in_sg, cmd->elem.in_num, 0, resp, resp_len); 243 if (s != resp_len) { 244 g_critical("%s: response size incorrect %zu vs %zu", 245 __func__, s, resp_len); 246 } 247 vu_queue_push(&g->dev.parent, cmd->vq, &cmd->elem, s); 248 vu_queue_notify(&g->dev.parent, cmd->vq); 249 cmd->state = VG_CMD_STATE_FINISHED; 250 } 251 252 void 253 vg_ctrl_response_nodata(VuGpu *g, 254 struct virtio_gpu_ctrl_command *cmd, 255 enum virtio_gpu_ctrl_type type) 256 { 257 struct virtio_gpu_ctrl_hdr resp = { 258 .type = type, 259 }; 260 261 vg_ctrl_response(g, cmd, &resp, sizeof(resp)); 262 } 263 264 265 static gboolean 266 get_display_info_cb(gint fd, GIOCondition condition, gpointer user_data) 267 { 268 struct virtio_gpu_resp_display_info dpy_info = { {} }; 269 VuGpu *vg = user_data; 270 struct virtio_gpu_ctrl_command *cmd = QTAILQ_LAST(&vg->fenceq); 271 272 g_debug("disp info cb"); 273 assert(cmd->cmd_hdr.type == VIRTIO_GPU_CMD_GET_DISPLAY_INFO); 274 if (!vg_recv_msg(vg, VHOST_USER_GPU_GET_DISPLAY_INFO, 275 sizeof(dpy_info), &dpy_info)) { 276 return G_SOURCE_CONTINUE; 277 } 278 279 QTAILQ_REMOVE(&vg->fenceq, cmd, next); 280 vg_ctrl_response(vg, cmd, &dpy_info.hdr, sizeof(dpy_info)); 281 282 vg->wait_in = 0; 283 vg_handle_ctrl(&vg->dev.parent, 0); 284 285 return G_SOURCE_REMOVE; 286 } 287 288 void 289 vg_get_display_info(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd) 290 { 291 VhostUserGpuMsg msg = { 292 .request = VHOST_USER_GPU_GET_DISPLAY_INFO, 293 .size = 0, 294 }; 295 296 assert(vg->wait_in == 0); 297 298 vg_send_msg(vg, &msg, -1); 299 vg->wait_in = g_unix_fd_add(vg->sock_fd, G_IO_IN | G_IO_HUP, 300 get_display_info_cb, vg); 301 cmd->state = VG_CMD_STATE_PENDING; 302 } 303 304 static void 305 vg_resource_create_2d(VuGpu *g, 306 struct virtio_gpu_ctrl_command *cmd) 307 { 308 pixman_format_code_t pformat; 309 struct virtio_gpu_simple_resource *res; 310 struct virtio_gpu_resource_create_2d c2d; 311 312 VUGPU_FILL_CMD(c2d); 313 virtio_gpu_bswap_32(&c2d, sizeof(c2d)); 314 315 if (c2d.resource_id == 0) { 316 g_critical("%s: resource id 0 is not allowed", __func__); 317 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; 318 return; 319 } 320 321 res = virtio_gpu_find_resource(g, c2d.resource_id); 322 if (res) { 323 g_critical("%s: resource already exists %d", __func__, c2d.resource_id); 324 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; 325 return; 326 } 327 328 res = g_new0(struct virtio_gpu_simple_resource, 1); 329 res->width = c2d.width; 330 res->height = c2d.height; 331 res->format = c2d.format; 332 res->resource_id = c2d.resource_id; 333 334 pformat = virtio_gpu_get_pixman_format(c2d.format); 335 if (!pformat) { 336 g_critical("%s: host couldn't handle guest format %d", 337 __func__, c2d.format); 338 g_free(res); 339 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; 340 return; 341 } 342 vugbm_buffer_create(&res->buffer, &g->gdev, c2d.width, c2d.height); 343 res->image = pixman_image_create_bits(pformat, 344 c2d.width, 345 c2d.height, 346 (uint32_t *)res->buffer.mmap, 347 res->buffer.stride); 348 if (!res->image) { 349 g_critical("%s: resource creation failed %d %d %d", 350 __func__, c2d.resource_id, c2d.width, c2d.height); 351 g_free(res); 352 cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; 353 return; 354 } 355 356 QTAILQ_INSERT_HEAD(&g->reslist, res, next); 357 } 358 359 static void 360 vg_disable_scanout(VuGpu *g, int scanout_id) 361 { 362 struct virtio_gpu_scanout *scanout = &g->scanout[scanout_id]; 363 struct virtio_gpu_simple_resource *res; 364 365 if (scanout->resource_id == 0) { 366 return; 367 } 368 369 res = virtio_gpu_find_resource(g, scanout->resource_id); 370 if (res) { 371 res->scanout_bitmask &= ~(1 << scanout_id); 372 } 373 374 scanout->width = 0; 375 scanout->height = 0; 376 377 if (g->sock_fd >= 0) { 378 VhostUserGpuMsg msg = { 379 .request = VHOST_USER_GPU_SCANOUT, 380 .size = sizeof(VhostUserGpuScanout), 381 .payload.scanout.scanout_id = scanout_id, 382 }; 383 vg_send_msg(g, &msg, -1); 384 } 385 } 386 387 static void 388 vg_resource_destroy(VuGpu *g, 389 struct virtio_gpu_simple_resource *res) 390 { 391 int i; 392 393 if (res->scanout_bitmask) { 394 for (i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) { 395 if (res->scanout_bitmask & (1 << i)) { 396 vg_disable_scanout(g, i); 397 } 398 } 399 } 400 401 vugbm_buffer_destroy(&res->buffer); 402 pixman_image_unref(res->image); 403 QTAILQ_REMOVE(&g->reslist, res, next); 404 g_free(res); 405 } 406 407 static void 408 vg_resource_unref(VuGpu *g, 409 struct virtio_gpu_ctrl_command *cmd) 410 { 411 struct virtio_gpu_simple_resource *res; 412 struct virtio_gpu_resource_unref unref; 413 414 VUGPU_FILL_CMD(unref); 415 virtio_gpu_bswap_32(&unref, sizeof(unref)); 416 417 res = virtio_gpu_find_resource(g, unref.resource_id); 418 if (!res) { 419 g_critical("%s: illegal resource specified %d", 420 __func__, unref.resource_id); 421 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; 422 return; 423 } 424 vg_resource_destroy(g, res); 425 } 426 427 int 428 vg_create_mapping_iov(VuGpu *g, 429 struct virtio_gpu_resource_attach_backing *ab, 430 struct virtio_gpu_ctrl_command *cmd, 431 struct iovec **iov) 432 { 433 struct virtio_gpu_mem_entry *ents; 434 size_t esize, s; 435 int i; 436 437 if (ab->nr_entries > 16384) { 438 g_critical("%s: nr_entries is too big (%d > 16384)", 439 __func__, ab->nr_entries); 440 return -1; 441 } 442 443 esize = sizeof(*ents) * ab->nr_entries; 444 ents = g_malloc(esize); 445 s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 446 sizeof(*ab), ents, esize); 447 if (s != esize) { 448 g_critical("%s: command data size incorrect %zu vs %zu", 449 __func__, s, esize); 450 g_free(ents); 451 return -1; 452 } 453 454 *iov = g_malloc0(sizeof(struct iovec) * ab->nr_entries); 455 for (i = 0; i < ab->nr_entries; i++) { 456 uint64_t len = ents[i].length; 457 (*iov)[i].iov_len = ents[i].length; 458 (*iov)[i].iov_base = vu_gpa_to_va(&g->dev.parent, &len, ents[i].addr); 459 if (!(*iov)[i].iov_base || len != ents[i].length) { 460 g_critical("%s: resource %d element %d", 461 __func__, ab->resource_id, i); 462 g_free(*iov); 463 g_free(ents); 464 *iov = NULL; 465 return -1; 466 } 467 } 468 g_free(ents); 469 return 0; 470 } 471 472 static void 473 vg_resource_attach_backing(VuGpu *g, 474 struct virtio_gpu_ctrl_command *cmd) 475 { 476 struct virtio_gpu_simple_resource *res; 477 struct virtio_gpu_resource_attach_backing ab; 478 int ret; 479 480 VUGPU_FILL_CMD(ab); 481 virtio_gpu_bswap_32(&ab, sizeof(ab)); 482 483 res = virtio_gpu_find_resource(g, ab.resource_id); 484 if (!res) { 485 g_critical("%s: illegal resource specified %d", 486 __func__, ab.resource_id); 487 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; 488 return; 489 } 490 491 ret = vg_create_mapping_iov(g, &ab, cmd, &res->iov); 492 if (ret != 0) { 493 cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; 494 return; 495 } 496 497 res->iov_cnt = ab.nr_entries; 498 } 499 500 static void 501 vg_resource_detach_backing(VuGpu *g, 502 struct virtio_gpu_ctrl_command *cmd) 503 { 504 struct virtio_gpu_simple_resource *res; 505 struct virtio_gpu_resource_detach_backing detach; 506 507 VUGPU_FILL_CMD(detach); 508 virtio_gpu_bswap_32(&detach, sizeof(detach)); 509 510 res = virtio_gpu_find_resource(g, detach.resource_id); 511 if (!res || !res->iov) { 512 g_critical("%s: illegal resource specified %d", 513 __func__, detach.resource_id); 514 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; 515 return; 516 } 517 518 g_free(res->iov); 519 res->iov = NULL; 520 res->iov_cnt = 0; 521 } 522 523 static void 524 vg_transfer_to_host_2d(VuGpu *g, 525 struct virtio_gpu_ctrl_command *cmd) 526 { 527 struct virtio_gpu_simple_resource *res; 528 int h; 529 uint32_t src_offset, dst_offset, stride; 530 int bpp; 531 pixman_format_code_t format; 532 struct virtio_gpu_transfer_to_host_2d t2d; 533 534 VUGPU_FILL_CMD(t2d); 535 virtio_gpu_t2d_bswap(&t2d); 536 537 res = virtio_gpu_find_resource(g, t2d.resource_id); 538 if (!res || !res->iov) { 539 g_critical("%s: illegal resource specified %d", 540 __func__, t2d.resource_id); 541 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; 542 return; 543 } 544 545 if (t2d.r.x > res->width || 546 t2d.r.y > res->height || 547 t2d.r.width > res->width || 548 t2d.r.height > res->height || 549 t2d.r.x + t2d.r.width > res->width || 550 t2d.r.y + t2d.r.height > res->height) { 551 g_critical("%s: transfer bounds outside resource" 552 " bounds for resource %d: %d %d %d %d vs %d %d", 553 __func__, t2d.resource_id, t2d.r.x, t2d.r.y, 554 t2d.r.width, t2d.r.height, res->width, res->height); 555 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; 556 return; 557 } 558 559 format = pixman_image_get_format(res->image); 560 bpp = (PIXMAN_FORMAT_BPP(format) + 7) / 8; 561 stride = pixman_image_get_stride(res->image); 562 563 if (t2d.offset || t2d.r.x || t2d.r.y || 564 t2d.r.width != pixman_image_get_width(res->image)) { 565 void *img_data = pixman_image_get_data(res->image); 566 for (h = 0; h < t2d.r.height; h++) { 567 src_offset = t2d.offset + stride * h; 568 dst_offset = (t2d.r.y + h) * stride + (t2d.r.x * bpp); 569 570 iov_to_buf(res->iov, res->iov_cnt, src_offset, 571 img_data 572 + dst_offset, t2d.r.width * bpp); 573 } 574 } else { 575 iov_to_buf(res->iov, res->iov_cnt, 0, 576 pixman_image_get_data(res->image), 577 pixman_image_get_stride(res->image) 578 * pixman_image_get_height(res->image)); 579 } 580 } 581 582 static void 583 vg_set_scanout(VuGpu *g, 584 struct virtio_gpu_ctrl_command *cmd) 585 { 586 struct virtio_gpu_simple_resource *res, *ores; 587 struct virtio_gpu_scanout *scanout; 588 struct virtio_gpu_set_scanout ss; 589 int fd; 590 591 VUGPU_FILL_CMD(ss); 592 virtio_gpu_bswap_32(&ss, sizeof(ss)); 593 594 if (ss.scanout_id >= VIRTIO_GPU_MAX_SCANOUTS) { 595 g_critical("%s: illegal scanout id specified %d", 596 __func__, ss.scanout_id); 597 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; 598 return; 599 } 600 601 if (ss.resource_id == 0) { 602 vg_disable_scanout(g, ss.scanout_id); 603 return; 604 } 605 606 /* create a surface for this scanout */ 607 res = virtio_gpu_find_resource(g, ss.resource_id); 608 if (!res) { 609 g_critical("%s: illegal resource specified %d", 610 __func__, ss.resource_id); 611 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; 612 return; 613 } 614 615 if (ss.r.x > res->width || 616 ss.r.y > res->height || 617 ss.r.width > res->width || 618 ss.r.height > res->height || 619 ss.r.x + ss.r.width > res->width || 620 ss.r.y + ss.r.height > res->height) { 621 g_critical("%s: illegal scanout %d bounds for" 622 " resource %d, (%d,%d)+%d,%d vs %d %d", 623 __func__, ss.scanout_id, ss.resource_id, ss.r.x, ss.r.y, 624 ss.r.width, ss.r.height, res->width, res->height); 625 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; 626 return; 627 } 628 629 scanout = &g->scanout[ss.scanout_id]; 630 631 ores = virtio_gpu_find_resource(g, scanout->resource_id); 632 if (ores) { 633 ores->scanout_bitmask &= ~(1 << ss.scanout_id); 634 } 635 636 res->scanout_bitmask |= (1 << ss.scanout_id); 637 scanout->resource_id = ss.resource_id; 638 scanout->x = ss.r.x; 639 scanout->y = ss.r.y; 640 scanout->width = ss.r.width; 641 scanout->height = ss.r.height; 642 643 struct vugbm_buffer *buffer = &res->buffer; 644 645 if (vugbm_buffer_can_get_dmabuf_fd(buffer)) { 646 VhostUserGpuMsg msg = { 647 .request = VHOST_USER_GPU_DMABUF_SCANOUT, 648 .size = sizeof(VhostUserGpuDMABUFScanout), 649 .payload.dmabuf_scanout = (VhostUserGpuDMABUFScanout) { 650 .scanout_id = ss.scanout_id, 651 .x = ss.r.x, 652 .y = ss.r.y, 653 .width = ss.r.width, 654 .height = ss.r.height, 655 .fd_width = buffer->width, 656 .fd_height = buffer->height, 657 .fd_stride = buffer->stride, 658 .fd_drm_fourcc = buffer->format 659 } 660 }; 661 662 if (vugbm_buffer_get_dmabuf_fd(buffer, &fd)) { 663 vg_send_msg(g, &msg, fd); 664 close(fd); 665 } 666 } else { 667 VhostUserGpuMsg msg = { 668 .request = VHOST_USER_GPU_SCANOUT, 669 .size = sizeof(VhostUserGpuScanout), 670 .payload.scanout = (VhostUserGpuScanout) { 671 .scanout_id = ss.scanout_id, 672 .width = scanout->width, 673 .height = scanout->height 674 } 675 }; 676 vg_send_msg(g, &msg, -1); 677 } 678 } 679 680 static void 681 vg_resource_flush(VuGpu *g, 682 struct virtio_gpu_ctrl_command *cmd) 683 { 684 struct virtio_gpu_simple_resource *res; 685 struct virtio_gpu_resource_flush rf; 686 pixman_region16_t flush_region; 687 int i; 688 689 VUGPU_FILL_CMD(rf); 690 virtio_gpu_bswap_32(&rf, sizeof(rf)); 691 692 res = virtio_gpu_find_resource(g, rf.resource_id); 693 if (!res) { 694 g_critical("%s: illegal resource specified %d\n", 695 __func__, rf.resource_id); 696 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; 697 return; 698 } 699 700 if (rf.r.x > res->width || 701 rf.r.y > res->height || 702 rf.r.width > res->width || 703 rf.r.height > res->height || 704 rf.r.x + rf.r.width > res->width || 705 rf.r.y + rf.r.height > res->height) { 706 g_critical("%s: flush bounds outside resource" 707 " bounds for resource %d: %d %d %d %d vs %d %d\n", 708 __func__, rf.resource_id, rf.r.x, rf.r.y, 709 rf.r.width, rf.r.height, res->width, res->height); 710 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; 711 return; 712 } 713 714 pixman_region_init_rect(&flush_region, 715 rf.r.x, rf.r.y, rf.r.width, rf.r.height); 716 for (i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) { 717 struct virtio_gpu_scanout *scanout; 718 pixman_region16_t region, finalregion; 719 pixman_box16_t *extents; 720 721 if (!(res->scanout_bitmask & (1 << i))) { 722 continue; 723 } 724 scanout = &g->scanout[i]; 725 726 pixman_region_init(&finalregion); 727 pixman_region_init_rect(®ion, scanout->x, scanout->y, 728 scanout->width, scanout->height); 729 730 pixman_region_intersect(&finalregion, &flush_region, ®ion); 731 732 extents = pixman_region_extents(&finalregion); 733 size_t width = extents->x2 - extents->x1; 734 size_t height = extents->y2 - extents->y1; 735 736 if (vugbm_buffer_can_get_dmabuf_fd(&res->buffer)) { 737 VhostUserGpuMsg vmsg = { 738 .request = VHOST_USER_GPU_DMABUF_UPDATE, 739 .size = sizeof(VhostUserGpuUpdate), 740 .payload.update = (VhostUserGpuUpdate) { 741 .scanout_id = i, 742 .x = extents->x1, 743 .y = extents->y1, 744 .width = width, 745 .height = height, 746 } 747 }; 748 vg_send_msg(g, &vmsg, -1); 749 vg_wait_ok(g); 750 } else { 751 size_t bpp = 752 PIXMAN_FORMAT_BPP(pixman_image_get_format(res->image)) / 8; 753 size_t size = width * height * bpp; 754 755 void *p = g_malloc(VHOST_USER_GPU_HDR_SIZE + 756 sizeof(VhostUserGpuUpdate) + size); 757 VhostUserGpuMsg *msg = p; 758 msg->request = VHOST_USER_GPU_UPDATE; 759 msg->size = sizeof(VhostUserGpuUpdate) + size; 760 msg->payload.update = (VhostUserGpuUpdate) { 761 .scanout_id = i, 762 .x = extents->x1, 763 .y = extents->y1, 764 .width = width, 765 .height = height, 766 }; 767 pixman_image_t *i = 768 pixman_image_create_bits(pixman_image_get_format(res->image), 769 msg->payload.update.width, 770 msg->payload.update.height, 771 p + offsetof(VhostUserGpuMsg, 772 payload.update.data), 773 width * bpp); 774 pixman_image_composite(PIXMAN_OP_SRC, 775 res->image, NULL, i, 776 extents->x1, extents->y1, 777 0, 0, 0, 0, 778 width, height); 779 pixman_image_unref(i); 780 vg_send_msg(g, msg, -1); 781 g_free(msg); 782 } 783 pixman_region_fini(®ion); 784 pixman_region_fini(&finalregion); 785 } 786 pixman_region_fini(&flush_region); 787 } 788 789 static void 790 vg_process_cmd(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd) 791 { 792 switch (cmd->cmd_hdr.type) { 793 case VIRTIO_GPU_CMD_GET_DISPLAY_INFO: 794 vg_get_display_info(vg, cmd); 795 break; 796 case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: 797 vg_resource_create_2d(vg, cmd); 798 break; 799 case VIRTIO_GPU_CMD_RESOURCE_UNREF: 800 vg_resource_unref(vg, cmd); 801 break; 802 case VIRTIO_GPU_CMD_RESOURCE_FLUSH: 803 vg_resource_flush(vg, cmd); 804 break; 805 case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: 806 vg_transfer_to_host_2d(vg, cmd); 807 break; 808 case VIRTIO_GPU_CMD_SET_SCANOUT: 809 vg_set_scanout(vg, cmd); 810 break; 811 case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: 812 vg_resource_attach_backing(vg, cmd); 813 break; 814 case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: 815 vg_resource_detach_backing(vg, cmd); 816 break; 817 /* case VIRTIO_GPU_CMD_GET_EDID: */ 818 /* break */ 819 default: 820 g_warning("TODO handle ctrl %x\n", cmd->cmd_hdr.type); 821 cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; 822 break; 823 } 824 if (cmd->state == VG_CMD_STATE_NEW) { 825 vg_ctrl_response_nodata(vg, cmd, cmd->error ? cmd->error : 826 VIRTIO_GPU_RESP_OK_NODATA); 827 } 828 } 829 830 static void 831 vg_handle_ctrl(VuDev *dev, int qidx) 832 { 833 VuGpu *vg = container_of(dev, VuGpu, dev.parent); 834 VuVirtq *vq = vu_get_queue(dev, qidx); 835 struct virtio_gpu_ctrl_command *cmd = NULL; 836 size_t len; 837 838 for (;;) { 839 if (vg->wait_in != 0) { 840 return; 841 } 842 843 cmd = vu_queue_pop(dev, vq, sizeof(struct virtio_gpu_ctrl_command)); 844 if (!cmd) { 845 break; 846 } 847 cmd->vq = vq; 848 cmd->error = 0; 849 cmd->state = VG_CMD_STATE_NEW; 850 851 len = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 852 0, &cmd->cmd_hdr, sizeof(cmd->cmd_hdr)); 853 if (len != sizeof(cmd->cmd_hdr)) { 854 g_warning("%s: command size incorrect %zu vs %zu\n", 855 __func__, len, sizeof(cmd->cmd_hdr)); 856 } 857 858 virtio_gpu_ctrl_hdr_bswap(&cmd->cmd_hdr); 859 g_debug("%d %s\n", cmd->cmd_hdr.type, 860 vg_cmd_to_string(cmd->cmd_hdr.type)); 861 862 if (vg->virgl) { 863 vg_virgl_process_cmd(vg, cmd); 864 } else { 865 vg_process_cmd(vg, cmd); 866 } 867 868 if (cmd->state != VG_CMD_STATE_FINISHED) { 869 QTAILQ_INSERT_TAIL(&vg->fenceq, cmd, next); 870 vg->inflight++; 871 } else { 872 free(cmd); 873 } 874 } 875 } 876 877 static void 878 update_cursor_data_simple(VuGpu *g, uint32_t resource_id, gpointer data) 879 { 880 struct virtio_gpu_simple_resource *res; 881 882 res = virtio_gpu_find_resource(g, resource_id); 883 g_return_if_fail(res != NULL); 884 g_return_if_fail(pixman_image_get_width(res->image) == 64); 885 g_return_if_fail(pixman_image_get_height(res->image) == 64); 886 g_return_if_fail( 887 PIXMAN_FORMAT_BPP(pixman_image_get_format(res->image)) == 32); 888 889 memcpy(data, pixman_image_get_data(res->image), 64 * 64 * sizeof(uint32_t)); 890 } 891 892 static void 893 vg_process_cursor_cmd(VuGpu *g, struct virtio_gpu_update_cursor *cursor) 894 { 895 bool move = cursor->hdr.type != VIRTIO_GPU_CMD_MOVE_CURSOR; 896 897 g_debug("%s move:%d\n", G_STRFUNC, move); 898 899 if (move) { 900 VhostUserGpuMsg msg = { 901 .request = cursor->resource_id ? 902 VHOST_USER_GPU_CURSOR_POS : VHOST_USER_GPU_CURSOR_POS_HIDE, 903 .size = sizeof(VhostUserGpuCursorPos), 904 .payload.cursor_pos = { 905 .scanout_id = cursor->pos.scanout_id, 906 .x = cursor->pos.x, 907 .y = cursor->pos.y, 908 } 909 }; 910 vg_send_msg(g, &msg, -1); 911 } else { 912 VhostUserGpuMsg msg = { 913 .request = VHOST_USER_GPU_CURSOR_UPDATE, 914 .size = sizeof(VhostUserGpuCursorUpdate), 915 .payload.cursor_update = { 916 .pos = { 917 .scanout_id = cursor->pos.scanout_id, 918 .x = cursor->pos.x, 919 .y = cursor->pos.y, 920 }, 921 .hot_x = cursor->hot_x, 922 .hot_y = cursor->hot_y, 923 } 924 }; 925 if (g->virgl) { 926 vg_virgl_update_cursor_data(g, cursor->resource_id, 927 msg.payload.cursor_update.data); 928 } else { 929 update_cursor_data_simple(g, cursor->resource_id, 930 msg.payload.cursor_update.data); 931 } 932 vg_send_msg(g, &msg, -1); 933 } 934 } 935 936 static void 937 vg_handle_cursor(VuDev *dev, int qidx) 938 { 939 VuGpu *g = container_of(dev, VuGpu, dev.parent); 940 VuVirtq *vq = vu_get_queue(dev, qidx); 941 VuVirtqElement *elem; 942 size_t len; 943 struct virtio_gpu_update_cursor cursor; 944 945 for (;;) { 946 elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement)); 947 if (!elem) { 948 break; 949 } 950 g_debug("cursor out:%d in:%d\n", elem->out_num, elem->in_num); 951 952 len = iov_to_buf(elem->out_sg, elem->out_num, 953 0, &cursor, sizeof(cursor)); 954 if (len != sizeof(cursor)) { 955 g_warning("%s: cursor size incorrect %zu vs %zu\n", 956 __func__, len, sizeof(cursor)); 957 } else { 958 virtio_gpu_bswap_32(&cursor, sizeof(cursor)); 959 vg_process_cursor_cmd(g, &cursor); 960 } 961 vu_queue_push(dev, vq, elem, 0); 962 vu_queue_notify(dev, vq); 963 free(elem); 964 } 965 } 966 967 static void 968 vg_panic(VuDev *dev, const char *msg) 969 { 970 g_critical("%s\n", msg); 971 exit(1); 972 } 973 974 static void 975 vg_queue_set_started(VuDev *dev, int qidx, bool started) 976 { 977 VuVirtq *vq = vu_get_queue(dev, qidx); 978 979 g_debug("queue started %d:%d\n", qidx, started); 980 981 switch (qidx) { 982 case 0: 983 vu_set_queue_handler(dev, vq, started ? vg_handle_ctrl : NULL); 984 break; 985 case 1: 986 vu_set_queue_handler(dev, vq, started ? vg_handle_cursor : NULL); 987 break; 988 default: 989 break; 990 } 991 } 992 993 static gboolean 994 protocol_features_cb(gint fd, GIOCondition condition, gpointer user_data) 995 { 996 VuGpu *g = user_data; 997 uint64_t u64; 998 VhostUserGpuMsg msg = { 999 .request = VHOST_USER_GPU_GET_PROTOCOL_FEATURES 1000 }; 1001 1002 if (!vg_recv_msg(g, msg.request, sizeof(u64), &u64)) { 1003 return G_SOURCE_CONTINUE; 1004 } 1005 1006 msg = (VhostUserGpuMsg) { 1007 .request = VHOST_USER_GPU_SET_PROTOCOL_FEATURES, 1008 .size = sizeof(uint64_t), 1009 .payload.u64 = 0 1010 }; 1011 vg_send_msg(g, &msg, -1); 1012 1013 g->wait_in = 0; 1014 vg_handle_ctrl(&g->dev.parent, 0); 1015 1016 return G_SOURCE_REMOVE; 1017 } 1018 1019 static void 1020 set_gpu_protocol_features(VuGpu *g) 1021 { 1022 VhostUserGpuMsg msg = { 1023 .request = VHOST_USER_GPU_GET_PROTOCOL_FEATURES 1024 }; 1025 1026 vg_send_msg(g, &msg, -1); 1027 assert(g->wait_in == 0); 1028 g->wait_in = g_unix_fd_add(g->sock_fd, G_IO_IN | G_IO_HUP, 1029 protocol_features_cb, g); 1030 } 1031 1032 static int 1033 vg_process_msg(VuDev *dev, VhostUserMsg *msg, int *do_reply) 1034 { 1035 VuGpu *g = container_of(dev, VuGpu, dev.parent); 1036 1037 switch (msg->request) { 1038 case VHOST_USER_GPU_SET_SOCKET: { 1039 g_return_val_if_fail(msg->fd_num == 1, 1); 1040 g_return_val_if_fail(g->sock_fd == -1, 1); 1041 g->sock_fd = msg->fds[0]; 1042 set_gpu_protocol_features(g); 1043 return 1; 1044 } 1045 default: 1046 return 0; 1047 } 1048 1049 return 0; 1050 } 1051 1052 static uint64_t 1053 vg_get_features(VuDev *dev) 1054 { 1055 uint64_t features = 0; 1056 1057 if (opt_virgl) { 1058 features |= 1 << VIRTIO_GPU_F_VIRGL; 1059 } 1060 1061 return features; 1062 } 1063 1064 static void 1065 vg_set_features(VuDev *dev, uint64_t features) 1066 { 1067 VuGpu *g = container_of(dev, VuGpu, dev.parent); 1068 bool virgl = features & (1 << VIRTIO_GPU_F_VIRGL); 1069 1070 if (virgl && !g->virgl_inited) { 1071 if (!vg_virgl_init(g)) { 1072 vg_panic(dev, "Failed to initialize virgl"); 1073 } 1074 g->virgl_inited = true; 1075 } 1076 1077 g->virgl = virgl; 1078 } 1079 1080 static int 1081 vg_get_config(VuDev *dev, uint8_t *config, uint32_t len) 1082 { 1083 VuGpu *g = container_of(dev, VuGpu, dev.parent); 1084 1085 if (len > sizeof(struct virtio_gpu_config)) { 1086 return -1; 1087 } 1088 1089 if (opt_virgl) { 1090 g->virtio_config.num_capsets = vg_virgl_get_num_capsets(); 1091 } 1092 1093 memcpy(config, &g->virtio_config, len); 1094 1095 return 0; 1096 } 1097 1098 static int 1099 vg_set_config(VuDev *dev, const uint8_t *data, 1100 uint32_t offset, uint32_t size, 1101 uint32_t flags) 1102 { 1103 VuGpu *g = container_of(dev, VuGpu, dev.parent); 1104 struct virtio_gpu_config *config = (struct virtio_gpu_config *)data; 1105 1106 if (config->events_clear) { 1107 g->virtio_config.events_read &= ~config->events_clear; 1108 } 1109 1110 return 0; 1111 } 1112 1113 static const VuDevIface vuiface = { 1114 .set_features = vg_set_features, 1115 .get_features = vg_get_features, 1116 .queue_set_started = vg_queue_set_started, 1117 .process_msg = vg_process_msg, 1118 .get_config = vg_get_config, 1119 .set_config = vg_set_config, 1120 }; 1121 1122 static void 1123 vg_destroy(VuGpu *g) 1124 { 1125 struct virtio_gpu_simple_resource *res, *tmp; 1126 1127 vug_deinit(&g->dev); 1128 1129 vg_sock_fd_close(g); 1130 1131 QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) { 1132 vg_resource_destroy(g, res); 1133 } 1134 1135 vugbm_device_destroy(&g->gdev); 1136 } 1137 1138 static GOptionEntry entries[] = { 1139 { "print-capabilities", 'c', 0, G_OPTION_ARG_NONE, &opt_print_caps, 1140 "Print capabilities", NULL }, 1141 { "fd", 'f', 0, G_OPTION_ARG_INT, &opt_fdnum, 1142 "Use inherited fd socket", "FDNUM" }, 1143 { "socket-path", 's', 0, G_OPTION_ARG_FILENAME, &opt_socket_path, 1144 "Use UNIX socket path", "PATH" }, 1145 { "render-node", 'r', 0, G_OPTION_ARG_FILENAME, &opt_render_node, 1146 "Specify DRM render node", "PATH" }, 1147 { "virgl", 'v', 0, G_OPTION_ARG_NONE, &opt_virgl, 1148 "Turn virgl rendering on", NULL }, 1149 { NULL, } 1150 }; 1151 1152 int 1153 main(int argc, char *argv[]) 1154 { 1155 GOptionContext *context; 1156 GError *error = NULL; 1157 GMainLoop *loop = NULL; 1158 int fd; 1159 VuGpu g = { .sock_fd = -1, .drm_rnode_fd = -1 }; 1160 1161 QTAILQ_INIT(&g.reslist); 1162 QTAILQ_INIT(&g.fenceq); 1163 1164 context = g_option_context_new("QEMU vhost-user-gpu"); 1165 g_option_context_add_main_entries(context, entries, NULL); 1166 if (!g_option_context_parse(context, &argc, &argv, &error)) { 1167 g_printerr("Option parsing failed: %s\n", error->message); 1168 exit(EXIT_FAILURE); 1169 } 1170 g_option_context_free(context); 1171 1172 if (opt_print_caps) { 1173 g_print("{\n"); 1174 g_print(" \"type\": \"gpu\",\n"); 1175 g_print(" \"features\": [\n"); 1176 g_print(" \"render-node\",\n"); 1177 g_print(" \"virgl\"\n"); 1178 g_print(" ]\n"); 1179 g_print("}\n"); 1180 exit(EXIT_SUCCESS); 1181 } 1182 1183 g.drm_rnode_fd = qemu_drm_rendernode_open(opt_render_node); 1184 if (opt_render_node && g.drm_rnode_fd == -1) { 1185 g_printerr("Failed to open DRM rendernode.\n"); 1186 exit(EXIT_FAILURE); 1187 } 1188 1189 if (g.drm_rnode_fd >= 0) { 1190 if (!vugbm_device_init(&g.gdev, g.drm_rnode_fd)) { 1191 g_warning("Failed to init DRM device, using fallback path"); 1192 } 1193 } 1194 1195 if ((!!opt_socket_path + (opt_fdnum != -1)) != 1) { 1196 g_printerr("Please specify either --fd or --socket-path\n"); 1197 exit(EXIT_FAILURE); 1198 } 1199 1200 if (opt_socket_path) { 1201 int lsock = unix_listen(opt_socket_path, &error_fatal); 1202 if (lsock < 0) { 1203 g_printerr("Failed to listen on %s.\n", opt_socket_path); 1204 exit(EXIT_FAILURE); 1205 } 1206 fd = accept(lsock, NULL, NULL); 1207 close(lsock); 1208 } else { 1209 fd = opt_fdnum; 1210 } 1211 if (fd == -1) { 1212 g_printerr("Invalid vhost-user socket.\n"); 1213 exit(EXIT_FAILURE); 1214 } 1215 1216 if (!vug_init(&g.dev, VHOST_USER_GPU_MAX_QUEUES, fd, vg_panic, &vuiface)) { 1217 g_printerr("Failed to initialize libvhost-user-glib.\n"); 1218 exit(EXIT_FAILURE); 1219 } 1220 1221 loop = g_main_loop_new(NULL, FALSE); 1222 g_main_loop_run(loop); 1223 g_main_loop_unref(loop); 1224 1225 vg_destroy(&g); 1226 if (g.drm_rnode_fd >= 0) { 1227 close(g.drm_rnode_fd); 1228 } 1229 1230 return 0; 1231 } 1232