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