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 | 032a955 | 2020-11-25 19:07:31 +0000 | [diff] [blame] | 124 | void test_import_dma_buf(struct gbm_device *gbm) |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 125 | { |
Kristian H. Kristensen | 032a955 | 2020-11-25 19:07:31 +0000 | [diff] [blame] | 126 | int width = 64; |
| 127 | int height = 64; |
| 128 | size_t size = width * height * 4; |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 129 | |
| 130 | int memfd_fd = create_memfd(size); |
| 131 | fail_if(memfd_fd == -1, "failed to create memfd"); |
| 132 | |
| 133 | uint32_t *memfd_map = |
| 134 | mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, memfd_fd, 0); |
| 135 | fail_if(memfd_map == MAP_FAILED, "failed to mmap memfd"); |
| 136 | |
| 137 | write_pattern(memfd_map, size); |
| 138 | fail_if(!verify_pattern(memfd_map, size), "failed to verify pattern"); |
| 139 | |
| 140 | fail_if(munmap(memfd_map, size) != 0, "munmap failed"); |
| 141 | |
| 142 | int udmabuf_fd = create_udmabuf(memfd_fd, size); |
| 143 | fail_if(udmabuf_fd == -1, "failed to create udmabuf"); |
| 144 | |
| 145 | fail_if(close(memfd_fd) != 0, "close memfd failed"); |
| 146 | |
Kristian H. Kristensen | 032a955 | 2020-11-25 19:07:31 +0000 | [diff] [blame] | 147 | struct gbm_import_fd_modifier_data gbm_import_data = { |
| 148 | .width = width, |
| 149 | .height = height, |
| 150 | .format = GBM_FORMAT_ARGB8888, |
| 151 | .num_fds = 1, |
| 152 | .fds[0] = udmabuf_fd, |
| 153 | .strides[0] = width * 4, |
| 154 | .offsets[0] = 0, |
| 155 | .modifier = DRM_FORMAT_MOD_LINEAR, |
| 156 | }; |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 157 | |
Kristian H. Kristensen | 032a955 | 2020-11-25 19:07:31 +0000 | [diff] [blame] | 158 | struct gbm_bo *bo = |
| 159 | gbm_bo_import(gbm, GBM_BO_IMPORT_FD_MODIFIER, |
| 160 | &gbm_import_data, GBM_BO_USE_RENDERING); |
| 161 | fail_if(bo == NULL, "failed to import bo"); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 162 | |
| 163 | fail_if(close(udmabuf_fd) != 0, "failed to close udmabuf_fd"); |
| 164 | |
Kristian H. Kristensen | 032a955 | 2020-11-25 19:07:31 +0000 | [diff] [blame] | 165 | uint32_t stride; |
| 166 | void *map_data; |
| 167 | void *bo_map = gbm_bo_map(bo,0, 0, width, height, |
| 168 | GBM_BO_TRANSFER_READ, &stride, &map_data); |
| 169 | |
| 170 | fail_if(bo_map == MAP_FAILED, "gbm_bo_map failed"); |
| 171 | |
| 172 | fail_if(!verify_pattern(bo_map, size), "pattern mismatch after gbm_bo_map"); |
| 173 | |
| 174 | gbm_bo_unmap(bo, map_data); |
| 175 | |
| 176 | gbm_bo_destroy(bo); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 177 | } |
| 178 | |
Kristian H. Kristensen | 032a955 | 2020-11-25 19:07:31 +0000 | [diff] [blame] | 179 | void test_export_dma_buf(struct gbm_device *gbm) |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 180 | { |
Kristian H. Kristensen | 032a955 | 2020-11-25 19:07:31 +0000 | [diff] [blame] | 181 | int width = 64; |
| 182 | int height = 64; |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 183 | |
Kristian H. Kristensen | 032a955 | 2020-11-25 19:07:31 +0000 | [diff] [blame] | 184 | struct gbm_bo *bo = gbm_bo_create(gbm, width, height, |
| 185 | GBM_FORMAT_ARGB8888, GBM_BO_USE_LINEAR); |
| 186 | fail_if(bo == NULL, "create bo failed"); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 187 | |
Kristian H. Kristensen | 032a955 | 2020-11-25 19:07:31 +0000 | [diff] [blame] | 188 | uint32_t stride; |
| 189 | void *map_data; |
| 190 | void *bo_map = gbm_bo_map(bo,0, 0, width, height, |
| 191 | GBM_BO_TRANSFER_WRITE, &stride, &map_data); |
| 192 | fail_if(bo_map == MAP_FAILED, "gbm_bo_map failed"); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 193 | |
Kristian H. Kristensen | 032a955 | 2020-11-25 19:07:31 +0000 | [diff] [blame] | 194 | fail_if(stride != gbm_bo_get_stride(bo), |
| 195 | "mapped buffer stride doesn't match bo stride"); |
| 196 | uint32_t size = stride * height; |
| 197 | write_pattern(bo_map, size); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 198 | |
Kristian H. Kristensen | 032a955 | 2020-11-25 19:07:31 +0000 | [diff] [blame] | 199 | gbm_bo_unmap(bo, map_data); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 200 | |
Kristian H. Kristensen | 032a955 | 2020-11-25 19:07:31 +0000 | [diff] [blame] | 201 | int dmabuf_fd = gbm_bo_get_fd(bo); |
| 202 | fail_if(dmabuf_fd == -1, "dma-buf export failed"); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 203 | |
| 204 | uint32_t *dmabuf_map = |
Kristian H. Kristensen | 032a955 | 2020-11-25 19:07:31 +0000 | [diff] [blame] | 205 | mmap(NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, dmabuf_fd, 0); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 206 | fail_if(dmabuf_map == MAP_FAILED, "failed to mmap memfd"); |
| 207 | |
Kristian H. Kristensen | 032a955 | 2020-11-25 19:07:31 +0000 | [diff] [blame] | 208 | fail_if(!verify_pattern(dmabuf_map, size), "pattern mismatch after gbm_bo_map"); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 209 | |
Kristian H. Kristensen | 032a955 | 2020-11-25 19:07:31 +0000 | [diff] [blame] | 210 | fail_if(munmap(dmabuf_map, size) != 0, "munmap failed"); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 211 | } |
| 212 | |
| 213 | int main(int argc, char *argv[]) |
| 214 | { |
| 215 | int fd = bs_drm_open_for_display(); |
| 216 | fail_if(fd == -1, "error opening dri card"); |
| 217 | |
Kristian H. Kristensen | 032a955 | 2020-11-25 19:07:31 +0000 | [diff] [blame] | 218 | struct gbm_device* gbm = gbm_create_device(fd); |
| 219 | fail_if(gbm == NULL, "failed to create gbm device"); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 220 | |
Kristian H. Kristensen | 032a955 | 2020-11-25 19:07:31 +0000 | [diff] [blame] | 221 | test_import_dma_buf(gbm); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 222 | |
Kristian H. Kristensen | 032a955 | 2020-11-25 19:07:31 +0000 | [diff] [blame] | 223 | test_export_dma_buf(gbm); |
| 224 | |
| 225 | gbm_device_destroy(gbm); |
Kristian H. Kristensen | ee5a557 | 2020-09-03 07:47:01 +0000 | [diff] [blame] | 226 | close(fd); |
| 227 | |
| 228 | return EXIT_SUCCESS; |
| 229 | } |