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 
133*50c65a83SJohn 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 
139*50c65a83SJohn 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");
187a8779927SJohn Stultz 		goto out;
188a8779927SJohn Stultz 	}
189a8779927SJohn Stultz 
190a8779927SJohn Stultz 	ret = import_vgem_fd(importer_fd, dmabuf_fd, &handle);
191a8779927SJohn Stultz 	if (ret < 0) {
192a8779927SJohn Stultz 		printf("Failed to import buffer\n");
193a8779927SJohn Stultz 		goto out;
194a8779927SJohn Stultz 	}
195a8779927SJohn Stultz 	printf("import passed\n");
196a8779927SJohn Stultz 
197*50c65a83SJohn Stultz 	ret = dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START);
198*50c65a83SJohn Stultz 	if (ret < 0) {
199*50c65a83SJohn Stultz 		printf("Sync start failed!\n");
200*50c65a83SJohn Stultz 		goto out;
201*50c65a83SJohn Stultz 	}
202*50c65a83SJohn Stultz 
203a8779927SJohn Stultz 	memset(p, 0xff, ONE_MEG);
204*50c65a83SJohn Stultz 	ret = dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END);
205*50c65a83SJohn Stultz 	if (ret < 0) {
206*50c65a83SJohn Stultz 		printf("Sync end failed!\n");
207*50c65a83SJohn Stultz 		goto out;
208*50c65a83SJohn Stultz 	}
209a8779927SJohn Stultz 	printf("syncs passed\n");
210a8779927SJohn Stultz 
211a8779927SJohn Stultz 	close_handle(importer_fd, handle);
212a8779927SJohn Stultz 	ret = 0;
213a8779927SJohn Stultz 
214a8779927SJohn Stultz out:
215a8779927SJohn Stultz 	if (p)
216a8779927SJohn Stultz 		munmap(p, ONE_MEG);
217a8779927SJohn Stultz 	if (importer_fd >= 0)
218a8779927SJohn Stultz 		close(importer_fd);
219a8779927SJohn Stultz 	if (dmabuf_fd >= 0)
220a8779927SJohn Stultz 		close(dmabuf_fd);
221a8779927SJohn Stultz 	if (heap_fd >= 0)
222a8779927SJohn Stultz 		close(heap_fd);
223a8779927SJohn Stultz 
224a8779927SJohn Stultz 	return ret;
225a8779927SJohn Stultz }
226a8779927SJohn Stultz 
227a8779927SJohn Stultz /* Test the ioctl version compatibility w/ a smaller structure then expected */
228a8779927SJohn Stultz static int dmabuf_heap_alloc_older(int fd, size_t len, unsigned int flags,
229a8779927SJohn Stultz 				   int *dmabuf_fd)
230a8779927SJohn Stultz {
231a8779927SJohn Stultz 	int ret;
232a8779927SJohn Stultz 	unsigned int older_alloc_ioctl;
233a8779927SJohn Stultz 	struct dma_heap_allocation_data_smaller {
234a8779927SJohn Stultz 		__u64 len;
235a8779927SJohn Stultz 		__u32 fd;
236a8779927SJohn Stultz 		__u32 fd_flags;
237a8779927SJohn Stultz 	} data = {
238a8779927SJohn Stultz 		.len = len,
239a8779927SJohn Stultz 		.fd = 0,
240a8779927SJohn Stultz 		.fd_flags = O_RDWR | O_CLOEXEC,
241a8779927SJohn Stultz 	};
242a8779927SJohn Stultz 
243a8779927SJohn Stultz 	older_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,
244a8779927SJohn Stultz 				  struct dma_heap_allocation_data_smaller);
245a8779927SJohn Stultz 	if (!dmabuf_fd)
246a8779927SJohn Stultz 		return -EINVAL;
247a8779927SJohn Stultz 
248a8779927SJohn Stultz 	ret = ioctl(fd, older_alloc_ioctl, &data);
249a8779927SJohn Stultz 	if (ret < 0)
250a8779927SJohn Stultz 		return ret;
251a8779927SJohn Stultz 	*dmabuf_fd = (int)data.fd;
252a8779927SJohn Stultz 	return ret;
253a8779927SJohn Stultz }
254a8779927SJohn Stultz 
255a8779927SJohn Stultz /* Test the ioctl version compatibility w/ a larger structure then expected */
256a8779927SJohn Stultz static int dmabuf_heap_alloc_newer(int fd, size_t len, unsigned int flags,
257a8779927SJohn Stultz 				   int *dmabuf_fd)
258a8779927SJohn Stultz {
259a8779927SJohn Stultz 	int ret;
260a8779927SJohn Stultz 	unsigned int newer_alloc_ioctl;
261a8779927SJohn Stultz 	struct dma_heap_allocation_data_bigger {
262a8779927SJohn Stultz 		__u64 len;
263a8779927SJohn Stultz 		__u32 fd;
264a8779927SJohn Stultz 		__u32 fd_flags;
265a8779927SJohn Stultz 		__u64 heap_flags;
266a8779927SJohn Stultz 		__u64 garbage1;
267a8779927SJohn Stultz 		__u64 garbage2;
268a8779927SJohn Stultz 		__u64 garbage3;
269a8779927SJohn Stultz 	} data = {
270a8779927SJohn Stultz 		.len = len,
271a8779927SJohn Stultz 		.fd = 0,
272a8779927SJohn Stultz 		.fd_flags = O_RDWR | O_CLOEXEC,
273a8779927SJohn Stultz 		.heap_flags = flags,
274a8779927SJohn Stultz 		.garbage1 = 0xffffffff,
275a8779927SJohn Stultz 		.garbage2 = 0x88888888,
276a8779927SJohn Stultz 		.garbage3 = 0x11111111,
277a8779927SJohn Stultz 	};
278a8779927SJohn Stultz 
279a8779927SJohn Stultz 	newer_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0,
280a8779927SJohn Stultz 				  struct dma_heap_allocation_data_bigger);
281a8779927SJohn Stultz 	if (!dmabuf_fd)
282a8779927SJohn Stultz 		return -EINVAL;
283a8779927SJohn Stultz 
284a8779927SJohn Stultz 	ret = ioctl(fd, newer_alloc_ioctl, &data);
285a8779927SJohn Stultz 	if (ret < 0)
286a8779927SJohn Stultz 		return ret;
287a8779927SJohn Stultz 
288a8779927SJohn Stultz 	*dmabuf_fd = (int)data.fd;
289a8779927SJohn Stultz 	return ret;
290a8779927SJohn Stultz }
291a8779927SJohn Stultz 
292a8779927SJohn Stultz static int test_alloc_compat(char *heap_name)
293a8779927SJohn Stultz {
294a8779927SJohn Stultz 	int heap_fd = -1, dmabuf_fd = -1;
295a8779927SJohn Stultz 	int ret;
296a8779927SJohn Stultz 
297a8779927SJohn Stultz 	heap_fd = dmabuf_heap_open(heap_name);
298a8779927SJohn Stultz 	if (heap_fd < 0)
299a8779927SJohn Stultz 		return -1;
300a8779927SJohn Stultz 
301a8779927SJohn Stultz 	printf("Testing (theoretical)older alloc compat\n");
302a8779927SJohn Stultz 	ret = dmabuf_heap_alloc_older(heap_fd, ONE_MEG, 0, &dmabuf_fd);
303a8779927SJohn Stultz 	if (ret) {
304a8779927SJohn Stultz 		printf("Older compat allocation failed!\n");
305a8779927SJohn Stultz 		ret = -1;
306a8779927SJohn Stultz 		goto out;
307a8779927SJohn Stultz 	}
308a8779927SJohn Stultz 	close(dmabuf_fd);
309a8779927SJohn Stultz 
310a8779927SJohn Stultz 	printf("Testing (theoretical)newer alloc compat\n");
311a8779927SJohn Stultz 	ret = dmabuf_heap_alloc_newer(heap_fd, ONE_MEG, 0, &dmabuf_fd);
312a8779927SJohn Stultz 	if (ret) {
313a8779927SJohn Stultz 		printf("Newer compat allocation failed!\n");
314a8779927SJohn Stultz 		ret = -1;
315a8779927SJohn Stultz 		goto out;
316a8779927SJohn Stultz 	}
317a8779927SJohn Stultz 	printf("Ioctl compatibility tests passed\n");
318a8779927SJohn Stultz out:
319a8779927SJohn Stultz 	if (dmabuf_fd >= 0)
320a8779927SJohn Stultz 		close(dmabuf_fd);
321a8779927SJohn Stultz 	if (heap_fd >= 0)
322a8779927SJohn Stultz 		close(heap_fd);
323a8779927SJohn Stultz 
324a8779927SJohn Stultz 	return ret;
325a8779927SJohn Stultz }
326a8779927SJohn Stultz 
327a8779927SJohn Stultz static int test_alloc_errors(char *heap_name)
328a8779927SJohn Stultz {
329a8779927SJohn Stultz 	int heap_fd = -1, dmabuf_fd = -1;
330a8779927SJohn Stultz 	int ret;
331a8779927SJohn Stultz 
332a8779927SJohn Stultz 	heap_fd = dmabuf_heap_open(heap_name);
333a8779927SJohn Stultz 	if (heap_fd < 0)
334a8779927SJohn Stultz 		return -1;
335a8779927SJohn Stultz 
336a8779927SJohn Stultz 	printf("Testing expected error cases\n");
337a8779927SJohn Stultz 	ret = dmabuf_heap_alloc(0, ONE_MEG, 0x111111, &dmabuf_fd);
338a8779927SJohn Stultz 	if (!ret) {
339a8779927SJohn Stultz 		printf("Did not see expected error (invalid fd)!\n");
340a8779927SJohn Stultz 		ret = -1;
341a8779927SJohn Stultz 		goto out;
342a8779927SJohn Stultz 	}
343a8779927SJohn Stultz 
344a8779927SJohn Stultz 	ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0x111111, &dmabuf_fd);
345a8779927SJohn Stultz 	if (!ret) {
346a8779927SJohn Stultz 		printf("Did not see expected error (invalid heap flags)!\n");
347a8779927SJohn Stultz 		ret = -1;
348a8779927SJohn Stultz 		goto out;
349a8779927SJohn Stultz 	}
350a8779927SJohn Stultz 
351a8779927SJohn Stultz 	ret = dmabuf_heap_alloc_fdflags(heap_fd, ONE_MEG,
352a8779927SJohn Stultz 					~(O_RDWR | O_CLOEXEC), 0, &dmabuf_fd);
353a8779927SJohn Stultz 	if (!ret) {
354a8779927SJohn Stultz 		printf("Did not see expected error (invalid fd flags)!\n");
355a8779927SJohn Stultz 		ret = -1;
356a8779927SJohn Stultz 		goto out;
357a8779927SJohn Stultz 	}
358a8779927SJohn Stultz 
359a8779927SJohn Stultz 	printf("Expected error checking passed\n");
3604bb9d46dSJohn Stultz 	ret = 0;
361a8779927SJohn Stultz out:
362a8779927SJohn Stultz 	if (dmabuf_fd >= 0)
363a8779927SJohn Stultz 		close(dmabuf_fd);
364a8779927SJohn Stultz 	if (heap_fd >= 0)
365a8779927SJohn Stultz 		close(heap_fd);
366a8779927SJohn Stultz 
367a8779927SJohn Stultz 	return ret;
368a8779927SJohn Stultz }
369a8779927SJohn Stultz 
370a8779927SJohn Stultz int main(void)
371a8779927SJohn Stultz {
372a8779927SJohn Stultz 	DIR *d;
373a8779927SJohn Stultz 	struct dirent *dir;
374a8779927SJohn Stultz 	int ret = -1;
375a8779927SJohn Stultz 
376a8779927SJohn Stultz 	d = opendir(DEVPATH);
377a8779927SJohn Stultz 	if (!d) {
378a8779927SJohn Stultz 		printf("No %s directory?\n", DEVPATH);
379a8779927SJohn Stultz 		return -1;
380a8779927SJohn Stultz 	}
381a8779927SJohn Stultz 
382a8779927SJohn Stultz 	while ((dir = readdir(d)) != NULL) {
383a8779927SJohn Stultz 		if (!strncmp(dir->d_name, ".", 2))
384a8779927SJohn Stultz 			continue;
385a8779927SJohn Stultz 		if (!strncmp(dir->d_name, "..", 3))
386a8779927SJohn Stultz 			continue;
387a8779927SJohn Stultz 
388a8779927SJohn Stultz 		ret = test_alloc_and_import(dir->d_name);
389a8779927SJohn Stultz 		if (ret)
390a8779927SJohn Stultz 			break;
391a8779927SJohn Stultz 
392a8779927SJohn Stultz 		ret = test_alloc_compat(dir->d_name);
393a8779927SJohn Stultz 		if (ret)
394a8779927SJohn Stultz 			break;
395a8779927SJohn Stultz 
396a8779927SJohn Stultz 		ret = test_alloc_errors(dir->d_name);
397a8779927SJohn Stultz 		if (ret)
398a8779927SJohn Stultz 			break;
399a8779927SJohn Stultz 	}
400a8779927SJohn Stultz 	closedir(d);
401a8779927SJohn Stultz 
402a8779927SJohn Stultz 	return ret;
403a8779927SJohn Stultz }
404