4 * Copyright Red Hat, Inc. 2013-2014
7 * Dave Airlie <airlied@redhat.com>
8 * Gerd Hoffmann <kraxel@redhat.com>
10 * This work is licensed under the terms of the GNU GPL, version 2 or later.
11 * See the COPYING file in the top-level directory.
14 #include "qemu/osdep.h"
15 #include "qemu/units.h"
17 #include "ui/console.h"
18 #include "hw/virtio/virtio-gpu.h"
19 #include "hw/virtio/virtio-gpu-pixman.h"
21 #include "exec/ramblock.h"
22 #include "sysemu/hostmem.h"
23 #include <sys/ioctl.h>
25 #include <linux/memfd.h>
26 #include "qemu/memfd.h"
27 #include "standard-headers/linux/udmabuf.h"
29 static void virtio_gpu_create_udmabuf(struct virtio_gpu_simple_resource
*res
)
31 struct udmabuf_create_list
*list
;
36 udmabuf
= udmabuf_fd();
41 list
= g_malloc0(sizeof(struct udmabuf_create_list
) +
42 sizeof(struct udmabuf_create_item
) * res
->iov_cnt
);
44 for (i
= 0; i
< res
->iov_cnt
; i
++) {
46 rb
= qemu_ram_block_from_host(res
->iov
[i
].iov_base
, false, &offset
);
49 if (!rb
|| rb
->fd
< 0) {
54 list
->list
[i
].memfd
= rb
->fd
;
55 list
->list
[i
].offset
= offset
;
56 list
->list
[i
].size
= res
->iov
[i
].iov_len
;
59 list
->count
= res
->iov_cnt
;
60 list
->flags
= UDMABUF_FLAGS_CLOEXEC
;
62 res
->dmabuf_fd
= ioctl(udmabuf
, UDMABUF_CREATE_LIST
, list
);
63 if (res
->dmabuf_fd
< 0) {
64 warn_report("%s: UDMABUF_CREATE_LIST: %s", __func__
,
70 static void virtio_gpu_remap_udmabuf(struct virtio_gpu_simple_resource
*res
)
72 res
->remapped
= mmap(NULL
, res
->blob_size
, PROT_READ
,
73 MAP_SHARED
, res
->dmabuf_fd
, 0);
74 if (res
->remapped
== MAP_FAILED
) {
75 warn_report("%s: dmabuf mmap failed: %s", __func__
,
81 static void virtio_gpu_destroy_udmabuf(struct virtio_gpu_simple_resource
*res
)
84 munmap(res
->remapped
, res
->blob_size
);
87 if (res
->dmabuf_fd
>= 0) {
88 close(res
->dmabuf_fd
);
93 static int find_memory_backend_type(Object
*obj
, void *opaque
)
95 bool *memfd_backend
= opaque
;
98 if (object_dynamic_cast(obj
, TYPE_MEMORY_BACKEND
)) {
99 HostMemoryBackend
*backend
= MEMORY_BACKEND(obj
);
100 RAMBlock
*rb
= backend
->mr
.ram_block
;
102 if (rb
&& rb
->fd
> 0) {
103 ret
= fcntl(rb
->fd
, F_GET_SEALS
);
105 *memfd_backend
= true;
113 bool virtio_gpu_have_udmabuf(void)
117 bool memfd_backend
= false;
119 udmabuf
= udmabuf_fd();
124 memdev_root
= object_resolve_path("/objects", NULL
);
125 object_child_foreach(memdev_root
, find_memory_backend_type
, &memfd_backend
);
127 return memfd_backend
;
130 void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource
*res
)
135 if (res
->iov_cnt
== 1) {
136 pdata
= res
->iov
[0].iov_base
;
138 virtio_gpu_create_udmabuf(res
);
139 if (res
->dmabuf_fd
< 0) {
142 virtio_gpu_remap_udmabuf(res
);
143 if (!res
->remapped
) {
146 pdata
= res
->remapped
;
152 void virtio_gpu_fini_udmabuf(struct virtio_gpu_simple_resource
*res
)
155 virtio_gpu_destroy_udmabuf(res
);
159 static void virtio_gpu_free_dmabuf(VirtIOGPU
*g
, VGPUDMABuf
*dmabuf
)
161 struct virtio_gpu_scanout
*scanout
;
163 scanout
= &g
->parent_obj
.scanout
[dmabuf
->scanout_id
];
164 dpy_gl_release_dmabuf(scanout
->con
, &dmabuf
->buf
);
165 QTAILQ_REMOVE(&g
->dmabuf
.bufs
, dmabuf
, next
);
170 *virtio_gpu_create_dmabuf(VirtIOGPU
*g
,
172 struct virtio_gpu_simple_resource
*res
,
173 struct virtio_gpu_framebuffer
*fb
,
174 struct virtio_gpu_rect
*r
)
178 if (res
->dmabuf_fd
< 0) {
182 dmabuf
= g_new0(VGPUDMABuf
, 1);
183 dmabuf
->buf
.width
= fb
->width
;
184 dmabuf
->buf
.height
= fb
->height
;
185 dmabuf
->buf
.stride
= fb
->stride
;
186 dmabuf
->buf
.x
= r
->x
;
187 dmabuf
->buf
.y
= r
->y
;
188 dmabuf
->buf
.scanout_width
= r
->width
;
189 dmabuf
->buf
.scanout_height
= r
->height
;
190 dmabuf
->buf
.fourcc
= qemu_pixman_to_drm_format(fb
->format
);
191 dmabuf
->buf
.fd
= res
->dmabuf_fd
;
192 dmabuf
->buf
.allow_fences
= true;
193 dmabuf
->buf
.draw_submitted
= false;
194 dmabuf
->scanout_id
= scanout_id
;
195 QTAILQ_INSERT_HEAD(&g
->dmabuf
.bufs
, dmabuf
, next
);
200 int virtio_gpu_update_dmabuf(VirtIOGPU
*g
,
202 struct virtio_gpu_simple_resource
*res
,
203 struct virtio_gpu_framebuffer
*fb
,
204 struct virtio_gpu_rect
*r
)
206 struct virtio_gpu_scanout
*scanout
= &g
->parent_obj
.scanout
[scanout_id
];
207 VGPUDMABuf
*new_primary
, *old_primary
= NULL
;
209 new_primary
= virtio_gpu_create_dmabuf(g
, scanout_id
, res
, fb
, r
);
214 if (g
->dmabuf
.primary
[scanout_id
]) {
215 old_primary
= g
->dmabuf
.primary
[scanout_id
];
218 g
->dmabuf
.primary
[scanout_id
] = new_primary
;
219 qemu_console_resize(scanout
->con
,
220 new_primary
->buf
.scanout_width
,
221 new_primary
->buf
.scanout_height
);
222 dpy_gl_scanout_dmabuf(scanout
->con
, &new_primary
->buf
);
225 virtio_gpu_free_dmabuf(g
, old_primary
);