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