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 heap_fd = dmabuf_heap_open(heap_name); 152 if (heap_fd < 0) 153 return -1; 154 155 printf(" Testing allocation and importing: "); 156 ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0, &dmabuf_fd); 157 if (ret) { 158 printf("FAIL (Allocation Failed!)\n"); 159 ret = -1; 160 goto out; 161 } 162 /* mmap and write a simple pattern */ 163 p = mmap(NULL, 164 ONE_MEG, 165 PROT_READ | PROT_WRITE, 166 MAP_SHARED, 167 dmabuf_fd, 168 0); 169 if (p == MAP_FAILED) { 170 printf("FAIL (mmap() failed)\n"); 171 ret = -1; 172 goto out; 173 } 174 175 dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START); 176 memset(p, 1, ONE_MEG / 2); 177 memset((char *)p + ONE_MEG / 2, 0, ONE_MEG / 2); 178 dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END); 179 180 importer_fd = open_vgem(); 181 if (importer_fd < 0) { 182 ret = importer_fd; 183 printf("(Could not open vgem - skipping): "); 184 } else { 185 ret = import_vgem_fd(importer_fd, dmabuf_fd, &handle); 186 if (ret < 0) { 187 printf("FAIL (Failed to import buffer)\n"); 188 goto out; 189 } 190 } 191 192 ret = dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_START); 193 if (ret < 0) { 194 printf("FAIL (DMA_BUF_SYNC_START failed!)\n"); 195 goto out; 196 } 197 198 memset(p, 0xff, ONE_MEG); 199 ret = dmabuf_sync(dmabuf_fd, DMA_BUF_SYNC_END); 200 if (ret < 0) { 201 printf("FAIL (DMA_BUF_SYNC_END failed!)\n"); 202 goto out; 203 } 204 205 close_handle(importer_fd, handle); 206 ret = 0; 207 printf(" OK\n"); 208 out: 209 if (p) 210 munmap(p, ONE_MEG); 211 if (importer_fd >= 0) 212 close(importer_fd); 213 if (dmabuf_fd >= 0) 214 close(dmabuf_fd); 215 if (heap_fd >= 0) 216 close(heap_fd); 217 218 return ret; 219 } 220 221 static int test_alloc_zeroed(char *heap_name, size_t size) 222 { 223 int heap_fd = -1, dmabuf_fd[32]; 224 int i, j, ret; 225 void *p = NULL; 226 char *c; 227 228 printf(" Testing alloced %ldk buffers are zeroed: ", size / 1024); 229 heap_fd = dmabuf_heap_open(heap_name); 230 if (heap_fd < 0) 231 return -1; 232 233 /* Allocate and fill a bunch of buffers */ 234 for (i = 0; i < 32; i++) { 235 ret = dmabuf_heap_alloc(heap_fd, size, 0, &dmabuf_fd[i]); 236 if (ret < 0) { 237 printf("FAIL (Allocation (%i) failed)\n", i); 238 goto out; 239 } 240 /* mmap and fill with simple pattern */ 241 p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, dmabuf_fd[i], 0); 242 if (p == MAP_FAILED) { 243 printf("FAIL (mmap() failed!)\n"); 244 ret = -1; 245 goto out; 246 } 247 dmabuf_sync(dmabuf_fd[i], DMA_BUF_SYNC_START); 248 memset(p, 0xff, size); 249 dmabuf_sync(dmabuf_fd[i], DMA_BUF_SYNC_END); 250 munmap(p, size); 251 } 252 /* close them all */ 253 for (i = 0; i < 32; i++) 254 close(dmabuf_fd[i]); 255 256 /* Allocate and validate all buffers are zeroed */ 257 for (i = 0; i < 32; i++) { 258 ret = dmabuf_heap_alloc(heap_fd, size, 0, &dmabuf_fd[i]); 259 if (ret < 0) { 260 printf("FAIL (Allocation (%i) failed)\n", i); 261 goto out; 262 } 263 264 /* mmap and validate everything is zero */ 265 p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, dmabuf_fd[i], 0); 266 if (p == MAP_FAILED) { 267 printf("FAIL (mmap() failed!)\n"); 268 ret = -1; 269 goto out; 270 } 271 dmabuf_sync(dmabuf_fd[i], DMA_BUF_SYNC_START); 272 c = (char *)p; 273 for (j = 0; j < size; j++) { 274 if (c[j] != 0) { 275 printf("FAIL (Allocated buffer not zeroed @ %i)\n", j); 276 break; 277 } 278 } 279 dmabuf_sync(dmabuf_fd[i], DMA_BUF_SYNC_END); 280 munmap(p, size); 281 } 282 /* close them all */ 283 for (i = 0; i < 32; i++) 284 close(dmabuf_fd[i]); 285 286 close(heap_fd); 287 printf("OK\n"); 288 return 0; 289 290 out: 291 while (i > 0) { 292 close(dmabuf_fd[i]); 293 i--; 294 } 295 close(heap_fd); 296 return ret; 297 } 298 299 /* Test the ioctl version compatibility w/ a smaller structure then expected */ 300 static int dmabuf_heap_alloc_older(int fd, size_t len, unsigned int flags, 301 int *dmabuf_fd) 302 { 303 int ret; 304 unsigned int older_alloc_ioctl; 305 struct dma_heap_allocation_data_smaller { 306 __u64 len; 307 __u32 fd; 308 __u32 fd_flags; 309 } data = { 310 .len = len, 311 .fd = 0, 312 .fd_flags = O_RDWR | O_CLOEXEC, 313 }; 314 315 older_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0, 316 struct dma_heap_allocation_data_smaller); 317 if (!dmabuf_fd) 318 return -EINVAL; 319 320 ret = ioctl(fd, older_alloc_ioctl, &data); 321 if (ret < 0) 322 return ret; 323 *dmabuf_fd = (int)data.fd; 324 return ret; 325 } 326 327 /* Test the ioctl version compatibility w/ a larger structure then expected */ 328 static int dmabuf_heap_alloc_newer(int fd, size_t len, unsigned int flags, 329 int *dmabuf_fd) 330 { 331 int ret; 332 unsigned int newer_alloc_ioctl; 333 struct dma_heap_allocation_data_bigger { 334 __u64 len; 335 __u32 fd; 336 __u32 fd_flags; 337 __u64 heap_flags; 338 __u64 garbage1; 339 __u64 garbage2; 340 __u64 garbage3; 341 } data = { 342 .len = len, 343 .fd = 0, 344 .fd_flags = O_RDWR | O_CLOEXEC, 345 .heap_flags = flags, 346 .garbage1 = 0xffffffff, 347 .garbage2 = 0x88888888, 348 .garbage3 = 0x11111111, 349 }; 350 351 newer_alloc_ioctl = _IOWR(DMA_HEAP_IOC_MAGIC, 0x0, 352 struct dma_heap_allocation_data_bigger); 353 if (!dmabuf_fd) 354 return -EINVAL; 355 356 ret = ioctl(fd, newer_alloc_ioctl, &data); 357 if (ret < 0) 358 return ret; 359 360 *dmabuf_fd = (int)data.fd; 361 return ret; 362 } 363 364 static int test_alloc_compat(char *heap_name) 365 { 366 int heap_fd = -1, dmabuf_fd = -1; 367 int ret; 368 369 heap_fd = dmabuf_heap_open(heap_name); 370 if (heap_fd < 0) 371 return -1; 372 373 printf(" Testing (theoretical)older alloc compat: "); 374 ret = dmabuf_heap_alloc_older(heap_fd, ONE_MEG, 0, &dmabuf_fd); 375 if (ret) { 376 printf("FAIL (Older compat allocation failed!)\n"); 377 ret = -1; 378 goto out; 379 } 380 close(dmabuf_fd); 381 printf("OK\n"); 382 383 printf(" Testing (theoretical)newer alloc compat: "); 384 ret = dmabuf_heap_alloc_newer(heap_fd, ONE_MEG, 0, &dmabuf_fd); 385 if (ret) { 386 printf("FAIL (Newer compat allocation failed!)\n"); 387 ret = -1; 388 goto out; 389 } 390 printf("OK\n"); 391 out: 392 if (dmabuf_fd >= 0) 393 close(dmabuf_fd); 394 if (heap_fd >= 0) 395 close(heap_fd); 396 397 return ret; 398 } 399 400 static int test_alloc_errors(char *heap_name) 401 { 402 int heap_fd = -1, dmabuf_fd = -1; 403 int ret; 404 405 heap_fd = dmabuf_heap_open(heap_name); 406 if (heap_fd < 0) 407 return -1; 408 409 printf(" Testing expected error cases: "); 410 ret = dmabuf_heap_alloc(0, ONE_MEG, 0x111111, &dmabuf_fd); 411 if (!ret) { 412 printf("FAIL (Did not see expected error (invalid fd)!)\n"); 413 ret = -1; 414 goto out; 415 } 416 417 ret = dmabuf_heap_alloc(heap_fd, ONE_MEG, 0x111111, &dmabuf_fd); 418 if (!ret) { 419 printf("FAIL (Did not see expected error (invalid heap flags)!)\n"); 420 ret = -1; 421 goto out; 422 } 423 424 ret = dmabuf_heap_alloc_fdflags(heap_fd, ONE_MEG, 425 ~(O_RDWR | O_CLOEXEC), 0, &dmabuf_fd); 426 if (!ret) { 427 printf("FAIL (Did not see expected error (invalid fd flags)!)\n"); 428 ret = -1; 429 goto out; 430 } 431 432 printf("OK\n"); 433 ret = 0; 434 out: 435 if (dmabuf_fd >= 0) 436 close(dmabuf_fd); 437 if (heap_fd >= 0) 438 close(heap_fd); 439 440 return ret; 441 } 442 443 int main(void) 444 { 445 DIR *d; 446 struct dirent *dir; 447 int ret = -1; 448 449 d = opendir(DEVPATH); 450 if (!d) { 451 printf("No %s directory?\n", DEVPATH); 452 return -1; 453 } 454 455 while ((dir = readdir(d)) != NULL) { 456 if (!strncmp(dir->d_name, ".", 2)) 457 continue; 458 if (!strncmp(dir->d_name, "..", 3)) 459 continue; 460 461 printf("Testing heap: %s\n", dir->d_name); 462 printf("=======================================\n"); 463 ret = test_alloc_and_import(dir->d_name); 464 if (ret) 465 break; 466 467 ret = test_alloc_zeroed(dir->d_name, 4 * 1024); 468 if (ret) 469 break; 470 471 ret = test_alloc_zeroed(dir->d_name, ONE_MEG); 472 if (ret) 473 break; 474 475 ret = test_alloc_compat(dir->d_name); 476 if (ret) 477 break; 478 479 ret = test_alloc_errors(dir->d_name); 480 if (ret) 481 break; 482 } 483 closedir(d); 484 485 return ret; 486 } 487