2 * Virtio vhost-user GPU Device
4 * Copyright Red Hat, Inc. 2013-2018
7 * Dave Airlie <airlied@redhat.com>
8 * Gerd Hoffmann <kraxel@redhat.com>
9 * Marc-André Lureau <marcandre.lureau@redhat.com>
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.
14 #include "qemu/osdep.h"
16 #include "qapi/error.h"
17 #include "qemu/sockets.h"
20 #include <glib-unix.h>
23 #include "hw/virtio/virtio-gpu-bswap.h"
24 #include "hw/virtio/virtio-gpu-pixman.h"
29 VHOST_USER_GPU_MAX_QUEUES
= 2,
32 struct virtio_gpu_simple_resource
{
39 uint32_t scanout_bitmask
;
40 pixman_image_t
*image
;
41 struct vugbm_buffer buffer
;
42 QTAILQ_ENTRY(virtio_gpu_simple_resource
) next
;
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
;
51 static void vg_handle_ctrl(VuDev
*dev
, int qidx
);
54 vg_cmd_to_string(int cmd
)
56 #define CMD(cmd) [cmd] = #cmd
57 static const char *vg_cmd_str
[] = {
58 CMD(VIRTIO_GPU_UNDEFINED
),
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
),
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
),
83 CMD(VIRTIO_GPU_CMD_UPDATE_CURSOR
),
84 CMD(VIRTIO_GPU_CMD_MOVE_CURSOR
),
88 if (cmd
>= 0 && cmd
< G_N_ELEMENTS(vg_cmd_str
)) {
89 return vg_cmd_str
[cmd
];
96 vg_sock_fd_read(int sock
, void *buf
, ssize_t buflen
)
101 ret
= read(sock
, buf
, buflen
);
102 } while (ret
< 0 && (errno
== EINTR
|| errno
== EAGAIN
));
104 g_warn_if_fail(ret
== buflen
);
109 vg_sock_fd_close(VuGpu
*g
)
111 if (g
->sock_fd
>= 0) {
118 source_wait_cb(gint fd
, GIOCondition condition
, gpointer user_data
)
120 VuGpu
*g
= user_data
;
122 if (!vg_recv_msg(g
, VHOST_USER_GPU_DMABUF_UPDATE
, 0, NULL
)) {
123 return G_SOURCE_CONTINUE
;
128 vg_handle_ctrl(&g
->dev
.parent
, 0);
130 return G_SOURCE_REMOVE
;
136 assert(g
->wait_in
== 0);
137 g
->wait_in
= g_unix_fd_add(g
->sock_fd
, G_IO_IN
| G_IO_HUP
,
142 vg_sock_fd_write(int sock
, const void *buf
, ssize_t buflen
, int fd
)
146 .iov_base
= (void *)buf
,
149 struct msghdr msg
= {
154 struct cmsghdr cmsghdr
;
155 char control
[CMSG_SPACE(sizeof(int))];
157 struct cmsghdr
*cmsg
;
160 msg
.msg_control
= cmsgu
.control
;
161 msg
.msg_controllen
= sizeof(cmsgu
.control
);
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
;
168 *((int *)CMSG_DATA(cmsg
)) = fd
;
172 ret
= sendmsg(sock
, &msg
, 0);
173 } while (ret
== -1 && (errno
== EINTR
|| errno
== EAGAIN
));
175 g_warn_if_fail(ret
== buflen
);
180 vg_send_msg(VuGpu
*vg
, const VhostUserGpuMsg
*msg
, int fd
)
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
);
189 vg_recv_msg(VuGpu
*g
, uint32_t expect_req
, uint32_t expect_size
,
192 uint32_t req
, flags
, size
;
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) {
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);
204 if (size
&& vg_sock_fd_read(g
->sock_fd
, payload
, size
) != size
) {
215 static struct virtio_gpu_simple_resource
*
216 virtio_gpu_find_resource(VuGpu
*g
, uint32_t resource_id
)
218 struct virtio_gpu_simple_resource
*res
;
220 QTAILQ_FOREACH(res
, &g
->reslist
, next
) {
221 if (res
->resource_id
== resource_id
) {
229 vg_ctrl_response(VuGpu
*g
,
230 struct virtio_gpu_ctrl_command
*cmd
,
231 struct virtio_gpu_ctrl_hdr
*resp
,
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
;
241 virtio_gpu_ctrl_hdr_bswap(resp
);
242 s
= iov_from_buf(cmd
->elem
.in_sg
, cmd
->elem
.in_num
, 0, resp
, resp_len
);
244 g_critical("%s: response size incorrect %zu vs %zu",
245 __func__
, s
, resp_len
);
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
;
253 vg_ctrl_response_nodata(VuGpu
*g
,
254 struct virtio_gpu_ctrl_command
*cmd
,
255 enum virtio_gpu_ctrl_type type
)
257 struct virtio_gpu_ctrl_hdr resp
= {
261 vg_ctrl_response(g
, cmd
, &resp
, sizeof(resp
));
266 get_display_info_cb(gint fd
, GIOCondition condition
, gpointer user_data
)
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
);
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
;
279 QTAILQ_REMOVE(&vg
->fenceq
, cmd
, next
);
280 vg_ctrl_response(vg
, cmd
, &dpy_info
.hdr
, sizeof(dpy_info
));
283 vg_handle_ctrl(&vg
->dev
.parent
, 0);
285 return G_SOURCE_REMOVE
;
289 vg_get_display_info(VuGpu
*vg
, struct virtio_gpu_ctrl_command
*cmd
)
291 VhostUserGpuMsg msg
= {
292 .request
= VHOST_USER_GPU_GET_DISPLAY_INFO
,
296 assert(vg
->wait_in
== 0);
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
;
305 vg_resource_create_2d(VuGpu
*g
,
306 struct virtio_gpu_ctrl_command
*cmd
)
308 pixman_format_code_t pformat
;
309 struct virtio_gpu_simple_resource
*res
;
310 struct virtio_gpu_resource_create_2d c2d
;
313 virtio_gpu_bswap_32(&c2d
, sizeof(c2d
));
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
;
321 res
= virtio_gpu_find_resource(g
, c2d
.resource_id
);
323 g_critical("%s: resource already exists %d", __func__
, c2d
.resource_id
);
324 cmd
->error
= VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID
;
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
;
334 pformat
= virtio_gpu_get_pixman_format(c2d
.format
);
336 g_critical("%s: host couldn't handle guest format %d",
337 __func__
, c2d
.format
);
339 cmd
->error
= VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER
;
342 vugbm_buffer_create(&res
->buffer
, &g
->gdev
, c2d
.width
, c2d
.height
);
343 res
->image
= pixman_image_create_bits(pformat
,
346 (uint32_t *)res
->buffer
.mmap
,
349 g_critical("%s: resource creation failed %d %d %d",
350 __func__
, c2d
.resource_id
, c2d
.width
, c2d
.height
);
352 cmd
->error
= VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY
;
356 QTAILQ_INSERT_HEAD(&g
->reslist
, res
, next
);
360 vg_disable_scanout(VuGpu
*g
, int scanout_id
)
362 struct virtio_gpu_scanout
*scanout
= &g
->scanout
[scanout_id
];
363 struct virtio_gpu_simple_resource
*res
;
365 if (scanout
->resource_id
== 0) {
369 res
= virtio_gpu_find_resource(g
, scanout
->resource_id
);
371 res
->scanout_bitmask
&= ~(1 << scanout_id
);
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
,
383 vg_send_msg(g
, &msg
, -1);
388 vg_resource_destroy(VuGpu
*g
,
389 struct virtio_gpu_simple_resource
*res
)
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
);
401 vugbm_buffer_destroy(&res
->buffer
);
402 pixman_image_unref(res
->image
);
403 QTAILQ_REMOVE(&g
->reslist
, res
, next
);
408 vg_resource_unref(VuGpu
*g
,
409 struct virtio_gpu_ctrl_command
*cmd
)
411 struct virtio_gpu_simple_resource
*res
;
412 struct virtio_gpu_resource_unref unref
;
414 VUGPU_FILL_CMD(unref
);
415 virtio_gpu_bswap_32(&unref
, sizeof(unref
));
417 res
= virtio_gpu_find_resource(g
, unref
.resource_id
);
419 g_critical("%s: illegal resource specified %d",
420 __func__
, unref
.resource_id
);
421 cmd
->error
= VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID
;
424 vg_resource_destroy(g
, res
);
428 vg_create_mapping_iov(VuGpu
*g
,
429 struct virtio_gpu_resource_attach_backing
*ab
,
430 struct virtio_gpu_ctrl_command
*cmd
,
433 struct virtio_gpu_mem_entry
*ents
;
437 if (ab
->nr_entries
> 16384) {
438 g_critical("%s: nr_entries is too big (%d > 16384)",
439 __func__
, ab
->nr_entries
);
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
);
448 g_critical("%s: command data size incorrect %zu vs %zu",
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
);
473 vg_resource_attach_backing(VuGpu
*g
,
474 struct virtio_gpu_ctrl_command
*cmd
)
476 struct virtio_gpu_simple_resource
*res
;
477 struct virtio_gpu_resource_attach_backing ab
;
481 virtio_gpu_bswap_32(&ab
, sizeof(ab
));
483 res
= virtio_gpu_find_resource(g
, ab
.resource_id
);
485 g_critical("%s: illegal resource specified %d",
486 __func__
, ab
.resource_id
);
487 cmd
->error
= VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID
;
491 ret
= vg_create_mapping_iov(g
, &ab
, cmd
, &res
->iov
);
493 cmd
->error
= VIRTIO_GPU_RESP_ERR_UNSPEC
;
497 res
->iov_cnt
= ab
.nr_entries
;
501 vg_resource_detach_backing(VuGpu
*g
,
502 struct virtio_gpu_ctrl_command
*cmd
)
504 struct virtio_gpu_simple_resource
*res
;
505 struct virtio_gpu_resource_detach_backing detach
;
507 VUGPU_FILL_CMD(detach
);
508 virtio_gpu_bswap_32(&detach
, sizeof(detach
));
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
;
524 vg_transfer_to_host_2d(VuGpu
*g
,
525 struct virtio_gpu_ctrl_command
*cmd
)
527 struct virtio_gpu_simple_resource
*res
;
529 uint32_t src_offset
, dst_offset
, stride
;
531 pixman_format_code_t format
;
532 struct virtio_gpu_transfer_to_host_2d t2d
;
535 virtio_gpu_t2d_bswap(&t2d
);
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
;
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
;
559 format
= pixman_image_get_format(res
->image
);
560 bpp
= (PIXMAN_FORMAT_BPP(format
) + 7) / 8;
561 stride
= pixman_image_get_stride(res
->image
);
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
);
570 iov_to_buf(res
->iov
, res
->iov_cnt
, src_offset
,
572 + dst_offset
, t2d
.r
.width
* bpp
);
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
));
583 vg_set_scanout(VuGpu
*g
,
584 struct virtio_gpu_ctrl_command
*cmd
)
586 struct virtio_gpu_simple_resource
*res
, *ores
;
587 struct virtio_gpu_scanout
*scanout
;
588 struct virtio_gpu_set_scanout ss
;
592 virtio_gpu_bswap_32(&ss
, sizeof(ss
));
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
;
601 if (ss
.resource_id
== 0) {
602 vg_disable_scanout(g
, ss
.scanout_id
);
606 /* create a surface for this scanout */
607 res
= virtio_gpu_find_resource(g
, ss
.resource_id
);
609 g_critical("%s: illegal resource specified %d",
610 __func__
, ss
.resource_id
);
611 cmd
->error
= VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID
;
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
;
629 scanout
= &g
->scanout
[ss
.scanout_id
];
631 ores
= virtio_gpu_find_resource(g
, scanout
->resource_id
);
633 ores
->scanout_bitmask
&= ~(1 << ss
.scanout_id
);
636 res
->scanout_bitmask
|= (1 << ss
.scanout_id
);
637 scanout
->resource_id
= ss
.resource_id
;
640 scanout
->width
= ss
.r
.width
;
641 scanout
->height
= ss
.r
.height
;
643 struct vugbm_buffer
*buffer
= &res
->buffer
;
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
,
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
662 if (vugbm_buffer_get_dmabuf_fd(buffer
, &fd
)) {
663 vg_send_msg(g
, &msg
, fd
);
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
676 vg_send_msg(g
, &msg
, -1);
681 vg_resource_flush(VuGpu
*g
,
682 struct virtio_gpu_ctrl_command
*cmd
)
684 struct virtio_gpu_simple_resource
*res
;
685 struct virtio_gpu_resource_flush rf
;
686 pixman_region16_t flush_region
;
690 virtio_gpu_bswap_32(&rf
, sizeof(rf
));
692 res
= virtio_gpu_find_resource(g
, rf
.resource_id
);
694 g_critical("%s: illegal resource specified %d\n",
695 __func__
, rf
.resource_id
);
696 cmd
->error
= VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID
;
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
;
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
;
721 if (!(res
->scanout_bitmask
& (1 << i
))) {
724 scanout
= &g
->scanout
[i
];
726 pixman_region_init(&finalregion
);
727 pixman_region_init_rect(®ion
, scanout
->x
, scanout
->y
,
728 scanout
->width
, scanout
->height
);
730 pixman_region_intersect(&finalregion
, &flush_region
, ®ion
);
732 extents
= pixman_region_extents(&finalregion
);
733 size_t width
= extents
->x2
- extents
->x1
;
734 size_t height
= extents
->y2
- extents
->y1
;
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
) {
748 vg_send_msg(g
, &vmsg
, -1);
752 PIXMAN_FORMAT_BPP(pixman_image_get_format(res
->image
)) / 8;
753 size_t size
= width
* height
* bpp
;
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
) {
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
),
774 pixman_image_composite(PIXMAN_OP_SRC
,
776 extents
->x1
, extents
->y1
,
779 pixman_image_unref(i
);
780 vg_send_msg(g
, msg
, -1);
783 pixman_region_fini(®ion
);
784 pixman_region_fini(&finalregion
);
786 pixman_region_fini(&flush_region
);
790 vg_process_cmd(VuGpu
*vg
, struct virtio_gpu_ctrl_command
*cmd
)
792 switch (cmd
->cmd_hdr
.type
) {
793 case VIRTIO_GPU_CMD_GET_DISPLAY_INFO
:
794 vg_get_display_info(vg
, cmd
);
796 case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D
:
797 vg_resource_create_2d(vg
, cmd
);
799 case VIRTIO_GPU_CMD_RESOURCE_UNREF
:
800 vg_resource_unref(vg
, cmd
);
802 case VIRTIO_GPU_CMD_RESOURCE_FLUSH
:
803 vg_resource_flush(vg
, cmd
);
805 case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D
:
806 vg_transfer_to_host_2d(vg
, cmd
);
808 case VIRTIO_GPU_CMD_SET_SCANOUT
:
809 vg_set_scanout(vg
, cmd
);
811 case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING
:
812 vg_resource_attach_backing(vg
, cmd
);
814 case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING
:
815 vg_resource_detach_backing(vg
, cmd
);
817 /* case VIRTIO_GPU_CMD_GET_EDID: */
820 g_warning("TODO handle ctrl %x\n", cmd
->cmd_hdr
.type
);
821 cmd
->error
= VIRTIO_GPU_RESP_ERR_UNSPEC
;
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
);
831 vg_handle_ctrl(VuDev
*dev
, int qidx
)
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
;
839 if (vg
->wait_in
!= 0) {
843 cmd
= vu_queue_pop(dev
, vq
, sizeof(struct virtio_gpu_ctrl_command
));
849 cmd
->state
= VG_CMD_STATE_NEW
;
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
));
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
));
863 vg_virgl_process_cmd(vg
, cmd
);
865 vg_process_cmd(vg
, cmd
);
868 if (cmd
->state
!= VG_CMD_STATE_FINISHED
) {
869 QTAILQ_INSERT_TAIL(&vg
->fenceq
, cmd
, next
);
878 update_cursor_data_simple(VuGpu
*g
, uint32_t resource_id
, gpointer data
)
880 struct virtio_gpu_simple_resource
*res
;
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);
887 PIXMAN_FORMAT_BPP(pixman_image_get_format(res
->image
)) == 32);
889 memcpy(data
, pixman_image_get_data(res
->image
), 64 * 64 * sizeof(uint32_t));
893 vg_process_cursor_cmd(VuGpu
*g
, struct virtio_gpu_update_cursor
*cursor
)
895 bool move
= cursor
->hdr
.type
!= VIRTIO_GPU_CMD_MOVE_CURSOR
;
897 g_debug("%s move:%d\n", G_STRFUNC
, 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
,
910 vg_send_msg(g
, &msg
, -1);
912 VhostUserGpuMsg msg
= {
913 .request
= VHOST_USER_GPU_CURSOR_UPDATE
,
914 .size
= sizeof(VhostUserGpuCursorUpdate
),
915 .payload
.cursor_update
= {
917 .scanout_id
= cursor
->pos
.scanout_id
,
921 .hot_x
= cursor
->hot_x
,
922 .hot_y
= cursor
->hot_y
,
926 vg_virgl_update_cursor_data(g
, cursor
->resource_id
,
927 msg
.payload
.cursor_update
.data
);
929 update_cursor_data_simple(g
, cursor
->resource_id
,
930 msg
.payload
.cursor_update
.data
);
932 vg_send_msg(g
, &msg
, -1);
937 vg_handle_cursor(VuDev
*dev
, int qidx
)
939 VuGpu
*g
= container_of(dev
, VuGpu
, dev
.parent
);
940 VuVirtq
*vq
= vu_get_queue(dev
, qidx
);
941 VuVirtqElement
*elem
;
943 struct virtio_gpu_update_cursor cursor
;
946 elem
= vu_queue_pop(dev
, vq
, sizeof(VuVirtqElement
));
950 g_debug("cursor out:%d in:%d\n", elem
->out_num
, elem
->in_num
);
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
));
958 virtio_gpu_bswap_32(&cursor
, sizeof(cursor
));
959 vg_process_cursor_cmd(g
, &cursor
);
961 vu_queue_push(dev
, vq
, elem
, 0);
962 vu_queue_notify(dev
, vq
);
968 vg_panic(VuDev
*dev
, const char *msg
)
970 g_critical("%s\n", msg
);
975 vg_queue_set_started(VuDev
*dev
, int qidx
, bool started
)
977 VuVirtq
*vq
= vu_get_queue(dev
, qidx
);
979 g_debug("queue started %d:%d\n", qidx
, started
);
983 vu_set_queue_handler(dev
, vq
, started
? vg_handle_ctrl
: NULL
);
986 vu_set_queue_handler(dev
, vq
, started
? vg_handle_cursor
: NULL
);
994 protocol_features_cb(gint fd
, GIOCondition condition
, gpointer user_data
)
996 VuGpu
*g
= user_data
;
998 VhostUserGpuMsg msg
= {
999 .request
= VHOST_USER_GPU_GET_PROTOCOL_FEATURES
1002 if (!vg_recv_msg(g
, msg
.request
, sizeof(u64
), &u64
)) {
1003 return G_SOURCE_CONTINUE
;
1006 msg
= (VhostUserGpuMsg
) {
1007 .request
= VHOST_USER_GPU_SET_PROTOCOL_FEATURES
,
1008 .size
= sizeof(uint64_t),
1011 vg_send_msg(g
, &msg
, -1);
1014 vg_handle_ctrl(&g
->dev
.parent
, 0);
1016 return G_SOURCE_REMOVE
;
1020 set_gpu_protocol_features(VuGpu
*g
)
1022 VhostUserGpuMsg msg
= {
1023 .request
= VHOST_USER_GPU_GET_PROTOCOL_FEATURES
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
);
1033 vg_process_msg(VuDev
*dev
, VhostUserMsg
*msg
, int *do_reply
)
1035 VuGpu
*g
= container_of(dev
, VuGpu
, dev
.parent
);
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
);
1053 vg_get_features(VuDev
*dev
)
1055 uint64_t features
= 0;
1058 features
|= 1 << VIRTIO_GPU_F_VIRGL
;
1065 vg_set_features(VuDev
*dev
, uint64_t features
)
1067 VuGpu
*g
= container_of(dev
, VuGpu
, dev
.parent
);
1068 bool virgl
= features
& (1 << VIRTIO_GPU_F_VIRGL
);
1070 if (virgl
&& !g
->virgl_inited
) {
1071 if (!vg_virgl_init(g
)) {
1072 vg_panic(dev
, "Failed to initialize virgl");
1074 g
->virgl_inited
= true;
1081 vg_get_config(VuDev
*dev
, uint8_t *config
, uint32_t len
)
1083 VuGpu
*g
= container_of(dev
, VuGpu
, dev
.parent
);
1085 if (len
> sizeof(struct virtio_gpu_config
)) {
1090 g
->virtio_config
.num_capsets
= vg_virgl_get_num_capsets();
1093 memcpy(config
, &g
->virtio_config
, len
);
1099 vg_set_config(VuDev
*dev
, const uint8_t *data
,
1100 uint32_t offset
, uint32_t size
,
1103 VuGpu
*g
= container_of(dev
, VuGpu
, dev
.parent
);
1104 struct virtio_gpu_config
*config
= (struct virtio_gpu_config
*)data
;
1106 if (config
->events_clear
) {
1107 g
->virtio_config
.events_read
&= ~config
->events_clear
;
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
,
1123 vg_destroy(VuGpu
*g
)
1125 struct virtio_gpu_simple_resource
*res
, *tmp
;
1127 vug_deinit(&g
->dev
);
1129 vg_sock_fd_close(g
);
1131 QTAILQ_FOREACH_SAFE(res
, &g
->reslist
, next
, tmp
) {
1132 vg_resource_destroy(g
, res
);
1135 vugbm_device_destroy(&g
->gdev
);
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
},
1153 main(int argc
, char *argv
[])
1155 GOptionContext
*context
;
1156 GError
*error
= NULL
;
1157 GMainLoop
*loop
= NULL
;
1159 VuGpu g
= { .sock_fd
= -1, .drm_rnode_fd
= -1 };
1161 QTAILQ_INIT(&g
.reslist
);
1162 QTAILQ_INIT(&g
.fenceq
);
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
);
1170 g_option_context_free(context
);
1172 if (opt_print_caps
) {
1174 g_print(" \"type\": \"gpu\",\n");
1175 g_print(" \"features\": [\n");
1176 g_print(" \"render-node\",\n");
1177 g_print(" \"virgl\"\n");
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");
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");
1195 if ((!!opt_socket_path
+ (opt_fdnum
!= -1)) != 1) {
1196 g_printerr("Please specify either --fd or --socket-path\n");
1200 if (opt_socket_path
) {
1201 int lsock
= unix_listen(opt_socket_path
, &error_fatal
);
1203 g_printerr("Failed to listen on %s.\n", opt_socket_path
);
1206 fd
= accept(lsock
, NULL
, NULL
);
1212 g_printerr("Invalid vhost-user socket.\n");
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");
1221 loop
= g_main_loop_new(NULL
, FALSE
);
1222 g_main_loop_run(loop
);
1223 g_main_loop_unref(loop
);
1226 if (g
.drm_rnode_fd
>= 0) {
1227 close(g
.drm_rnode_fd
);