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