xref: /qemu/hw/display/virtio-gpu-udmabuf.c (revision 3e1210e8b0d86a2190e51632985c3da3e5721336)
1 /*
2  * Virtio GPU Device
3  *
4  * Copyright Red Hat, Inc. 2013-2014
5  *
6  * Authors:
7  *     Dave Airlie <airlied@redhat.com>
8  *     Gerd Hoffmann <kraxel@redhat.com>
9  *
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.
12  */
13 
14 #include "qemu/osdep.h"
15 #include "qemu/error-report.h"
16 #include "qemu/units.h"
17 #include "qemu/iov.h"
18 #include "ui/console.h"
19 #include "hw/virtio/virtio-gpu.h"
20 #include "hw/virtio/virtio-gpu-pixman.h"
21 #include "trace.h"
22 #include "system/ramblock.h"
23 #include "system/hostmem.h"
24 #include <sys/ioctl.h>
25 #include <linux/memfd.h>
26 #include "qemu/memfd.h"
27 #include "standard-headers/linux/udmabuf.h"
28 #include "standard-headers/drm/drm_fourcc.h"
29 
virtio_gpu_create_udmabuf(struct virtio_gpu_simple_resource * res)30 static void virtio_gpu_create_udmabuf(struct virtio_gpu_simple_resource *res)
31 {
32     struct udmabuf_create_list *list;
33     RAMBlock *rb;
34     ram_addr_t offset;
35     int udmabuf, i;
36 
37     udmabuf = udmabuf_fd();
38     if (udmabuf < 0) {
39         return;
40     }
41 
42     list = g_malloc0(sizeof(struct udmabuf_create_list) +
43                      sizeof(struct udmabuf_create_item) * res->iov_cnt);
44 
45     for (i = 0; i < res->iov_cnt; i++) {
46         rcu_read_lock();
47         rb = qemu_ram_block_from_host(res->iov[i].iov_base, false, &offset);
48         rcu_read_unlock();
49 
50         if (!rb || rb->fd < 0) {
51             g_free(list);
52             return;
53         }
54 
55         list->list[i].memfd  = rb->fd;
56         list->list[i].offset = offset;
57         list->list[i].size   = res->iov[i].iov_len;
58     }
59 
60     list->count = res->iov_cnt;
61     list->flags = UDMABUF_FLAGS_CLOEXEC;
62 
63     res->dmabuf_fd = ioctl(udmabuf, UDMABUF_CREATE_LIST, list);
64     if (res->dmabuf_fd < 0) {
65         warn_report("%s: UDMABUF_CREATE_LIST: %s", __func__,
66                     strerror(errno));
67     }
68     g_free(list);
69 }
70 
virtio_gpu_remap_udmabuf(struct virtio_gpu_simple_resource * res)71 static void virtio_gpu_remap_udmabuf(struct virtio_gpu_simple_resource *res)
72 {
73     res->remapped = mmap(NULL, res->blob_size, PROT_READ,
74                          MAP_SHARED, res->dmabuf_fd, 0);
75     if (res->remapped == MAP_FAILED) {
76         warn_report("%s: dmabuf mmap failed: %s", __func__,
77                     strerror(errno));
78         res->remapped = NULL;
79     }
80 }
81 
virtio_gpu_destroy_udmabuf(struct virtio_gpu_simple_resource * res)82 static void virtio_gpu_destroy_udmabuf(struct virtio_gpu_simple_resource *res)
83 {
84     if (res->remapped) {
85         munmap(res->remapped, res->blob_size);
86         res->remapped = NULL;
87     }
88     if (res->dmabuf_fd >= 0) {
89         close(res->dmabuf_fd);
90         res->dmabuf_fd = -1;
91     }
92 }
93 
find_memory_backend_type(Object * obj,void * opaque)94 static int find_memory_backend_type(Object *obj, void *opaque)
95 {
96     bool *memfd_backend = opaque;
97     int ret;
98 
99     if (object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) {
100         HostMemoryBackend *backend = MEMORY_BACKEND(obj);
101         RAMBlock *rb = backend->mr.ram_block;
102 
103         if (rb && rb->fd > 0) {
104             ret = fcntl(rb->fd, F_GET_SEALS);
105             if (ret > 0) {
106                 *memfd_backend = true;
107             }
108         }
109     }
110 
111     return 0;
112 }
113 
virtio_gpu_have_udmabuf(void)114 bool virtio_gpu_have_udmabuf(void)
115 {
116     Object *memdev_root;
117     int udmabuf;
118     bool memfd_backend = false;
119 
120     udmabuf = udmabuf_fd();
121     if (udmabuf < 0) {
122         return false;
123     }
124 
125     memdev_root = object_resolve_path("/objects", NULL);
126     object_child_foreach(memdev_root, find_memory_backend_type, &memfd_backend);
127 
128     return memfd_backend;
129 }
130 
virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource * res)131 void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res)
132 {
133     void *pdata = NULL;
134 
135     res->dmabuf_fd = -1;
136     if (res->iov_cnt == 1 &&
137         res->iov[0].iov_len < 4096) {
138         pdata = res->iov[0].iov_base;
139     } else {
140         virtio_gpu_create_udmabuf(res);
141         if (res->dmabuf_fd < 0) {
142             return;
143         }
144         virtio_gpu_remap_udmabuf(res);
145         if (!res->remapped) {
146             return;
147         }
148         pdata = res->remapped;
149     }
150 
151     res->blob = pdata;
152 }
153 
virtio_gpu_fini_udmabuf(struct virtio_gpu_simple_resource * res)154 void virtio_gpu_fini_udmabuf(struct virtio_gpu_simple_resource *res)
155 {
156     if (res->remapped) {
157         virtio_gpu_destroy_udmabuf(res);
158     }
159 }
160 
virtio_gpu_free_dmabuf(VirtIOGPU * g,VGPUDMABuf * dmabuf)161 static void virtio_gpu_free_dmabuf(VirtIOGPU *g, VGPUDMABuf *dmabuf)
162 {
163     struct virtio_gpu_scanout *scanout;
164 
165     scanout = &g->parent_obj.scanout[dmabuf->scanout_id];
166     dpy_gl_release_dmabuf(scanout->con, dmabuf->buf);
167     g_clear_pointer(&dmabuf->buf, qemu_dmabuf_free);
168     QTAILQ_REMOVE(&g->dmabuf.bufs, dmabuf, next);
169     g_free(dmabuf);
170 }
171 
172 static VGPUDMABuf
virtio_gpu_create_dmabuf(VirtIOGPU * g,uint32_t scanout_id,struct virtio_gpu_simple_resource * res,struct virtio_gpu_framebuffer * fb,struct virtio_gpu_rect * r)173 *virtio_gpu_create_dmabuf(VirtIOGPU *g,
174                           uint32_t scanout_id,
175                           struct virtio_gpu_simple_resource *res,
176                           struct virtio_gpu_framebuffer *fb,
177                           struct virtio_gpu_rect *r)
178 {
179     VGPUDMABuf *dmabuf;
180     uint32_t offset = 0;
181 
182     if (res->dmabuf_fd < 0) {
183         return NULL;
184     }
185 
186     dmabuf = g_new0(VGPUDMABuf, 1);
187     dmabuf->buf = qemu_dmabuf_new(r->width, r->height,
188                                   &offset, &fb->stride,
189                                   r->x, r->y, fb->width, fb->height,
190                                   qemu_pixman_to_drm_format(fb->format),
191                                   DRM_FORMAT_MOD_INVALID, &res->dmabuf_fd,
192                                   1, true, false);
193     dmabuf->scanout_id = scanout_id;
194     QTAILQ_INSERT_HEAD(&g->dmabuf.bufs, dmabuf, next);
195 
196     return dmabuf;
197 }
198 
virtio_gpu_update_dmabuf(VirtIOGPU * g,uint32_t scanout_id,struct virtio_gpu_simple_resource * res,struct virtio_gpu_framebuffer * fb,struct virtio_gpu_rect * r)199 int virtio_gpu_update_dmabuf(VirtIOGPU *g,
200                              uint32_t scanout_id,
201                              struct virtio_gpu_simple_resource *res,
202                              struct virtio_gpu_framebuffer *fb,
203                              struct virtio_gpu_rect *r)
204 {
205     struct virtio_gpu_scanout *scanout = &g->parent_obj.scanout[scanout_id];
206     VGPUDMABuf *new_primary, *old_primary = NULL;
207     uint32_t width, height;
208 
209     new_primary = virtio_gpu_create_dmabuf(g, scanout_id, res, fb, r);
210     if (!new_primary) {
211         return -EINVAL;
212     }
213 
214     if (g->dmabuf.primary[scanout_id]) {
215         old_primary = g->dmabuf.primary[scanout_id];
216     }
217 
218     width = qemu_dmabuf_get_width(new_primary->buf);
219     height = qemu_dmabuf_get_height(new_primary->buf);
220     g->dmabuf.primary[scanout_id] = new_primary;
221     qemu_console_resize(scanout->con, width, height);
222     dpy_gl_scanout_dmabuf(scanout->con, new_primary->buf);
223 
224     if (old_primary) {
225         virtio_gpu_free_dmabuf(g, old_primary);
226     }
227 
228     return 0;
229 }
230