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/error-report.h"
16 #include "qemu/units.h"
18 #include "ui/console.h"
19 #include "hw/virtio/virtio-gpu.h"
20 #include "hw/virtio/virtio-gpu-pixman.h"
22 #include "exec/ramblock.h"
23 #include "sysemu/hostmem.h"
24 #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 res
->iov
[0].iov_len
< 4096) {
137 pdata
= res
->iov
[0].iov_base
;
139 virtio_gpu_create_udmabuf(res
);
140 if (res
->dmabuf_fd
< 0) {
143 virtio_gpu_remap_udmabuf(res
);
144 if (!res
->remapped
) {
147 pdata
= res
->remapped
;
153 void virtio_gpu_fini_udmabuf(struct virtio_gpu_simple_resource
*res
)
156 virtio_gpu_destroy_udmabuf(res
);
160 static void virtio_gpu_free_dmabuf(VirtIOGPU
*g
, VGPUDMABuf
*dmabuf
)
162 struct virtio_gpu_scanout
*scanout
;
164 scanout
= &g
->parent_obj
.scanout
[dmabuf
->scanout_id
];
165 dpy_gl_release_dmabuf(scanout
->con
, &dmabuf
->buf
);
166 QTAILQ_REMOVE(&g
->dmabuf
.bufs
, dmabuf
, next
);
171 *virtio_gpu_create_dmabuf(VirtIOGPU
*g
,
173 struct virtio_gpu_simple_resource
*res
,
174 struct virtio_gpu_framebuffer
*fb
,
175 struct virtio_gpu_rect
*r
)
179 if (res
->dmabuf_fd
< 0) {
183 dmabuf
= g_new0(VGPUDMABuf
, 1);
184 dmabuf
->buf
.width
= r
->width
;
185 dmabuf
->buf
.height
= r
->height
;
186 dmabuf
->buf
.stride
= fb
->stride
;
187 dmabuf
->buf
.x
= r
->x
;
188 dmabuf
->buf
.y
= r
->y
;
189 dmabuf
->buf
.backing_width
= fb
->width
;
190 dmabuf
->buf
.backing_height
= fb
->height
;
191 dmabuf
->buf
.fourcc
= qemu_pixman_to_drm_format(fb
->format
);
192 dmabuf
->buf
.fd
= res
->dmabuf_fd
;
193 dmabuf
->buf
.allow_fences
= true;
194 dmabuf
->buf
.draw_submitted
= false;
195 dmabuf
->scanout_id
= scanout_id
;
196 QTAILQ_INSERT_HEAD(&g
->dmabuf
.bufs
, dmabuf
, next
);
201 int virtio_gpu_update_dmabuf(VirtIOGPU
*g
,
203 struct virtio_gpu_simple_resource
*res
,
204 struct virtio_gpu_framebuffer
*fb
,
205 struct virtio_gpu_rect
*r
)
207 struct virtio_gpu_scanout
*scanout
= &g
->parent_obj
.scanout
[scanout_id
];
208 VGPUDMABuf
*new_primary
, *old_primary
= NULL
;
210 new_primary
= virtio_gpu_create_dmabuf(g
, scanout_id
, res
, fb
, r
);
215 if (g
->dmabuf
.primary
[scanout_id
]) {
216 old_primary
= g
->dmabuf
.primary
[scanout_id
];
219 g
->dmabuf
.primary
[scanout_id
] = new_primary
;
220 qemu_console_resize(scanout
->con
,
221 new_primary
->buf
.width
,
222 new_primary
->buf
.height
);
223 dpy_gl_scanout_dmabuf(scanout
->con
, &new_primary
->buf
);
226 virtio_gpu_free_dmabuf(g
, old_primary
);