Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 1 | /* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. |
| 2 | * Use of this source code is governed by a BSD-style license that can be |
| 3 | * found in the LICENSE file. |
| 4 | * |
| 5 | * This is a test meant to exercise the VGEM DRM kernel module's PRIME |
| 6 | * import/export functions. It will create a gem buffer object, mmap, write, and |
| 7 | * then verify it. Then the test will repeat that with the same gem buffer, but |
| 8 | * exported and then imported. Finally, a new gem buffer object is made in a |
| 9 | * different driver which exports into VGEM and the mmap, write, verify sequence |
| 10 | * is repeated on that. |
| 11 | */ |
| 12 | |
| 13 | #define _GNU_SOURCE |
| 14 | #include <assert.h> |
| 15 | #include <fcntl.h> |
| 16 | #include <stdbool.h> |
| 17 | #include <stdint.h> |
| 18 | #include <stdio.h> |
| 19 | #include <stdlib.h> |
| 20 | #include <string.h> |
| 21 | #include <sys/mman.h> |
| 22 | #include <sys/ioctl.h> |
| 23 | #include <sys/stat.h> |
| 24 | #include <sys/types.h> |
| 25 | #include <unistd.h> |
| 26 | #include <errno.h> |
| 27 | #include <xf86drm.h> |
| 28 | #include <linux/udmabuf.h> |
| 29 | |
| 30 | #include "bs_drm.h" |
| 31 | |
| 32 | #define HANDLE_EINTR_AND_EAGAIN(x) \ |
| 33 | ({ \ |
| 34 | int result; \ |
| 35 | do { \ |
| 36 | result = (x); \ |
| 37 | } while (result != -1 && (errno == EINTR || errno == EAGAIN)); \ |
| 38 | result; \ |
| 39 | }) |
| 40 | |
| 41 | #define fail_if(cond, ...) \ |
| 42 | do { \ |
| 43 | if (cond) { \ |
| 44 | bs_debug_print("FAIL", __func__, __FILE__, __LINE__, __VA_ARGS__); \ |
| 45 | exit(EXIT_FAILURE); \ |
| 46 | } \ |
| 47 | } while (0) |
| 48 | |
| 49 | const uint32_t g_bo_pattern = 0xdeadbeef; |
| 50 | |
| 51 | void *mmap_dumb_bo(int fd, int handle, size_t size) |
| 52 | { |
| 53 | struct drm_mode_map_dumb mmap_arg; |
| 54 | void *ptr; |
| 55 | int ret; |
| 56 | |
| 57 | memset(&mmap_arg, 0, sizeof(mmap_arg)); |
| 58 | |
| 59 | mmap_arg.handle = handle; |
| 60 | |
| 61 | ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mmap_arg); |
| 62 | assert(ret == 0); |
| 63 | assert(mmap_arg.offset != 0); |
| 64 | |
| 65 | ptr = mmap(NULL, size, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, mmap_arg.offset); |
| 66 | |
| 67 | return ptr; |
| 68 | } |
| 69 | |
| 70 | void write_pattern(uint32_t *bo_ptr, size_t bo_size) |
| 71 | { |
| 72 | uint32_t *ptr; |
| 73 | |
| 74 | for (ptr = bo_ptr; ptr < bo_ptr + (bo_size / sizeof(*bo_ptr)); ptr++) { |
| 75 | *ptr = g_bo_pattern; |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | bool verify_pattern(uint32_t *bo_ptr, size_t bo_size) |
| 80 | { |
| 81 | uint32_t *ptr; |
| 82 | |
| 83 | for (ptr = bo_ptr; ptr < bo_ptr + (bo_size / sizeof(*bo_ptr)); ptr++) { |
| 84 | fail_if(*ptr != g_bo_pattern, "buffer object verify"); |
| 85 | } |
| 86 | |
| 87 | return true; |
| 88 | } |
| 89 | |
| 90 | int create_udmabuf(int fd, size_t length) |
| 91 | { |
| 92 | int udmabuf_dev_fd = HANDLE_EINTR_AND_EAGAIN(open("/dev/udmabuf", O_RDWR)); |
| 93 | fail_if(udmabuf_dev_fd < 0, "error opening /dev/udmabuf"); |
| 94 | |
| 95 | struct udmabuf_create create; |
| 96 | create.memfd = fd; |
| 97 | create.flags = UDMABUF_FLAGS_CLOEXEC; |
| 98 | create.offset = 0; |
| 99 | create.size = length; |
| 100 | |
| 101 | int dmabuf_fd = HANDLE_EINTR_AND_EAGAIN(ioctl(udmabuf_dev_fd, UDMABUF_CREATE, &create)); |
| 102 | fail_if(dmabuf_fd < 0, "error creating udmabuf"); |
| 103 | |
| 104 | close(udmabuf_dev_fd); |
| 105 | return dmabuf_fd; |
| 106 | } |
| 107 | |
| 108 | int create_memfd(size_t length) |
| 109 | { |
| 110 | int fd = memfd_create("test memfd", MFD_ALLOW_SEALING); |
| 111 | fail_if(fd == -1, "memfd_create() error: %s", strerror(errno)); |
| 112 | |
| 113 | int res = HANDLE_EINTR_AND_EAGAIN(ftruncate(fd, length)); |
| 114 | fail_if(res == -1, "ftruncate() error: %s", strerror(errno)); |
| 115 | |
| 116 | // udmabuf_create requires that file descriptors be sealed with |
| 117 | // F_SEAL_SHRINK. |
| 118 | fail_if(fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK) < 0, |
| 119 | "fcntl() error: %s", strerror(errno)); |
| 120 | |
| 121 | return fd; |
| 122 | } |
| 123 | |
Kristian H. Kristensen | dfa86ce | 2020-11-20 15:44:57 +0000 | [diff] [blame^] | 124 | void test_import_dma_buf(int native_fd) |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 125 | { |
| 126 | int width = 256; |
| 127 | int height = 256; |
Kristian H. Kristensen | dfa86ce | 2020-11-20 15:44:57 +0000 | [diff] [blame^] | 128 | int bpp = 4; |
| 129 | int stride = width * bpp; |
| 130 | size_t size = stride * height; |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 131 | |
| 132 | int memfd_fd = create_memfd(size); |
| 133 | fail_if(memfd_fd == -1, "failed to create memfd"); |
| 134 | |
| 135 | uint32_t *memfd_map = |
| 136 | mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, memfd_fd, 0); |
| 137 | fail_if(memfd_map == MAP_FAILED, "failed to mmap memfd"); |
| 138 | |
| 139 | write_pattern(memfd_map, size); |
| 140 | fail_if(!verify_pattern(memfd_map, size), "failed to verify pattern"); |
| 141 | |
| 142 | fail_if(munmap(memfd_map, size) != 0, "munmap failed"); |
| 143 | |
| 144 | int udmabuf_fd = create_udmabuf(memfd_fd, size); |
| 145 | fail_if(udmabuf_fd == -1, "failed to create udmabuf"); |
| 146 | |
| 147 | fail_if(close(memfd_fd) != 0, "close memfd failed"); |
| 148 | |
Kristian H. Kristensen | dfa86ce | 2020-11-20 15:44:57 +0000 | [diff] [blame^] | 149 | uint32_t foreign_imported_handle; |
| 150 | fail_if(drmPrimeFDToHandle(native_fd, udmabuf_fd, &foreign_imported_handle) != 0, |
| 151 | "drmPrimeFDToHandle failed"); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 152 | |
Kristian H. Kristensen | dfa86ce | 2020-11-20 15:44:57 +0000 | [diff] [blame^] | 153 | uint32_t *bo_ptr = |
| 154 | mmap_dumb_bo(native_fd, foreign_imported_handle, size); |
| 155 | fail_if(bo_ptr == MAP_FAILED, |
| 156 | "failed to map imported buffer object\n"); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 157 | |
| 158 | fail_if(close(udmabuf_fd) != 0, "failed to close udmabuf_fd"); |
| 159 | |
Kristian H. Kristensen | dfa86ce | 2020-11-20 15:44:57 +0000 | [diff] [blame^] | 160 | fail_if(!verify_pattern(bo_ptr, size), "pattern mismatch after map"); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 161 | } |
| 162 | |
Kristian H. Kristensen | dfa86ce | 2020-11-20 15:44:57 +0000 | [diff] [blame^] | 163 | void test_export_dma_buf(int native_fd) |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 164 | { |
Kristian H. Kristensen | dfa86ce | 2020-11-20 15:44:57 +0000 | [diff] [blame^] | 165 | struct drm_mode_create_dumb create; |
| 166 | uint32_t *bo_ptr; |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 167 | |
Kristian H. Kristensen | dfa86ce | 2020-11-20 15:44:57 +0000 | [diff] [blame^] | 168 | memset(&create, 0, sizeof(create)); |
| 169 | create.width = 640; |
| 170 | create.height = 480; |
| 171 | create.bpp = 32; |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 172 | |
Kristian H. Kristensen | dfa86ce | 2020-11-20 15:44:57 +0000 | [diff] [blame^] | 173 | fail_if(drmIoctl(native_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create) != 0, |
| 174 | "failed to create dumb buffer object"); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 175 | |
Kristian H. Kristensen | dfa86ce | 2020-11-20 15:44:57 +0000 | [diff] [blame^] | 176 | bo_ptr = mmap_dumb_bo(native_fd, create.handle, create.size); |
| 177 | fail_if(bo_ptr == MAP_FAILED, "failed to map dumb bo"); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 178 | |
Kristian H. Kristensen | dfa86ce | 2020-11-20 15:44:57 +0000 | [diff] [blame^] | 179 | write_pattern(bo_ptr, create.size); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 180 | |
Kristian H. Kristensen | dfa86ce | 2020-11-20 15:44:57 +0000 | [diff] [blame^] | 181 | munmap(bo_ptr, create.size); |
| 182 | |
| 183 | int dmabuf_fd; |
| 184 | fail_if(drmPrimeHandleToFD(native_fd, create.handle, DRM_CLOEXEC | DRM_RDWR, &dmabuf_fd) != 0, |
| 185 | "drmPrimeHandleToFD failed"); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 186 | |
| 187 | uint32_t *dmabuf_map = |
Kristian H. Kristensen | dfa86ce | 2020-11-20 15:44:57 +0000 | [diff] [blame^] | 188 | mmap(NULL, create.size, PROT_WRITE | PROT_READ, MAP_SHARED, dmabuf_fd, 0); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 189 | fail_if(dmabuf_map == MAP_FAILED, "failed to mmap memfd"); |
| 190 | |
Kristian H. Kristensen | dfa86ce | 2020-11-20 15:44:57 +0000 | [diff] [blame^] | 191 | fail_if(!verify_pattern(dmabuf_map, create.size), |
| 192 | "pattern mismatch after gbm_bo_map"); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 193 | |
Kristian H. Kristensen | dfa86ce | 2020-11-20 15:44:57 +0000 | [diff] [blame^] | 194 | fail_if(munmap(dmabuf_map, create.size) != 0, "munmap failed"); |
| 195 | |
| 196 | fail_if(close(dmabuf_fd) != 0, "failed to close dmabuf_fd"); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 197 | } |
| 198 | |
| 199 | int main(int argc, char *argv[]) |
| 200 | { |
| 201 | int fd = bs_drm_open_for_display(); |
| 202 | fail_if(fd == -1, "error opening dri card"); |
| 203 | |
Kristian H. Kristensen | dfa86ce | 2020-11-20 15:44:57 +0000 | [diff] [blame^] | 204 | test_import_dma_buf(fd); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 205 | |
Kristian H. Kristensen | dfa86ce | 2020-11-20 15:44:57 +0000 | [diff] [blame^] | 206 | test_export_dma_buf(fd); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 207 | |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 208 | close(fd); |
| 209 | |
| 210 | return EXIT_SUCCESS; |
| 211 | } |