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