master
c 335 lines 6.61 KB
Raw
1 /*
2 * Virtio vhost-user GPU Device
3 *
4 * DRM helpers
5 *
6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
7 * See the COPYING file in the top-level directory.
8 */
9
10 #include "qemu/osdep.h"
11 #include "vugbm.h"
12
13 static bool
14 mem_alloc_bo(struct vugbm_buffer *buf)
15 {
16 buf->mmap = g_try_malloc((uint64_t)buf->width * buf->height * 4);
17 if (!buf->mmap && buf->width && buf->height) {
18 return false;
19 }
20 buf->stride = buf->width * 4;
21 return true;
22 }
23
24 static void
25 mem_free_bo(struct vugbm_buffer *buf)
26 {
27 g_free(buf->mmap);
28 }
29
30 static bool
31 mem_map_bo(struct vugbm_buffer *buf)
32 {
33 return buf->mmap != NULL;
34 }
35
36 static void
37 mem_unmap_bo(struct vugbm_buffer *buf)
38 {
39 }
40
41 static void
42 mem_device_destroy(struct vugbm_device *dev)
43 {
44 }
45
46 #ifdef CONFIG_MEMFD
47 struct udmabuf_create {
48 uint32_t memfd;
49 uint32_t flags;
50 uint64_t offset;
51 uint64_t size;
52 };
53
54 #define UDMABUF_CREATE _IOW('u', 0x42, struct udmabuf_create)
55
56 static size_t
57 udmabuf_get_size(struct vugbm_buffer *buf)
58 {
59 return ROUND_UP((uint64_t)buf->width * buf->height * 4,
60 qemu_real_host_page_size());
61 }
62
63 static bool
64 udmabuf_alloc_bo(struct vugbm_buffer *buf)
65 {
66 int ret;
67
68 buf->memfd = memfd_create("udmabuf-bo", MFD_ALLOW_SEALING);
69 if (buf->memfd < 0) {
70 return false;
71 }
72
73 ret = ftruncate(buf->memfd, udmabuf_get_size(buf));
74 if (ret < 0) {
75 close(buf->memfd);
76 return false;
77 }
78
79 ret = fcntl(buf->memfd, F_ADD_SEALS, F_SEAL_SHRINK);
80 if (ret < 0) {
81 close(buf->memfd);
82 return false;
83 }
84
85 buf->stride = buf->width * 4;
86
87 return true;
88 }
89
90 static void
91 udmabuf_free_bo(struct vugbm_buffer *buf)
92 {
93 close(buf->memfd);
94 }
95
96 static bool
97 udmabuf_map_bo(struct vugbm_buffer *buf)
98 {
99 buf->mmap = mmap(NULL, udmabuf_get_size(buf),
100 PROT_READ | PROT_WRITE, MAP_SHARED, buf->memfd, 0);
101 if (buf->mmap == MAP_FAILED) {
102 return false;
103 }
104
105 return true;
106 }
107
108 static bool
109 udmabuf_get_fd(struct vugbm_buffer *buf, int *fd)
110 {
111 struct udmabuf_create create = {
112 .memfd = buf->memfd,
113 .offset = 0,
114 .size = udmabuf_get_size(buf),
115 };
116
117 *fd = ioctl(buf->dev->fd, UDMABUF_CREATE, &create);
118
119 return *fd >= 0;
120 }
121
122 static void
123 udmabuf_unmap_bo(struct vugbm_buffer *buf)
124 {
125 munmap(buf->mmap, udmabuf_get_size(buf));
126 }
127
128 static void
129 udmabuf_device_destroy(struct vugbm_device *dev)
130 {
131 close(dev->fd);
132 }
133 #endif
134
135 #ifdef CONFIG_GBM
136 static bool
137 alloc_bo(struct vugbm_buffer *buf)
138 {
139 struct gbm_device *dev = buf->dev->dev;
140
141 assert(!buf->bo);
142
143 buf->bo = gbm_bo_create(dev, buf->width, buf->height,
144 buf->format,
145 GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR);
146
147 if (buf->bo) {
148 buf->stride = gbm_bo_get_stride(buf->bo);
149 return true;
150 }
151
152 return false;
153 }
154
155 static void
156 free_bo(struct vugbm_buffer *buf)
157 {
158 gbm_bo_destroy(buf->bo);
159 }
160
161 static bool
162 map_bo(struct vugbm_buffer *buf)
163 {
164 uint32_t stride;
165
166 buf->mmap = gbm_bo_map(buf->bo, 0, 0, buf->width, buf->height,
167 GBM_BO_TRANSFER_READ_WRITE, &stride,
168 &buf->mmap_data);
169
170 assert(stride == buf->stride);
171
172 return buf->mmap != NULL;
173 }
174
175 static void
176 unmap_bo(struct vugbm_buffer *buf)
177 {
178 gbm_bo_unmap(buf->bo, buf->mmap_data);
179 }
180
181 static bool
182 get_fd(struct vugbm_buffer *buf, int *fd)
183 {
184 *fd = gbm_bo_get_fd(buf->bo);
185
186 return *fd >= 0;
187 }
188
189 static void
190 device_destroy(struct vugbm_device *dev)
191 {
192 gbm_device_destroy(dev->dev);
193 }
194 #endif
195
196 void
197 vugbm_device_destroy(struct vugbm_device *dev)
198 {
199 if (!dev->inited) {
200 return;
201 }
202
203 dev->device_destroy(dev);
204 }
205
206 void
207 vugbm_device_init(struct vugbm_device *dev, int fd)
208 {
209 assert(!dev->inited);
210
211 #ifdef CONFIG_GBM
212 if (fd >= 0) {
213 dev->dev = gbm_create_device(fd);
214 }
215 if (dev->dev != NULL) {
216 dev->fd = fd;
217 dev->alloc_bo = alloc_bo;
218 dev->free_bo = free_bo;
219 dev->get_fd = get_fd;
220 dev->map_bo = map_bo;
221 dev->unmap_bo = unmap_bo;
222 dev->device_destroy = device_destroy;
223 dev->inited = true;
224 }
225 #endif
226 #ifdef CONFIG_MEMFD
227 if (!dev->inited && g_file_test("/dev/udmabuf", G_FILE_TEST_EXISTS)) {
228 dev->fd = open("/dev/udmabuf", O_RDWR);
229 if (dev->fd >= 0) {
230 g_debug("Using experimental udmabuf backend");
231 dev->alloc_bo = udmabuf_alloc_bo;
232 dev->free_bo = udmabuf_free_bo;
233 dev->get_fd = udmabuf_get_fd;
234 dev->map_bo = udmabuf_map_bo;
235 dev->unmap_bo = udmabuf_unmap_bo;
236 dev->device_destroy = udmabuf_device_destroy;
237 dev->inited = true;
238 }
239 }
240 #endif
241 if (!dev->inited) {
242 g_debug("Using mem fallback");
243 dev->alloc_bo = mem_alloc_bo;
244 dev->free_bo = mem_free_bo;
245 dev->map_bo = mem_map_bo;
246 dev->unmap_bo = mem_unmap_bo;
247 dev->device_destroy = mem_device_destroy;
248 dev->inited = true;
249 }
250 assert(dev->inited);
251 }
252
253 static bool
254 vugbm_buffer_map(struct vugbm_buffer *buf)
255 {
256 struct vugbm_device *dev = buf->dev;
257
258 return dev->map_bo(buf);
259 }
260
261 static void
262 vugbm_buffer_unmap(struct vugbm_buffer *buf)
263 {
264 struct vugbm_device *dev = buf->dev;
265
266 dev->unmap_bo(buf);
267 }
268
269 bool
270 vugbm_buffer_can_get_dmabuf_fd(struct vugbm_buffer *buffer)
271 {
272 if (!buffer->dev->get_fd) {
273 return false;
274 }
275
276 return true;
277 }
278
279 bool
280 vugbm_buffer_get_dmabuf_fd(struct vugbm_buffer *buffer, int *fd)
281 {
282 if (!vugbm_buffer_can_get_dmabuf_fd(buffer) ||
283 !buffer->dev->get_fd(buffer, fd)) {
284 g_warning("Failed to get dmabuf");
285 return false;
286 }
287
288 if (*fd < 0) {
289 g_warning("error: dmabuf_fd < 0");
290 return false;
291 }
292
293 return true;
294 }
295
296 bool
297 vugbm_buffer_create(struct vugbm_buffer *buffer, struct vugbm_device *dev,
298 uint32_t width, uint32_t height)
299 {
300 uint64_t size = (uint64_t)width * height * 4;
301 if (size > UINT32_MAX) {
302 g_warning("buffer dimensions too large: %ux%u", width, height);
303 return false;
304 }
305
306 buffer->dev = dev;
307 buffer->width = width;
308 buffer->height = height;
309 buffer->format = GBM_FORMAT_XRGB8888;
310 buffer->stride = 0; /* modified during alloc */
311 if (!dev->alloc_bo(buffer)) {
312 g_warning("alloc_bo failed");
313 return false;
314 }
315
316 if (!vugbm_buffer_map(buffer)) {
317 g_warning("map_bo failed");
318 goto err;
319 }
320
321 return true;
322
323 err:
324 dev->free_bo(buffer);
325 return false;
326 }
327
328 void
329 vugbm_buffer_destroy(struct vugbm_buffer *buffer)
330 {
331 struct vugbm_device *dev = buffer->dev;
332
333 vugbm_buffer_unmap(buffer);
334 dev->free_bo(buffer);
335 }