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 switch (cursor->hdr.type) { 896 case VIRTIO_GPU_CMD_MOVE_CURSOR: { 897 VhostUserGpuMsg msg = { 898 .request = cursor->resource_id ? 899 VHOST_USER_GPU_CURSOR_POS : VHOST_USER_GPU_CURSOR_POS_HIDE, 900 .size = sizeof(VhostUserGpuCursorPos), 901 .payload.cursor_pos = { 902 .scanout_id = cursor->pos.scanout_id, 903 .x = cursor->pos.x, 904 .y = cursor->pos.y, 905 } 906 }; 907 g_debug("%s: move", G_STRFUNC); 908 vg_send_msg(g, &msg, -1); 909 break; 910 } 911 case VIRTIO_GPU_CMD_UPDATE_CURSOR: { 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 g_debug("%s: update", G_STRFUNC); 926 if (g->virgl) { 927 vg_virgl_update_cursor_data(g, cursor->resource_id, 928 msg.payload.cursor_update.data); 929 } else { 930 update_cursor_data_simple(g, cursor->resource_id, 931 msg.payload.cursor_update.data); 932 } 933 vg_send_msg(g, &msg, -1); 934 break; 935 } 936 default: 937 g_debug("%s: unknown cmd %d", G_STRFUNC, cursor->hdr.type); 938 break; 939 } 940 } 941 942 static void 943 vg_handle_cursor(VuDev *dev, int qidx) 944 { 945 VuGpu *g = container_of(dev, VuGpu, dev.parent); 946 VuVirtq *vq = vu_get_queue(dev, qidx); 947 VuVirtqElement *elem; 948 size_t len; 949 struct virtio_gpu_update_cursor cursor; 950 951 for (;;) { 952 elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement)); 953 if (!elem) { 954 break; 955 } 956 g_debug("cursor out:%d in:%d\n", elem->out_num, elem->in_num); 957 958 len = iov_to_buf(elem->out_sg, elem->out_num, 959 0, &cursor, sizeof(cursor)); 960 if (len != sizeof(cursor)) { 961 g_warning("%s: cursor size incorrect %zu vs %zu\n", 962 __func__, len, sizeof(cursor)); 963 } else { 964 virtio_gpu_bswap_32(&cursor, sizeof(cursor)); 965 vg_process_cursor_cmd(g, &cursor); 966 } 967 vu_queue_push(dev, vq, elem, 0); 968 vu_queue_notify(dev, vq); 969 free(elem); 970 } 971 } 972 973 static void 974 vg_panic(VuDev *dev, const char *msg) 975 { 976 g_critical("%s\n", msg); 977 exit(1); 978 } 979 980 static void 981 vg_queue_set_started(VuDev *dev, int qidx, bool started) 982 { 983 VuVirtq *vq = vu_get_queue(dev, qidx); 984 985 g_debug("queue started %d:%d\n", qidx, started); 986 987 switch (qidx) { 988 case 0: 989 vu_set_queue_handler(dev, vq, started ? vg_handle_ctrl : NULL); 990 break; 991 case 1: 992 vu_set_queue_handler(dev, vq, started ? vg_handle_cursor : NULL); 993 break; 994 default: 995 break; 996 } 997 } 998 999 static gboolean 1000 protocol_features_cb(gint fd, GIOCondition condition, gpointer user_data) 1001 { 1002 VuGpu *g = user_data; 1003 uint64_t u64; 1004 VhostUserGpuMsg msg = { 1005 .request = VHOST_USER_GPU_GET_PROTOCOL_FEATURES 1006 }; 1007 1008 if (!vg_recv_msg(g, msg.request, sizeof(u64), &u64)) { 1009 return G_SOURCE_CONTINUE; 1010 } 1011 1012 msg = (VhostUserGpuMsg) { 1013 .request = VHOST_USER_GPU_SET_PROTOCOL_FEATURES, 1014 .size = sizeof(uint64_t), 1015 .payload.u64 = 0 1016 }; 1017 vg_send_msg(g, &msg, -1); 1018 1019 g->wait_in = 0; 1020 vg_handle_ctrl(&g->dev.parent, 0); 1021 1022 return G_SOURCE_REMOVE; 1023 } 1024 1025 static void 1026 set_gpu_protocol_features(VuGpu *g) 1027 { 1028 VhostUserGpuMsg msg = { 1029 .request = VHOST_USER_GPU_GET_PROTOCOL_FEATURES 1030 }; 1031 1032 vg_send_msg(g, &msg, -1); 1033 assert(g->wait_in == 0); 1034 g->wait_in = g_unix_fd_add(g->sock_fd, G_IO_IN | G_IO_HUP, 1035 protocol_features_cb, g); 1036 } 1037 1038 static int 1039 vg_process_msg(VuDev *dev, VhostUserMsg *msg, int *do_reply) 1040 { 1041 VuGpu *g = container_of(dev, VuGpu, dev.parent); 1042 1043 switch (msg->request) { 1044 case VHOST_USER_GPU_SET_SOCKET: { 1045 g_return_val_if_fail(msg->fd_num == 1, 1); 1046 g_return_val_if_fail(g->sock_fd == -1, 1); 1047 g->sock_fd = msg->fds[0]; 1048 set_gpu_protocol_features(g); 1049 return 1; 1050 } 1051 default: 1052 return 0; 1053 } 1054 1055 return 0; 1056 } 1057 1058 static uint64_t 1059 vg_get_features(VuDev *dev) 1060 { 1061 uint64_t features = 0; 1062 1063 if (opt_virgl) { 1064 features |= 1 << VIRTIO_GPU_F_VIRGL; 1065 } 1066 1067 return features; 1068 } 1069 1070 static void 1071 vg_set_features(VuDev *dev, uint64_t features) 1072 { 1073 VuGpu *g = container_of(dev, VuGpu, dev.parent); 1074 bool virgl = features & (1 << VIRTIO_GPU_F_VIRGL); 1075 1076 if (virgl && !g->virgl_inited) { 1077 if (!vg_virgl_init(g)) { 1078 vg_panic(dev, "Failed to initialize virgl"); 1079 } 1080 g->virgl_inited = true; 1081 } 1082 1083 g->virgl = virgl; 1084 } 1085 1086 static int 1087 vg_get_config(VuDev *dev, uint8_t *config, uint32_t len) 1088 { 1089 VuGpu *g = container_of(dev, VuGpu, dev.parent); 1090 1091 if (len > sizeof(struct virtio_gpu_config)) { 1092 return -1; 1093 } 1094 1095 if (opt_virgl) { 1096 g->virtio_config.num_capsets = vg_virgl_get_num_capsets(); 1097 } 1098 1099 memcpy(config, &g->virtio_config, len); 1100 1101 return 0; 1102 } 1103 1104 static int 1105 vg_set_config(VuDev *dev, const uint8_t *data, 1106 uint32_t offset, uint32_t size, 1107 uint32_t flags) 1108 { 1109 VuGpu *g = container_of(dev, VuGpu, dev.parent); 1110 struct virtio_gpu_config *config = (struct virtio_gpu_config *)data; 1111 1112 if (config->events_clear) { 1113 g->virtio_config.events_read &= ~config->events_clear; 1114 } 1115 1116 return 0; 1117 } 1118 1119 static const VuDevIface vuiface = { 1120 .set_features = vg_set_features, 1121 .get_features = vg_get_features, 1122 .queue_set_started = vg_queue_set_started, 1123 .process_msg = vg_process_msg, 1124 .get_config = vg_get_config, 1125 .set_config = vg_set_config, 1126 }; 1127 1128 static void 1129 vg_destroy(VuGpu *g) 1130 { 1131 struct virtio_gpu_simple_resource *res, *tmp; 1132 1133 vug_deinit(&g->dev); 1134 1135 vg_sock_fd_close(g); 1136 1137 QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) { 1138 vg_resource_destroy(g, res); 1139 } 1140 1141 vugbm_device_destroy(&g->gdev); 1142 } 1143 1144 static GOptionEntry entries[] = { 1145 { "print-capabilities", 'c', 0, G_OPTION_ARG_NONE, &opt_print_caps, 1146 "Print capabilities", NULL }, 1147 { "fd", 'f', 0, G_OPTION_ARG_INT, &opt_fdnum, 1148 "Use inherited fd socket", "FDNUM" }, 1149 { "socket-path", 's', 0, G_OPTION_ARG_FILENAME, &opt_socket_path, 1150 "Use UNIX socket path", "PATH" }, 1151 { "render-node", 'r', 0, G_OPTION_ARG_FILENAME, &opt_render_node, 1152 "Specify DRM render node", "PATH" }, 1153 { "virgl", 'v', 0, G_OPTION_ARG_NONE, &opt_virgl, 1154 "Turn virgl rendering on", NULL }, 1155 { NULL, } 1156 }; 1157 1158 int 1159 main(int argc, char *argv[]) 1160 { 1161 GOptionContext *context; 1162 GError *error = NULL; 1163 GMainLoop *loop = NULL; 1164 int fd; 1165 VuGpu g = { .sock_fd = -1, .drm_rnode_fd = -1 }; 1166 1167 QTAILQ_INIT(&g.reslist); 1168 QTAILQ_INIT(&g.fenceq); 1169 1170 context = g_option_context_new("QEMU vhost-user-gpu"); 1171 g_option_context_add_main_entries(context, entries, NULL); 1172 if (!g_option_context_parse(context, &argc, &argv, &error)) { 1173 g_printerr("Option parsing failed: %s\n", error->message); 1174 exit(EXIT_FAILURE); 1175 } 1176 g_option_context_free(context); 1177 1178 if (opt_print_caps) { 1179 g_print("{\n"); 1180 g_print(" \"type\": \"gpu\",\n"); 1181 g_print(" \"features\": [\n"); 1182 g_print(" \"render-node\",\n"); 1183 g_print(" \"virgl\"\n"); 1184 g_print(" ]\n"); 1185 g_print("}\n"); 1186 exit(EXIT_SUCCESS); 1187 } 1188 1189 g.drm_rnode_fd = qemu_drm_rendernode_open(opt_render_node); 1190 if (opt_render_node && g.drm_rnode_fd == -1) { 1191 g_printerr("Failed to open DRM rendernode.\n"); 1192 exit(EXIT_FAILURE); 1193 } 1194 1195 vugbm_device_init(&g.gdev, g.drm_rnode_fd); 1196 1197 if ((!!opt_socket_path + (opt_fdnum != -1)) != 1) { 1198 g_printerr("Please specify either --fd or --socket-path\n"); 1199 exit(EXIT_FAILURE); 1200 } 1201 1202 if (opt_socket_path) { 1203 int lsock = unix_listen(opt_socket_path, &error_fatal); 1204 if (lsock < 0) { 1205 g_printerr("Failed to listen on %s.\n", opt_socket_path); 1206 exit(EXIT_FAILURE); 1207 } 1208 fd = accept(lsock, NULL, NULL); 1209 close(lsock); 1210 } else { 1211 fd = opt_fdnum; 1212 } 1213 if (fd == -1) { 1214 g_printerr("Invalid vhost-user socket.\n"); 1215 exit(EXIT_FAILURE); 1216 } 1217 1218 if (!vug_init(&g.dev, VHOST_USER_GPU_MAX_QUEUES, fd, vg_panic, &vuiface)) { 1219 g_printerr("Failed to initialize libvhost-user-glib.\n"); 1220 exit(EXIT_FAILURE); 1221 } 1222 1223 loop = g_main_loop_new(NULL, FALSE); 1224 g_main_loop_run(loop); 1225 g_main_loop_unref(loop); 1226 1227 vg_destroy(&g); 1228 if (g.drm_rnode_fd >= 0) { 1229 close(g.drm_rnode_fd); 1230 } 1231 1232 return 0; 1233 } 1234