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

source code of linux/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c