1 // SPDX-License-Identifier: GPL-2.0
2
3 #include <dirent.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <stdint.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <sys/ioctl.h>
12 #include <sys/mman.h>
13 #include <sys/types.h>
14
15 #include <linux/dma-buf.h>
16 #include <linux/dma-heap.h>
17 #include <drm/drm.h>
18
19 #define DEVPATH "/dev/dma_heap"
20
check_vgem(int fd)21 static int check_vgem(int fd)
22 {
23 drm_version_t version = { 0 };
24 char name[5];
25 int ret;
26
27 version.name_len = 4;
28 version.name = name;
29
30 ret = ioctl(fd, DRM_IOCTL_VERSION, &version);
31 if (ret || version.name_len != 4)
32 return 0;
33
34 name[4] = '\0';
35
36 return !strcmp(name, "vgem");
37 }
38
open_vgem(void)39 static int open_vgem(void)
40 {
41 int i, fd;
42 const char *drmstr = "/dev/dri/card";
43
44 fd = -1;
45 for (i = 0; i < 16; i++) {
46 char name[80];
47
48 snprintf(name, 80, "%s%u", drmstr, i);
49
50 fd = open(name, O_RDWR);
51 if (fd < 0)
52 continue;
53
54 if (!check_vgem(fd)) {
55 close(fd);
56 fd = -1;
57 continue;
58 } else {
59 break;
60 }
61 }
62 return fd;
63 }
64
import_vgem_fd(int vgem_fd,int dma_buf_fd,uint32_t * handle)65 static int import_vgem_fd(int vgem_fd, int dma_buf_fd, uint32_t *handle)
66 {
67 struct drm_prime_handle import_handle = {
68 .fd = dma_buf_fd,
69 .flags = 0,
70 .handle = 0,
71 };
72 int ret;
73
74 ret = ioctl(vgem_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &import_handle);
75 if (ret == 0)
76 *handle = import_handle.handle;
77 return ret;
78 }
79
close_handle(int vgem_fd,uint32_t handle)80 static void close_handle(int vgem_fd, uint32_t handle)
81 {
82 struct drm_gem_close close = {
83 .handle = handle,
84 };
85
86 ioctl(vgem_fd, DRM_IOCTL_GEM_CLOSE, &close);
87 }
88
dmabuf_heap_open(char * name)89 static int dmabuf_heap_open(char *name)
90 {
91 int ret, fd;
92 char buf[256];
93
94 ret = snprintf(buf, 256, "%s/%s", DEVPATH, name);
95 if (ret < 0) {
96 printf("snprintf failed!\n");
97 return ret;
98 }
99
100 fd = open(buf, O_RDWR);
101 if (fd < 0)
102 printf("open %s failed!\n", buf);
103 return fd;
104 }
105
dmabuf_heap_alloc_fdflags(int fd,size_t len,unsigned int fd_flags,unsigned int heap_flags,int * dmabuf_fd)106 static int dmabuf_heap_alloc_fdflags(int fd, size_t len, unsigned int fd_flags,
107 unsigned int heap_flags, int *dmabuf_fd)
108 {
109 struct dma_heap_allocation_data data = {
110 .len = len,
111 .fd = 0,
112 .fd_flags = fd_flags,
113 .heap_flags = heap_flags,
114 };
115 int ret;
116
117 if (!dmabuf_fd)
118 return -EINVAL;
119
120 ret = ioctl(fd, DMA_HEAP_IOCTL_ALLOC, &data);
121 if (ret < 0)
122 return ret;
123 *dmabuf_fd = (int)data.fd;
124 return ret;
125 }
126
dmabuf_heap_alloc(int fd,size_t len,unsigned int flags,int * dmabuf_fd)127 static int dmabuf_heap_alloc(int fd, size_t len, unsigned int flags,
128 int *dmabuf_fd)
129 {
130 return dmabuf_heap_alloc_fdflags(fd, len, O_RDWR | O_CLOEXEC, flags,
131 dmabuf_fd);
132 }
133
dmabuf_sync(int fd,int start_stop)134 static int dmabuf_sync(int fd, int start_stop)
135 {
136 struct dma_buf_sync sync = {
137 .flags = start_stop | DMA_BUF_SYNC_RW,
138 };
139
140 return ioctl(fd, DMA_BUF_IOCTL_SYNC, &sync);
141 }
142
143 #define ONE_MEG (1024 * 1024)
144
test_alloc_and_import(char * heap_name)145 static int test_alloc_and_import(char *heap_name)
146 {
147 int heap_fd = -1, dmabuf_fd = -1, importer_fd = -1;
148 uint32_t handle = 0;
149 void *p = NULL;
150 int ret;
151
152 heap_fd = dmabuf_heap_open(heap_name);
153 if (heap_fd < 0)
154 return -1;
155
156 printf(" Testing allocation and importing: ");
157 ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0, &dmabuf_fd);
158 if (ret) {
159 printf("FAIL (Allocation Failed!)\n");
160 ret = -1;
161 goto out;
162 }
163 /* mmap and write a simple pattern */
164 p = mmap(NULL,
165 ONE_MEG,
166 PROT_READ | PROT_WRITE,
167 MAP_SHARED,
168 dmabuf_fd,
169 0);
170 if (p == MAP_FAILED) {
171 printf("FAIL (mmap() failed)\n");
172 ret = -1;
173 goto out;
174 }
175
176 dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START);
177 memset(p, 1, ONE_MEG / 2);
178 memset((char *)p + ONE_MEG / 2, 0, ONE_MEG / 2);
179 dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END);
180
181 importer_fd = open_vgem();
182 if (importer_fd < 0) {
183 ret = importer_fd;
184 printf("(Could not open vgem - skipping): ");
185 } else {
186 ret = import_vgem_fd(importer_fd, dmabuf_fd, &handle);
187 if (ret < 0) {
188 printf("FAIL (Failed to import buffer)\n");
189 goto out;
190 }
191 }
192
193 ret = dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START);
194 if (ret < 0) {
195 printf("FAIL (DMA_BUF_SYNC_START failed!)\n");
196 goto out;
197 }
198
199 memset(p, 0xff, ONE_MEG);
200 ret = dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END);
201 if (ret < 0) {
202 printf("FAIL (DMA_BUF_SYNC_END failed!)\n");
203 goto out;
204 }
205
206 close_handle(importer_fd, handle);
207 ret = 0;
208 printf(" OK\n");
209 out:
210 if (p)
211 munmap(p, ONE_MEG);
212 if (importer_fd >= 0)
213 close(importer_fd);
214 if (dmabuf_fd >= 0)
215 close(dmabuf_fd);
216 if (heap_fd >= 0)
217 close(heap_fd);
218
219 return ret;
220 }
221
test_alloc_zeroed(char * heap_name,size_t size)222 static int test_alloc_zeroed(char *heap_name, size_t size)
223 {
224 int heap_fd = -1, dmabuf_fd[32];
225 int i, j, ret;
226 void *p = NULL;
227 char *c;
228
229 printf(" Testing alloced %ldk buffers are zeroed: ", size / 1024);
230 heap_fd = dmabuf_heap_open(heap_name);
231 if (heap_fd < 0)
232 return -1;
233
234 /* Allocate and fill a bunch of buffers */
235 for (i = 0; i < 32; i++) {
236 ret = dmabuf_heap_alloc(heap_fd, size, 0, &dmabuf_fd[i]);
237 if (ret < 0) {
238 printf("FAIL (Allocation (%i) failed)\n", i);
239 goto out;
240 }
241 /* mmap and fill with simple pattern */
242 p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, dmabuf_fd[i], 0);
243 if (p == MAP_FAILED) {
244 printf("FAIL (mmap() failed!)\n");
245 ret = -1;
246 goto out;
247 }
248 dmabuf_sync(dmabuf_fd[i], DMA_BUF_SYNC_START);
249 memset(p, 0xff, size);
250 dmabuf_sync(dmabuf_fd[i], DMA_BUF_SYNC_END);
251 munmap(p, size);
252 }
253 /* close them all */
254 for (i = 0; i < 32; i++)
255 close(dmabuf_fd[i]);
256
257 /* Allocate and validate all buffers are zeroed */
258 for (i = 0; i < 32; i++) {
259 ret = dmabuf_heap_alloc(heap_fd, size, 0, &dmabuf_fd[i]);
260 if (ret < 0) {
261 printf("FAIL (Allocation (%i) failed)\n", i);
262 goto out;
263 }
264
265 /* mmap and validate everything is zero */
266 p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, dmabuf_fd[i], 0);
267 if (p == MAP_FAILED) {
268 printf("FAIL (mmap() failed!)\n");
269 ret = -1;
270 goto out;
271 }
272 dmabuf_sync(dmabuf_fd[i], DMA_BUF_SYNC_START);
273 c = (char *)p;
274 for (j = 0; j < size; j++) {
275 if (c[j] != 0) {
276 printf("FAIL (Allocated buffer not zeroed @ %i)\n", j);
277 break;
278 }
279 }
280 dmabuf_sync(dmabuf_fd[i], DMA_BUF_SYNC_END);
281 munmap(p, size);
282 }
283 /* close them all */
284 for (i = 0; i < 32; i++)
285 close(dmabuf_fd[i]);
286
287 close(heap_fd);
288 printf("OK\n");
289 return 0;
290
291 out:
292 while (i > 0) {
293 close(dmabuf_fd[i]);
294 i--;
295 }
296 close(heap_fd);
297 return ret;
298 }
299
300 /* Test the ioctl version compatibility w/ a smaller structure then expected */
dmabuf_heap_alloc_older(int fd,size_t len,unsigned int flags,int * dmabuf_fd)301 static int dmabuf_heap_alloc_older(int fd, size_t len, unsigned int flags,
302 int *dmabuf_fd)
303 {
304 int ret;
305 unsigned int older_alloc_ioctl;
306 struct dma_heap_allocation_data_smaller {
307 __u64 len;
308 __u32 fd;
309 __u32 fd_flags;
310 } data = {
311 .len = len,
312 .fd = 0,
313 .fd_flags = O_RDWR | O_CLOEXEC,
314 };
315
316 older_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,
317 struct dma_heap_allocation_data_smaller);
318 if (!dmabuf_fd)
319 return -EINVAL;
320
321 ret = ioctl(fd, older_alloc_ioctl, &data);
322 if (ret < 0)
323 return ret;
324 *dmabuf_fd = (int)data.fd;
325 return ret;
326 }
327
328 /* Test the ioctl version compatibility w/ a larger structure then expected */
dmabuf_heap_alloc_newer(int fd,size_t len,unsigned int flags,int * dmabuf_fd)329 static int dmabuf_heap_alloc_newer(int fd, size_t len, unsigned int flags,
330 int *dmabuf_fd)
331 {
332 int ret;
333 unsigned int newer_alloc_ioctl;
334 struct dma_heap_allocation_data_bigger {
335 __u64 len;
336 __u32 fd;
337 __u32 fd_flags;
338 __u64 heap_flags;
339 __u64 garbage1;
340 __u64 garbage2;
341 __u64 garbage3;
342 } data = {
343 .len = len,
344 .fd = 0,
345 .fd_flags = O_RDWR | O_CLOEXEC,
346 .heap_flags = flags,
347 .garbage1 = 0xffffffff,
348 .garbage2 = 0x88888888,
349 .garbage3 = 0x11111111,
350 };
351
352 newer_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,
353 struct dma_heap_allocation_data_bigger);
354 if (!dmabuf_fd)
355 return -EINVAL;
356
357 ret = ioctl(fd, newer_alloc_ioctl, &data);
358 if (ret < 0)
359 return ret;
360
361 *dmabuf_fd = (int)data.fd;
362 return ret;
363 }
364
test_alloc_compat(char * heap_name)365 static int test_alloc_compat(char *heap_name)
366 {
367 int heap_fd = -1, dmabuf_fd = -1;
368 int ret;
369
370 heap_fd = dmabuf_heap_open(heap_name);
371 if (heap_fd < 0)
372 return -1;
373
374 printf(" Testing (theoretical)older alloc compat: ");
375 ret = dmabuf_heap_alloc_older(heap_fd, ONE_MEG, 0, &dmabuf_fd);
376 if (ret) {
377 printf("FAIL (Older compat allocation failed!)\n");
378 ret = -1;
379 goto out;
380 }
381 close(dmabuf_fd);
382 printf("OK\n");
383
384 printf(" Testing (theoretical)newer alloc compat: ");
385 ret = dmabuf_heap_alloc_newer(heap_fd, ONE_MEG, 0, &dmabuf_fd);
386 if (ret) {
387 printf("FAIL (Newer compat allocation failed!)\n");
388 ret = -1;
389 goto out;
390 }
391 printf("OK\n");
392 out:
393 if (dmabuf_fd >= 0)
394 close(dmabuf_fd);
395 if (heap_fd >= 0)
396 close(heap_fd);
397
398 return ret;
399 }
400
test_alloc_errors(char * heap_name)401 static int test_alloc_errors(char *heap_name)
402 {
403 int heap_fd = -1, dmabuf_fd = -1;
404 int ret;
405
406 heap_fd = dmabuf_heap_open(heap_name);
407 if (heap_fd < 0)
408 return -1;
409
410 printf(" Testing expected error cases: ");
411 ret = dmabuf_heap_alloc(0, ONE_MEG, 0x111111, &dmabuf_fd);
412 if (!ret) {
413 printf("FAIL (Did not see expected error (invalid fd)!)\n");
414 ret = -1;
415 goto out;
416 }
417
418 ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0x111111, &dmabuf_fd);
419 if (!ret) {
420 printf("FAIL (Did not see expected error (invalid heap flags)!)\n");
421 ret = -1;
422 goto out;
423 }
424
425 ret = dmabuf_heap_alloc_fdflags(heap_fd, ONE_MEG,
426 ~(O_RDWR | O_CLOEXEC), 0, &dmabuf_fd);
427 if (!ret) {
428 printf("FAIL (Did not see expected error (invalid fd flags)!)\n");
429 ret = -1;
430 goto out;
431 }
432
433 printf("OK\n");
434 ret = 0;
435 out:
436 if (dmabuf_fd >= 0)
437 close(dmabuf_fd);
438 if (heap_fd >= 0)
439 close(heap_fd);
440
441 return ret;
442 }
443
main(void)444 int main(void)
445 {
446 DIR *d;
447 struct dirent *dir;
448 int ret = -1;
449
450 d = opendir(DEVPATH);
451 if (!d) {
452 printf("No %s directory?\n", DEVPATH);
453 return -1;
454 }
455
456 while ((dir = readdir(d)) != NULL) {
457 if (!strncmp(dir->d_name, ".", 2))
458 continue;
459 if (!strncmp(dir->d_name, "..", 3))
460 continue;
461
462 printf("Testing heap: %s\n", dir->d_name);
463 printf("=======================================\n");
464 ret = test_alloc_and_import(dir->d_name);
465 if (ret)
466 break;
467
468 ret = test_alloc_zeroed(dir->d_name, 4 * 1024);
469 if (ret)
470 break;
471
472 ret = test_alloc_zeroed(dir->d_name, ONE_MEG);
473 if (ret)
474 break;
475
476 ret = test_alloc_compat(dir->d_name);
477 if (ret)
478 break;
479
480 ret = test_alloc_errors(dir->d_name);
481 if (ret)
482 break;
483 }
484 closedir(d);
485
486 return ret;
487 }
488