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 vugbm_buffer_destroy(&res->buffer); 353 cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; 354 return; 355 } 356 357 QTAILQ_INSERT_HEAD(&g->reslist, res, next); 358 } 359 360 static void 361 vg_disable_scanout(VuGpu *g, int scanout_id) 362 { 363 struct virtio_gpu_scanout *scanout = &g->scanout[scanout_id]; 364 struct virtio_gpu_simple_resource *res; 365 366 if (scanout->resource_id == 0) { 367 return; 368 } 369 370 res = virtio_gpu_find_resource(g, scanout->resource_id); 371 if (res) { 372 res->scanout_bitmask &= ~(1 << scanout_id); 373 } 374 375 scanout->width = 0; 376 scanout->height = 0; 377 378 if (g->sock_fd >= 0) { 379 VhostUserGpuMsg msg = { 380 .request = VHOST_USER_GPU_SCANOUT, 381 .size = sizeof(VhostUserGpuScanout), 382 .payload.scanout.scanout_id = scanout_id, 383 }; 384 vg_send_msg(g, &msg, -1); 385 } 386 } 387 388 static void 389 vg_resource_destroy(VuGpu *g, 390 struct virtio_gpu_simple_resource *res) 391 { 392 int i; 393 394 if (res->scanout_bitmask) { 395 for (i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) { 396 if (res->scanout_bitmask & (1 << i)) { 397 vg_disable_scanout(g, i); 398 } 399 } 400 } 401 402 vugbm_buffer_destroy(&res->buffer); 403 pixman_image_unref(res->image); 404 QTAILQ_REMOVE(&g->reslist, res, next); 405 g_free(res); 406 } 407 408 static void 409 vg_resource_unref(VuGpu *g, 410 struct virtio_gpu_ctrl_command *cmd) 411 { 412 struct virtio_gpu_simple_resource *res; 413 struct virtio_gpu_resource_unref unref; 414 415 VUGPU_FILL_CMD(unref); 416 virtio_gpu_bswap_32(&unref, sizeof(unref)); 417 418 res = virtio_gpu_find_resource(g, unref.resource_id); 419 if (!res) { 420 g_critical("%s: illegal resource specified %d", 421 __func__, unref.resource_id); 422 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; 423 return; 424 } 425 vg_resource_destroy(g, res); 426 } 427 428 int 429 vg_create_mapping_iov(VuGpu *g, 430 struct virtio_gpu_resource_attach_backing *ab, 431 struct virtio_gpu_ctrl_command *cmd, 432 struct iovec **iov) 433 { 434 struct virtio_gpu_mem_entry *ents; 435 size_t esize, s; 436 int i; 437 438 if (ab->nr_entries > 16384) { 439 g_critical("%s: nr_entries is too big (%d > 16384)", 440 __func__, ab->nr_entries); 441 return -1; 442 } 443 444 esize = sizeof(*ents) * ab->nr_entries; 445 ents = g_malloc(esize); 446 s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 447 sizeof(*ab), ents, esize); 448 if (s != esize) { 449 g_critical("%s: command data size incorrect %zu vs %zu", 450 __func__, s, esize); 451 g_free(ents); 452 return -1; 453 } 454 455 *iov = g_malloc0(sizeof(struct iovec) * ab->nr_entries); 456 for (i = 0; i < ab->nr_entries; i++) { 457 uint64_t len = ents[i].length; 458 (*iov)[i].iov_len = ents[i].length; 459 (*iov)[i].iov_base = vu_gpa_to_va(&g->dev.parent, &len, ents[i].addr); 460 if (!(*iov)[i].iov_base || len != ents[i].length) { 461 g_critical("%s: resource %d element %d", 462 __func__, ab->resource_id, i); 463 g_free(*iov); 464 g_free(ents); 465 *iov = NULL; 466 return -1; 467 } 468 } 469 g_free(ents); 470 return 0; 471 } 472 473 static void 474 vg_resource_attach_backing(VuGpu *g, 475 struct virtio_gpu_ctrl_command *cmd) 476 { 477 struct virtio_gpu_simple_resource *res; 478 struct virtio_gpu_resource_attach_backing ab; 479 int ret; 480 481 VUGPU_FILL_CMD(ab); 482 virtio_gpu_bswap_32(&ab, sizeof(ab)); 483 484 res = virtio_gpu_find_resource(g, ab.resource_id); 485 if (!res) { 486 g_critical("%s: illegal resource specified %d", 487 __func__, ab.resource_id); 488 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; 489 return; 490 } 491 492 ret = vg_create_mapping_iov(g, &ab, cmd, &res->iov); 493 if (ret != 0) { 494 cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; 495 return; 496 } 497 498 res->iov_cnt = ab.nr_entries; 499 } 500 501 static void 502 vg_resource_detach_backing(VuGpu *g, 503 struct virtio_gpu_ctrl_command *cmd) 504 { 505 struct virtio_gpu_simple_resource *res; 506 struct virtio_gpu_resource_detach_backing detach; 507 508 VUGPU_FILL_CMD(detach); 509 virtio_gpu_bswap_32(&detach, sizeof(detach)); 510 511 res = virtio_gpu_find_resource(g, detach.resource_id); 512 if (!res || !res->iov) { 513 g_critical("%s: illegal resource specified %d", 514 __func__, detach.resource_id); 515 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; 516 return; 517 } 518 519 g_free(res->iov); 520 res->iov = NULL; 521 res->iov_cnt = 0; 522 } 523 524 static void 525 vg_transfer_to_host_2d(VuGpu *g, 526 struct virtio_gpu_ctrl_command *cmd) 527 { 528 struct virtio_gpu_simple_resource *res; 529 int h; 530 uint32_t src_offset, dst_offset, stride; 531 int bpp; 532 pixman_format_code_t format; 533 struct virtio_gpu_transfer_to_host_2d t2d; 534 535 VUGPU_FILL_CMD(t2d); 536 virtio_gpu_t2d_bswap(&t2d); 537 538 res = virtio_gpu_find_resource(g, t2d.resource_id); 539 if (!res || !res->iov) { 540 g_critical("%s: illegal resource specified %d", 541 __func__, t2d.resource_id); 542 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; 543 return; 544 } 545 546 if (t2d.r.x > res->width || 547 t2d.r.y > res->height || 548 t2d.r.width > res->width || 549 t2d.r.height > res->height || 550 t2d.r.x + t2d.r.width > res->width || 551 t2d.r.y + t2d.r.height > res->height) { 552 g_critical("%s: transfer bounds outside resource" 553 " bounds for resource %d: %d %d %d %d vs %d %d", 554 __func__, t2d.resource_id, t2d.r.x, t2d.r.y, 555 t2d.r.width, t2d.r.height, res->width, res->height); 556 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; 557 return; 558 } 559 560 format = pixman_image_get_format(res->image); 561 bpp = (PIXMAN_FORMAT_BPP(format) + 7) / 8; 562 stride = pixman_image_get_stride(res->image); 563 564 if (t2d.offset || t2d.r.x || t2d.r.y || 565 t2d.r.width != pixman_image_get_width(res->image)) { 566 void *img_data = pixman_image_get_data(res->image); 567 for (h = 0; h < t2d.r.height; h++) { 568 src_offset = t2d.offset + stride * h; 569 dst_offset = (t2d.r.y + h) * stride + (t2d.r.x * bpp); 570 571 iov_to_buf(res->iov, res->iov_cnt, src_offset, 572 img_data 573 + dst_offset, t2d.r.width * bpp); 574 } 575 } else { 576 iov_to_buf(res->iov, res->iov_cnt, 0, 577 pixman_image_get_data(res->image), 578 pixman_image_get_stride(res->image) 579 * pixman_image_get_height(res->image)); 580 } 581 } 582 583 static void 584 vg_set_scanout(VuGpu *g, 585 struct virtio_gpu_ctrl_command *cmd) 586 { 587 struct virtio_gpu_simple_resource *res, *ores; 588 struct virtio_gpu_scanout *scanout; 589 struct virtio_gpu_set_scanout ss; 590 int fd; 591 592 VUGPU_FILL_CMD(ss); 593 virtio_gpu_bswap_32(&ss, sizeof(ss)); 594 595 if (ss.scanout_id >= VIRTIO_GPU_MAX_SCANOUTS) { 596 g_critical("%s: illegal scanout id specified %d", 597 __func__, ss.scanout_id); 598 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; 599 return; 600 } 601 602 if (ss.resource_id == 0) { 603 vg_disable_scanout(g, ss.scanout_id); 604 return; 605 } 606 607 /* create a surface for this scanout */ 608 res = virtio_gpu_find_resource(g, ss.resource_id); 609 if (!res) { 610 g_critical("%s: illegal resource specified %d", 611 __func__, ss.resource_id); 612 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; 613 return; 614 } 615 616 if (ss.r.x > res->width || 617 ss.r.y > res->height || 618 ss.r.width > res->width || 619 ss.r.height > res->height || 620 ss.r.x + ss.r.width > res->width || 621 ss.r.y + ss.r.height > res->height) { 622 g_critical("%s: illegal scanout %d bounds for" 623 " resource %d, (%d,%d)+%d,%d vs %d %d", 624 __func__, ss.scanout_id, ss.resource_id, ss.r.x, ss.r.y, 625 ss.r.width, ss.r.height, res->width, res->height); 626 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; 627 return; 628 } 629 630 scanout = &g->scanout[ss.scanout_id]; 631 632 ores = virtio_gpu_find_resource(g, scanout->resource_id); 633 if (ores) { 634 ores->scanout_bitmask &= ~(1 << ss.scanout_id); 635 } 636 637 res->scanout_bitmask |= (1 << ss.scanout_id); 638 scanout->resource_id = ss.resource_id; 639 scanout->x = ss.r.x; 640 scanout->y = ss.r.y; 641 scanout->width = ss.r.width; 642 scanout->height = ss.r.height; 643 644 struct vugbm_buffer *buffer = &res->buffer; 645 646 if (vugbm_buffer_can_get_dmabuf_fd(buffer)) { 647 VhostUserGpuMsg msg = { 648 .request = VHOST_USER_GPU_DMABUF_SCANOUT, 649 .size = sizeof(VhostUserGpuDMABUFScanout), 650 .payload.dmabuf_scanout = (VhostUserGpuDMABUFScanout) { 651 .scanout_id = ss.scanout_id, 652 .x = ss.r.x, 653 .y = ss.r.y, 654 .width = ss.r.width, 655 .height = ss.r.height, 656 .fd_width = buffer->width, 657 .fd_height = buffer->height, 658 .fd_stride = buffer->stride, 659 .fd_drm_fourcc = buffer->format 660 } 661 }; 662 663 if (vugbm_buffer_get_dmabuf_fd(buffer, &fd)) { 664 vg_send_msg(g, &msg, fd); 665 close(fd); 666 } 667 } else { 668 VhostUserGpuMsg msg = { 669 .request = VHOST_USER_GPU_SCANOUT, 670 .size = sizeof(VhostUserGpuScanout), 671 .payload.scanout = (VhostUserGpuScanout) { 672 .scanout_id = ss.scanout_id, 673 .width = scanout->width, 674 .height = scanout->height 675 } 676 }; 677 vg_send_msg(g, &msg, -1); 678 } 679 } 680 681 static void 682 vg_resource_flush(VuGpu *g, 683 struct virtio_gpu_ctrl_command *cmd) 684 { 685 struct virtio_gpu_simple_resource *res; 686 struct virtio_gpu_resource_flush rf; 687 pixman_region16_t flush_region; 688 int i; 689 690 VUGPU_FILL_CMD(rf); 691 virtio_gpu_bswap_32(&rf, sizeof(rf)); 692 693 res = virtio_gpu_find_resource(g, rf.resource_id); 694 if (!res) { 695 g_critical("%s: illegal resource specified %d\n", 696 __func__, rf.resource_id); 697 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; 698 return; 699 } 700 701 if (rf.r.x > res->width || 702 rf.r.y > res->height || 703 rf.r.width > res->width || 704 rf.r.height > res->height || 705 rf.r.x + rf.r.width > res->width || 706 rf.r.y + rf.r.height > res->height) { 707 g_critical("%s: flush bounds outside resource" 708 " bounds for resource %d: %d %d %d %d vs %d %d\n", 709 __func__, rf.resource_id, rf.r.x, rf.r.y, 710 rf.r.width, rf.r.height, res->width, res->height); 711 cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; 712 return; 713 } 714 715 pixman_region_init_rect(&flush_region, 716 rf.r.x, rf.r.y, rf.r.width, rf.r.height); 717 for (i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) { 718 struct virtio_gpu_scanout *scanout; 719 pixman_region16_t region, finalregion; 720 pixman_box16_t *extents; 721 722 if (!(res->scanout_bitmask & (1 << i))) { 723 continue; 724 } 725 scanout = &g->scanout[i]; 726 727 pixman_region_init(&finalregion); 728 pixman_region_init_rect(®ion, scanout->x, scanout->y, 729 scanout->width, scanout->height); 730 731 pixman_region_intersect(&finalregion, &flush_region, ®ion); 732 733 extents = pixman_region_extents(&finalregion); 734 size_t width = extents->x2 - extents->x1; 735 size_t height = extents->y2 - extents->y1; 736 737 if (vugbm_buffer_can_get_dmabuf_fd(&res->buffer)) { 738 VhostUserGpuMsg vmsg = { 739 .request = VHOST_USER_GPU_DMABUF_UPDATE, 740 .size = sizeof(VhostUserGpuUpdate), 741 .payload.update = (VhostUserGpuUpdate) { 742 .scanout_id = i, 743 .x = extents->x1, 744 .y = extents->y1, 745 .width = width, 746 .height = height, 747 } 748 }; 749 vg_send_msg(g, &vmsg, -1); 750 vg_wait_ok(g); 751 } else { 752 size_t bpp = 753 PIXMAN_FORMAT_BPP(pixman_image_get_format(res->image)) / 8; 754 size_t size = width * height * bpp; 755 756 void *p = g_malloc(VHOST_USER_GPU_HDR_SIZE + 757 sizeof(VhostUserGpuUpdate) + size); 758 VhostUserGpuMsg *msg = p; 759 msg->request = VHOST_USER_GPU_UPDATE; 760 msg->size = sizeof(VhostUserGpuUpdate) + size; 761 msg->payload.update = (VhostUserGpuUpdate) { 762 .scanout_id = i, 763 .x = extents->x1, 764 .y = extents->y1, 765 .width = width, 766 .height = height, 767 }; 768 pixman_image_t *i = 769 pixman_image_create_bits(pixman_image_get_format(res->image), 770 msg->payload.update.width, 771 msg->payload.update.height, 772 p + offsetof(VhostUserGpuMsg, 773 payload.update.data), 774 width * bpp); 775 pixman_image_composite(PIXMAN_OP_SRC, 776 res->image, NULL, i, 777 extents->x1, extents->y1, 778 0, 0, 0, 0, 779 width, height); 780 pixman_image_unref(i); 781 vg_send_msg(g, msg, -1); 782 g_free(msg); 783 } 784 pixman_region_fini(®ion); 785 pixman_region_fini(&finalregion); 786 } 787 pixman_region_fini(&flush_region); 788 } 789 790 static void 791 vg_process_cmd(VuGpu *vg, struct virtio_gpu_ctrl_command *cmd) 792 { 793 switch (cmd->cmd_hdr.type) { 794 case VIRTIO_GPU_CMD_GET_DISPLAY_INFO: 795 vg_get_display_info(vg, cmd); 796 break; 797 case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: 798 vg_resource_create_2d(vg, cmd); 799 break; 800 case VIRTIO_GPU_CMD_RESOURCE_UNREF: 801 vg_resource_unref(vg, cmd); 802 break; 803 case VIRTIO_GPU_CMD_RESOURCE_FLUSH: 804 vg_resource_flush(vg, cmd); 805 break; 806 case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: 807 vg_transfer_to_host_2d(vg, cmd); 808 break; 809 case VIRTIO_GPU_CMD_SET_SCANOUT: 810 vg_set_scanout(vg, cmd); 811 break; 812 case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: 813 vg_resource_attach_backing(vg, cmd); 814 break; 815 case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: 816 vg_resource_detach_backing(vg, cmd); 817 break; 818 /* case VIRTIO_GPU_CMD_GET_EDID: */ 819 /* break */ 820 default: 821 g_warning("TODO handle ctrl %x\n", cmd->cmd_hdr.type); 822 cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; 823 break; 824 } 825 if (cmd->state == VG_CMD_STATE_NEW) { 826 vg_ctrl_response_nodata(vg, cmd, cmd->error ? cmd->error : 827 VIRTIO_GPU_RESP_OK_NODATA); 828 } 829 } 830 831 static void 832 vg_handle_ctrl(VuDev *dev, int qidx) 833 { 834 VuGpu *vg = container_of(dev, VuGpu, dev.parent); 835 VuVirtq *vq = vu_get_queue(dev, qidx); 836 struct virtio_gpu_ctrl_command *cmd = NULL; 837 size_t len; 838 839 for (;;) { 840 if (vg->wait_in != 0) { 841 return; 842 } 843 844 cmd = vu_queue_pop(dev, vq, sizeof(struct virtio_gpu_ctrl_command)); 845 if (!cmd) { 846 break; 847 } 848 cmd->vq = vq; 849 cmd->error = 0; 850 cmd->state = VG_CMD_STATE_NEW; 851 852 len = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 853 0, &cmd->cmd_hdr, sizeof(cmd->cmd_hdr)); 854 if (len != sizeof(cmd->cmd_hdr)) { 855 g_warning("%s: command size incorrect %zu vs %zu\n", 856 __func__, len, sizeof(cmd->cmd_hdr)); 857 } 858 859 virtio_gpu_ctrl_hdr_bswap(&cmd->cmd_hdr); 860 g_debug("%d %s\n", cmd->cmd_hdr.type, 861 vg_cmd_to_string(cmd->cmd_hdr.type)); 862 863 if (vg->virgl) { 864 vg_virgl_process_cmd(vg, cmd); 865 } else { 866 vg_process_cmd(vg, cmd); 867 } 868 869 if (cmd->state != VG_CMD_STATE_FINISHED) { 870 QTAILQ_INSERT_TAIL(&vg->fenceq, cmd, next); 871 vg->inflight++; 872 } else { 873 free(cmd); 874 } 875 } 876 } 877 878 static void 879 update_cursor_data_simple(VuGpu *g, uint32_t resource_id, gpointer data) 880 { 881 struct virtio_gpu_simple_resource *res; 882 883 res = virtio_gpu_find_resource(g, resource_id); 884 g_return_if_fail(res != NULL); 885 g_return_if_fail(pixman_image_get_width(res->image) == 64); 886 g_return_if_fail(pixman_image_get_height(res->image) == 64); 887 g_return_if_fail( 888 PIXMAN_FORMAT_BPP(pixman_image_get_format(res->image)) == 32); 889 890 memcpy(data, pixman_image_get_data(res->image), 64 * 64 * sizeof(uint32_t)); 891 } 892 893 static void 894 vg_process_cursor_cmd(VuGpu *g, struct virtio_gpu_update_cursor *cursor) 895 { 896 switch (cursor->hdr.type) { 897 case VIRTIO_GPU_CMD_MOVE_CURSOR: { 898 VhostUserGpuMsg msg = { 899 .request = cursor->resource_id ? 900 VHOST_USER_GPU_CURSOR_POS : VHOST_USER_GPU_CURSOR_POS_HIDE, 901 .size = sizeof(VhostUserGpuCursorPos), 902 .payload.cursor_pos = { 903 .scanout_id = cursor->pos.scanout_id, 904 .x = cursor->pos.x, 905 .y = cursor->pos.y, 906 } 907 }; 908 g_debug("%s: move", G_STRFUNC); 909 vg_send_msg(g, &msg, -1); 910 break; 911 } 912 case VIRTIO_GPU_CMD_UPDATE_CURSOR: { 913 VhostUserGpuMsg msg = { 914 .request = VHOST_USER_GPU_CURSOR_UPDATE, 915 .size = sizeof(VhostUserGpuCursorUpdate), 916 .payload.cursor_update = { 917 .pos = { 918 .scanout_id = cursor->pos.scanout_id, 919 .x = cursor->pos.x, 920 .y = cursor->pos.y, 921 }, 922 .hot_x = cursor->hot_x, 923 .hot_y = cursor->hot_y, 924 } 925 }; 926 g_debug("%s: update", G_STRFUNC); 927 if (g->virgl) { 928 vg_virgl_update_cursor_data(g, cursor->resource_id, 929 msg.payload.cursor_update.data); 930 } else { 931 update_cursor_data_simple(g, cursor->resource_id, 932 msg.payload.cursor_update.data); 933 } 934 vg_send_msg(g, &msg, -1); 935 break; 936 } 937 default: 938 g_debug("%s: unknown cmd %d", G_STRFUNC, cursor->hdr.type); 939 break; 940 } 941 } 942 943 static void 944 vg_handle_cursor(VuDev *dev, int qidx) 945 { 946 VuGpu *g = container_of(dev, VuGpu, dev.parent); 947 VuVirtq *vq = vu_get_queue(dev, qidx); 948 VuVirtqElement *elem; 949 size_t len; 950 struct virtio_gpu_update_cursor cursor; 951 952 for (;;) { 953 elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement)); 954 if (!elem) { 955 break; 956 } 957 g_debug("cursor out:%d in:%d\n", elem->out_num, elem->in_num); 958 959 len = iov_to_buf(elem->out_sg, elem->out_num, 960 0, &cursor, sizeof(cursor)); 961 if (len != sizeof(cursor)) { 962 g_warning("%s: cursor size incorrect %zu vs %zu\n", 963 __func__, len, sizeof(cursor)); 964 } else { 965 virtio_gpu_bswap_32(&cursor, sizeof(cursor)); 966 vg_process_cursor_cmd(g, &cursor); 967 } 968 vu_queue_push(dev, vq, elem, 0); 969 vu_queue_notify(dev, vq); 970 free(elem); 971 } 972 } 973 974 static void 975 vg_panic(VuDev *dev, const char *msg) 976 { 977 g_critical("%s\n", msg); 978 exit(1); 979 } 980 981 static void 982 vg_queue_set_started(VuDev *dev, int qidx, bool started) 983 { 984 VuVirtq *vq = vu_get_queue(dev, qidx); 985 986 g_debug("queue started %d:%d\n", qidx, started); 987 988 switch (qidx) { 989 case 0: 990 vu_set_queue_handler(dev, vq, started ? vg_handle_ctrl : NULL); 991 break; 992 case 1: 993 vu_set_queue_handler(dev, vq, started ? vg_handle_cursor : NULL); 994 break; 995 default: 996 break; 997 } 998 } 999 1000 static gboolean 1001 protocol_features_cb(gint fd, GIOCondition condition, gpointer user_data) 1002 { 1003 VuGpu *g = user_data; 1004 uint64_t u64; 1005 VhostUserGpuMsg msg = { 1006 .request = VHOST_USER_GPU_GET_PROTOCOL_FEATURES 1007 }; 1008 1009 if (!vg_recv_msg(g, msg.request, sizeof(u64), &u64)) { 1010 return G_SOURCE_CONTINUE; 1011 } 1012 1013 msg = (VhostUserGpuMsg) { 1014 .request = VHOST_USER_GPU_SET_PROTOCOL_FEATURES, 1015 .size = sizeof(uint64_t), 1016 .payload.u64 = 0 1017 }; 1018 vg_send_msg(g, &msg, -1); 1019 1020 g->wait_in = 0; 1021 vg_handle_ctrl(&g->dev.parent, 0); 1022 1023 return G_SOURCE_REMOVE; 1024 } 1025 1026 static void 1027 set_gpu_protocol_features(VuGpu *g) 1028 { 1029 VhostUserGpuMsg msg = { 1030 .request = VHOST_USER_GPU_GET_PROTOCOL_FEATURES 1031 }; 1032 1033 vg_send_msg(g, &msg, -1); 1034 assert(g->wait_in == 0); 1035 g->wait_in = g_unix_fd_add(g->sock_fd, G_IO_IN | G_IO_HUP, 1036 protocol_features_cb, g); 1037 } 1038 1039 static int 1040 vg_process_msg(VuDev *dev, VhostUserMsg *msg, int *do_reply) 1041 { 1042 VuGpu *g = container_of(dev, VuGpu, dev.parent); 1043 1044 switch (msg->request) { 1045 case VHOST_USER_GPU_SET_SOCKET: { 1046 g_return_val_if_fail(msg->fd_num == 1, 1); 1047 g_return_val_if_fail(g->sock_fd == -1, 1); 1048 g->sock_fd = msg->fds[0]; 1049 set_gpu_protocol_features(g); 1050 return 1; 1051 } 1052 default: 1053 return 0; 1054 } 1055 1056 return 0; 1057 } 1058 1059 static uint64_t 1060 vg_get_features(VuDev *dev) 1061 { 1062 uint64_t features = 0; 1063 1064 if (opt_virgl) { 1065 features |= 1 << VIRTIO_GPU_F_VIRGL; 1066 } 1067 1068 return features; 1069 } 1070 1071 static void 1072 vg_set_features(VuDev *dev, uint64_t features) 1073 { 1074 VuGpu *g = container_of(dev, VuGpu, dev.parent); 1075 bool virgl = features & (1 << VIRTIO_GPU_F_VIRGL); 1076 1077 if (virgl && !g->virgl_inited) { 1078 if (!vg_virgl_init(g)) { 1079 vg_panic(dev, "Failed to initialize virgl"); 1080 } 1081 g->virgl_inited = true; 1082 } 1083 1084 g->virgl = virgl; 1085 } 1086 1087 static int 1088 vg_get_config(VuDev *dev, uint8_t *config, uint32_t len) 1089 { 1090 VuGpu *g = container_of(dev, VuGpu, dev.parent); 1091 1092 if (len > sizeof(struct virtio_gpu_config)) { 1093 return -1; 1094 } 1095 1096 if (opt_virgl) { 1097 g->virtio_config.num_capsets = vg_virgl_get_num_capsets(); 1098 } 1099 1100 memcpy(config, &g->virtio_config, len); 1101 1102 return 0; 1103 } 1104 1105 static int 1106 vg_set_config(VuDev *dev, const uint8_t *data, 1107 uint32_t offset, uint32_t size, 1108 uint32_t flags) 1109 { 1110 VuGpu *g = container_of(dev, VuGpu, dev.parent); 1111 struct virtio_gpu_config *config = (struct virtio_gpu_config *)data; 1112 1113 if (config->events_clear) { 1114 g->virtio_config.events_read &= ~config->events_clear; 1115 } 1116 1117 return 0; 1118 } 1119 1120 static const VuDevIface vuiface = { 1121 .set_features = vg_set_features, 1122 .get_features = vg_get_features, 1123 .queue_set_started = vg_queue_set_started, 1124 .process_msg = vg_process_msg, 1125 .get_config = vg_get_config, 1126 .set_config = vg_set_config, 1127 }; 1128 1129 static void 1130 vg_destroy(VuGpu *g) 1131 { 1132 struct virtio_gpu_simple_resource *res, *tmp; 1133 1134 vug_deinit(&g->dev); 1135 1136 vg_sock_fd_close(g); 1137 1138 QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) { 1139 vg_resource_destroy(g, res); 1140 } 1141 1142 vugbm_device_destroy(&g->gdev); 1143 } 1144 1145 static GOptionEntry entries[] = { 1146 { "print-capabilities", 'c', 0, G_OPTION_ARG_NONE, &opt_print_caps, 1147 "Print capabilities", NULL }, 1148 { "fd", 'f', 0, G_OPTION_ARG_INT, &opt_fdnum, 1149 "Use inherited fd socket", "FDNUM" }, 1150 { "socket-path", 's', 0, G_OPTION_ARG_FILENAME, &opt_socket_path, 1151 "Use UNIX socket path", "PATH" }, 1152 { "render-node", 'r', 0, G_OPTION_ARG_FILENAME, &opt_render_node, 1153 "Specify DRM render node", "PATH" }, 1154 { "virgl", 'v', 0, G_OPTION_ARG_NONE, &opt_virgl, 1155 "Turn virgl rendering on", NULL }, 1156 { NULL, } 1157 }; 1158 1159 int 1160 main(int argc, char *argv[]) 1161 { 1162 GOptionContext *context; 1163 GError *error = NULL; 1164 GMainLoop *loop = NULL; 1165 int fd; 1166 VuGpu g = { .sock_fd = -1, .drm_rnode_fd = -1 }; 1167 1168 QTAILQ_INIT(&g.reslist); 1169 QTAILQ_INIT(&g.fenceq); 1170 1171 context = g_option_context_new("QEMU vhost-user-gpu"); 1172 g_option_context_add_main_entries(context, entries, NULL); 1173 if (!g_option_context_parse(context, &argc, &argv, &error)) { 1174 g_printerr("Option parsing failed: %s\n", error->message); 1175 exit(EXIT_FAILURE); 1176 } 1177 g_option_context_free(context); 1178 1179 if (opt_print_caps) { 1180 g_print("{\n"); 1181 g_print(" \"type\": \"gpu\",\n"); 1182 g_print(" \"features\": [\n"); 1183 g_print(" \"render-node\",\n"); 1184 g_print(" \"virgl\"\n"); 1185 g_print(" ]\n"); 1186 g_print("}\n"); 1187 exit(EXIT_SUCCESS); 1188 } 1189 1190 g.drm_rnode_fd = qemu_drm_rendernode_open(opt_render_node); 1191 if (opt_render_node && g.drm_rnode_fd == -1) { 1192 g_printerr("Failed to open DRM rendernode.\n"); 1193 exit(EXIT_FAILURE); 1194 } 1195 1196 vugbm_device_init(&g.gdev, g.drm_rnode_fd); 1197 1198 if ((!!opt_socket_path + (opt_fdnum != -1)) != 1) { 1199 g_printerr("Please specify either --fd or --socket-path\n"); 1200 exit(EXIT_FAILURE); 1201 } 1202 1203 if (opt_socket_path) { 1204 int lsock = unix_listen(opt_socket_path, &error_fatal); 1205 if (lsock < 0) { 1206 g_printerr("Failed to listen on %s.\n", opt_socket_path); 1207 exit(EXIT_FAILURE); 1208 } 1209 fd = accept(lsock, NULL, NULL); 1210 close(lsock); 1211 } else { 1212 fd = opt_fdnum; 1213 } 1214 if (fd == -1) { 1215 g_printerr("Invalid vhost-user socket.\n"); 1216 exit(EXIT_FAILURE); 1217 } 1218 1219 if (!vug_init(&g.dev, VHOST_USER_GPU_MAX_QUEUES, fd, vg_panic, &vuiface)) { 1220 g_printerr("Failed to initialize libvhost-user-glib.\n"); 1221 exit(EXIT_FAILURE); 1222 } 1223 1224 loop = g_main_loop_new(NULL, FALSE); 1225 g_main_loop_run(loop); 1226 g_main_loop_unref(loop); 1227 1228 vg_destroy(&g); 1229 if (g.drm_rnode_fd >= 0) { 1230 close(g.drm_rnode_fd); 1231 } 1232 1233 return 0; 1234 } 1235