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