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