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 <drm/drm.h>
17  
18  #include "../../../../include/uapi/linux/dma-heap.h"
19  
20  #define DEVPATH "/dev/dma_heap"
21  
check_vgem(int fd)22  static int check_vgem(int fd)
23  {
24  	drm_version_t version = { 0 };
25  	char name[5];
26  	int ret;
27  
28  	version.name_len = 4;
29  	version.name = name;
30  
31  	ret = ioctl(fd, DRM_IOCTL_VERSION, &version);
32  	if (ret)
33  		return 0;
34  
35  	return !strcmp(name, "vgem");
36  }
37  
open_vgem(void)38  static int open_vgem(void)
39  {
40  	int i, fd;
41  	const char *drmstr = "/dev/dri/card";
42  
43  	fd = -1;
44  	for (i = 0; i < 16; i++) {
45  		char name[80];
46  
47  		snprintf(name, 80, "%s%u", drmstr, i);
48  
49  		fd = open(name, O_RDWR);
50  		if (fd < 0)
51  			continue;
52  
53  		if (!check_vgem(fd)) {
54  			close(fd);
55  			fd = -1;
56  			continue;
57  		} else {
58  			break;
59  		}
60  	}
61  	return fd;
62  }
63  
import_vgem_fd(int vgem_fd,int dma_buf_fd,uint32_t * handle)64  static int import_vgem_fd(int vgem_fd, int dma_buf_fd, uint32_t *handle)
65  {
66  	struct drm_prime_handle import_handle = {
67  		.fd = dma_buf_fd,
68  		.flags = 0,
69  		.handle = 0,
70  	 };
71  	int ret;
72  
73  	ret = ioctl(vgem_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &import_handle);
74  	if (ret == 0)
75  		*handle = import_handle.handle;
76  	return ret;
77  }
78  
close_handle(int vgem_fd,uint32_t handle)79  static void close_handle(int vgem_fd, uint32_t handle)
80  {
81  	struct drm_gem_close close = {
82  		.handle = handle,
83  	};
84  
85  	ioctl(vgem_fd, DRM_IOCTL_GEM_CLOSE, &close);
86  }
87  
dmabuf_heap_open(char * name)88  static int dmabuf_heap_open(char *name)
89  {
90  	int ret, fd;
91  	char buf[256];
92  
93  	ret = snprintf(buf, 256, "%s/%s", DEVPATH, name);
94  	if (ret < 0) {
95  		printf("snprintf failed!\n");
96  		return ret;
97  	}
98  
99  	fd = open(buf, O_RDWR);
100  	if (fd < 0)
101  		printf("open %s failed!\n", buf);
102  	return fd;
103  }
104  
dmabuf_heap_alloc_fdflags(int fd,size_t len,unsigned int fd_flags,unsigned int heap_flags,int * dmabuf_fd)105  static int dmabuf_heap_alloc_fdflags(int fd, size_t len, unsigned int fd_flags,
106  				     unsigned int heap_flags, int *dmabuf_fd)
107  {
108  	struct dma_heap_allocation_data data = {
109  		.len = len,
110  		.fd = 0,
111  		.fd_flags = fd_flags,
112  		.heap_flags = heap_flags,
113  	};
114  	int ret;
115  
116  	if (!dmabuf_fd)
117  		return -EINVAL;
118  
119  	ret = ioctl(fd, DMA_HEAP_IOCTL_ALLOC, &data);
120  	if (ret < 0)
121  		return ret;
122  	*dmabuf_fd = (int)data.fd;
123  	return ret;
124  }
125  
dmabuf_heap_alloc(int fd,size_t len,unsigned int flags,int * dmabuf_fd)126  static int dmabuf_heap_alloc(int fd, size_t len, unsigned int flags,
127  			     int *dmabuf_fd)
128  {
129  	return dmabuf_heap_alloc_fdflags(fd, len, O_RDWR | O_CLOEXEC, flags,
130  					 dmabuf_fd);
131  }
132  
dmabuf_sync(int fd,int start_stop)133  static void dmabuf_sync(int fd, int start_stop)
134  {
135  	struct dma_buf_sync sync = {
136  		.flags = start_stop | DMA_BUF_SYNC_RW,
137  	};
138  	int ret;
139  
140  	ret = ioctl(fd, DMA_BUF_IOCTL_SYNC, &sync);
141  	if (ret)
142  		printf("sync failed %d\n", errno);
143  }
144  
145  #define ONE_MEG (1024 * 1024)
146  
test_alloc_and_import(char * heap_name)147  static int test_alloc_and_import(char *heap_name)
148  {
149  	int heap_fd = -1, dmabuf_fd = -1, importer_fd = -1;
150  	uint32_t handle = 0;
151  	void *p = NULL;
152  	int ret;
153  
154  	printf("Testing heap: %s\n", heap_name);
155  
156  	heap_fd = dmabuf_heap_open(heap_name);
157  	if (heap_fd < 0)
158  		return -1;
159  
160  	printf("Allocating 1 MEG\n");
161  	ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0, &dmabuf_fd);
162  	if (ret) {
163  		printf("Allocation Failed!\n");
164  		ret = -1;
165  		goto out;
166  	}
167  	/* mmap and write a simple pattern */
168  	p = mmap(NULL,
169  		 ONE_MEG,
170  		 PROT_READ | PROT_WRITE,
171  		 MAP_SHARED,
172  		 dmabuf_fd,
173  		 0);
174  	if (p == MAP_FAILED) {
175  		printf("mmap() failed: %m\n");
176  		ret = -1;
177  		goto out;
178  	}
179  	printf("mmap passed\n");
180  
181  	dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START);
182  	memset(p, 1, ONE_MEG / 2);
183  	memset((char *)p + ONE_MEG / 2, 0, ONE_MEG / 2);
184  	dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END);
185  
186  	importer_fd = open_vgem();
187  	if (importer_fd < 0) {
188  		ret = importer_fd;
189  		printf("Failed to open vgem\n");
190  		goto out;
191  	}
192  
193  	ret = import_vgem_fd(importer_fd, dmabuf_fd, &handle);
194  	if (ret < 0) {
195  		printf("Failed to import buffer\n");
196  		goto out;
197  	}
198  	printf("import passed\n");
199  
200  	dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START);
201  	memset(p, 0xff, ONE_MEG);
202  	dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END);
203  	printf("syncs passed\n");
204  
205  	close_handle(importer_fd, handle);
206  	ret = 0;
207  
208  out:
209  	if (p)
210  		munmap(p, ONE_MEG);
211  	if (importer_fd >= 0)
212  		close(importer_fd);
213  	if (dmabuf_fd >= 0)
214  		close(dmabuf_fd);
215  	if (heap_fd >= 0)
216  		close(heap_fd);
217  
218  	return ret;
219  }
220  
221  /* 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)222  static int dmabuf_heap_alloc_older(int fd, size_t len, unsigned int flags,
223  				   int *dmabuf_fd)
224  {
225  	int ret;
226  	unsigned int older_alloc_ioctl;
227  	struct dma_heap_allocation_data_smaller {
228  		__u64 len;
229  		__u32 fd;
230  		__u32 fd_flags;
231  	} data = {
232  		.len = len,
233  		.fd = 0,
234  		.fd_flags = O_RDWR | O_CLOEXEC,
235  	};
236  
237  	older_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,
238  				  struct dma_heap_allocation_data_smaller);
239  	if (!dmabuf_fd)
240  		return -EINVAL;
241  
242  	ret = ioctl(fd, older_alloc_ioctl, &data);
243  	if (ret < 0)
244  		return ret;
245  	*dmabuf_fd = (int)data.fd;
246  	return ret;
247  }
248  
249  /* 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)250  static int dmabuf_heap_alloc_newer(int fd, size_t len, unsigned int flags,
251  				   int *dmabuf_fd)
252  {
253  	int ret;
254  	unsigned int newer_alloc_ioctl;
255  	struct dma_heap_allocation_data_bigger {
256  		__u64 len;
257  		__u32 fd;
258  		__u32 fd_flags;
259  		__u64 heap_flags;
260  		__u64 garbage1;
261  		__u64 garbage2;
262  		__u64 garbage3;
263  	} data = {
264  		.len = len,
265  		.fd = 0,
266  		.fd_flags = O_RDWR | O_CLOEXEC,
267  		.heap_flags = flags,
268  		.garbage1 = 0xffffffff,
269  		.garbage2 = 0x88888888,
270  		.garbage3 = 0x11111111,
271  	};
272  
273  	newer_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,
274  				  struct dma_heap_allocation_data_bigger);
275  	if (!dmabuf_fd)
276  		return -EINVAL;
277  
278  	ret = ioctl(fd, newer_alloc_ioctl, &data);
279  	if (ret < 0)
280  		return ret;
281  
282  	*dmabuf_fd = (int)data.fd;
283  	return ret;
284  }
285  
test_alloc_compat(char * heap_name)286  static int test_alloc_compat(char *heap_name)
287  {
288  	int heap_fd = -1, dmabuf_fd = -1;
289  	int ret;
290  
291  	heap_fd = dmabuf_heap_open(heap_name);
292  	if (heap_fd < 0)
293  		return -1;
294  
295  	printf("Testing (theoretical)older alloc compat\n");
296  	ret = dmabuf_heap_alloc_older(heap_fd, ONE_MEG, 0, &dmabuf_fd);
297  	if (ret) {
298  		printf("Older compat allocation failed!\n");
299  		ret = -1;
300  		goto out;
301  	}
302  	close(dmabuf_fd);
303  
304  	printf("Testing (theoretical)newer alloc compat\n");
305  	ret = dmabuf_heap_alloc_newer(heap_fd, ONE_MEG, 0, &dmabuf_fd);
306  	if (ret) {
307  		printf("Newer compat allocation failed!\n");
308  		ret = -1;
309  		goto out;
310  	}
311  	printf("Ioctl compatibility tests passed\n");
312  out:
313  	if (dmabuf_fd >= 0)
314  		close(dmabuf_fd);
315  	if (heap_fd >= 0)
316  		close(heap_fd);
317  
318  	return ret;
319  }
320  
test_alloc_errors(char * heap_name)321  static int test_alloc_errors(char *heap_name)
322  {
323  	int heap_fd = -1, dmabuf_fd = -1;
324  	int ret;
325  
326  	heap_fd = dmabuf_heap_open(heap_name);
327  	if (heap_fd < 0)
328  		return -1;
329  
330  	printf("Testing expected error cases\n");
331  	ret = dmabuf_heap_alloc(0, ONE_MEG, 0x111111, &dmabuf_fd);
332  	if (!ret) {
333  		printf("Did not see expected error (invalid fd)!\n");
334  		ret = -1;
335  		goto out;
336  	}
337  
338  	ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0x111111, &dmabuf_fd);
339  	if (!ret) {
340  		printf("Did not see expected error (invalid heap flags)!\n");
341  		ret = -1;
342  		goto out;
343  	}
344  
345  	ret = dmabuf_heap_alloc_fdflags(heap_fd, ONE_MEG,
346  					~(O_RDWR | O_CLOEXEC), 0, &dmabuf_fd);
347  	if (!ret) {
348  		printf("Did not see expected error (invalid fd flags)!\n");
349  		ret = -1;
350  		goto out;
351  	}
352  
353  	printf("Expected error checking passed\n");
354  	ret = 0;
355  out:
356  	if (dmabuf_fd >= 0)
357  		close(dmabuf_fd);
358  	if (heap_fd >= 0)
359  		close(heap_fd);
360  
361  	return ret;
362  }
363  
main(void)364  int main(void)
365  {
366  	DIR *d;
367  	struct dirent *dir;
368  	int ret = -1;
369  
370  	d = opendir(DEVPATH);
371  	if (!d) {
372  		printf("No %s directory?\n", DEVPATH);
373  		return -1;
374  	}
375  
376  	while ((dir = readdir(d)) != NULL) {
377  		if (!strncmp(dir->d_name, ".", 2))
378  			continue;
379  		if (!strncmp(dir->d_name, "..", 3))
380  			continue;
381  
382  		ret = test_alloc_and_import(dir->d_name);
383  		if (ret)
384  			break;
385  
386  		ret = test_alloc_compat(dir->d_name);
387  		if (ret)
388  			break;
389  
390  		ret = test_alloc_errors(dir->d_name);
391  		if (ret)
392  			break;
393  	}
394  	closedir(d);
395  
396  	return ret;
397  }
398