1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 /* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES */ 3 #ifndef __SELFTEST_IOMMUFD_UTILS 4 #define __SELFTEST_IOMMUFD_UTILS 5 6 #include <unistd.h> 7 #include <stddef.h> 8 #include <sys/fcntl.h> 9 #include <sys/ioctl.h> 10 #include <stdint.h> 11 #include <assert.h> 12 13 #include "../kselftest_harness.h" 14 #include "../../../../drivers/iommu/iommufd/iommufd_test.h" 15 16 /* Hack to make assertions more readable */ 17 #define _IOMMU_TEST_CMD(x) IOMMU_TEST_CMD 18 19 static void *buffer; 20 static unsigned long BUFFER_SIZE; 21 22 static unsigned long PAGE_SIZE; 23 24 #define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER)) 25 #define offsetofend(TYPE, MEMBER) \ 26 (offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER)) 27 28 /* 29 * Have the kernel check the refcount on pages. I don't know why a freshly 30 * mmap'd anon non-compound page starts out with a ref of 3 31 */ 32 #define check_refs(_ptr, _length, _refs) \ 33 ({ \ 34 struct iommu_test_cmd test_cmd = { \ 35 .size = sizeof(test_cmd), \ 36 .op = IOMMU_TEST_OP_MD_CHECK_REFS, \ 37 .check_refs = { .length = _length, \ 38 .uptr = (uintptr_t)(_ptr), \ 39 .refs = _refs }, \ 40 }; \ 41 ASSERT_EQ(0, \ 42 ioctl(self->fd, \ 43 _IOMMU_TEST_CMD(IOMMU_TEST_OP_MD_CHECK_REFS), \ 44 &test_cmd)); \ 45 }) 46 47 static int _test_cmd_mock_domain(int fd, unsigned int ioas_id, __u32 *stdev_id, 48 __u32 *hwpt_id, __u32 *idev_id) 49 { 50 struct iommu_test_cmd cmd = { 51 .size = sizeof(cmd), 52 .op = IOMMU_TEST_OP_MOCK_DOMAIN, 53 .id = ioas_id, 54 .mock_domain = {}, 55 }; 56 int ret; 57 58 ret = ioctl(fd, IOMMU_TEST_CMD, &cmd); 59 if (ret) 60 return ret; 61 if (stdev_id) 62 *stdev_id = cmd.mock_domain.out_stdev_id; 63 assert(cmd.id != 0); 64 if (hwpt_id) 65 *hwpt_id = cmd.mock_domain.out_hwpt_id; 66 if (idev_id) 67 *idev_id = cmd.mock_domain.out_idev_id; 68 return 0; 69 } 70 #define test_cmd_mock_domain(ioas_id, stdev_id, hwpt_id, idev_id) \ 71 ASSERT_EQ(0, _test_cmd_mock_domain(self->fd, ioas_id, stdev_id, \ 72 hwpt_id, idev_id)) 73 #define test_err_mock_domain(_errno, ioas_id, stdev_id, hwpt_id) \ 74 EXPECT_ERRNO(_errno, _test_cmd_mock_domain(self->fd, ioas_id, \ 75 stdev_id, hwpt_id, NULL)) 76 77 static int _test_cmd_mock_domain_replace(int fd, __u32 stdev_id, __u32 pt_id, 78 __u32 *hwpt_id) 79 { 80 struct iommu_test_cmd cmd = { 81 .size = sizeof(cmd), 82 .op = IOMMU_TEST_OP_MOCK_DOMAIN_REPLACE, 83 .id = stdev_id, 84 .mock_domain_replace = { 85 .pt_id = pt_id, 86 }, 87 }; 88 int ret; 89 90 ret = ioctl(fd, IOMMU_TEST_CMD, &cmd); 91 if (ret) 92 return ret; 93 if (hwpt_id) 94 *hwpt_id = cmd.mock_domain_replace.pt_id; 95 return 0; 96 } 97 98 #define test_cmd_mock_domain_replace(stdev_id, pt_id) \ 99 ASSERT_EQ(0, _test_cmd_mock_domain_replace(self->fd, stdev_id, pt_id, \ 100 NULL)) 101 #define test_err_mock_domain_replace(_errno, stdev_id, pt_id) \ 102 EXPECT_ERRNO(_errno, _test_cmd_mock_domain_replace(self->fd, stdev_id, \ 103 pt_id, NULL)) 104 105 static int _test_cmd_hwpt_alloc(int fd, __u32 device_id, __u32 pt_id, 106 __u32 *hwpt_id) 107 { 108 struct iommu_hwpt_alloc cmd = { 109 .size = sizeof(cmd), 110 .dev_id = device_id, 111 .pt_id = pt_id, 112 }; 113 int ret; 114 115 ret = ioctl(fd, IOMMU_HWPT_ALLOC, &cmd); 116 if (ret) 117 return ret; 118 if (hwpt_id) 119 *hwpt_id = cmd.out_hwpt_id; 120 return 0; 121 } 122 123 #define test_cmd_hwpt_alloc(device_id, pt_id, hwpt_id) \ 124 ASSERT_EQ(0, _test_cmd_hwpt_alloc(self->fd, device_id, pt_id, hwpt_id)) 125 126 static int _test_cmd_access_replace_ioas(int fd, __u32 access_id, 127 unsigned int ioas_id) 128 { 129 struct iommu_test_cmd cmd = { 130 .size = sizeof(cmd), 131 .op = IOMMU_TEST_OP_ACCESS_REPLACE_IOAS, 132 .id = access_id, 133 .access_replace_ioas = { .ioas_id = ioas_id }, 134 }; 135 int ret; 136 137 ret = ioctl(fd, IOMMU_TEST_CMD, &cmd); 138 if (ret) 139 return ret; 140 return 0; 141 } 142 #define test_cmd_access_replace_ioas(access_id, ioas_id) \ 143 ASSERT_EQ(0, _test_cmd_access_replace_ioas(self->fd, access_id, ioas_id)) 144 145 static int _test_cmd_create_access(int fd, unsigned int ioas_id, 146 __u32 *access_id, unsigned int flags) 147 { 148 struct iommu_test_cmd cmd = { 149 .size = sizeof(cmd), 150 .op = IOMMU_TEST_OP_CREATE_ACCESS, 151 .id = ioas_id, 152 .create_access = { .flags = flags }, 153 }; 154 int ret; 155 156 ret = ioctl(fd, IOMMU_TEST_CMD, &cmd); 157 if (ret) 158 return ret; 159 *access_id = cmd.create_access.out_access_fd; 160 return 0; 161 } 162 #define test_cmd_create_access(ioas_id, access_id, flags) \ 163 ASSERT_EQ(0, _test_cmd_create_access(self->fd, ioas_id, access_id, \ 164 flags)) 165 166 static int _test_cmd_destroy_access(unsigned int access_id) 167 { 168 return close(access_id); 169 } 170 #define test_cmd_destroy_access(access_id) \ 171 ASSERT_EQ(0, _test_cmd_destroy_access(access_id)) 172 173 static int _test_cmd_destroy_access_pages(int fd, unsigned int access_id, 174 unsigned int access_pages_id) 175 { 176 struct iommu_test_cmd cmd = { 177 .size = sizeof(cmd), 178 .op = IOMMU_TEST_OP_DESTROY_ACCESS_PAGES, 179 .id = access_id, 180 .destroy_access_pages = { .access_pages_id = access_pages_id }, 181 }; 182 return ioctl(fd, IOMMU_TEST_CMD, &cmd); 183 } 184 #define test_cmd_destroy_access_pages(access_id, access_pages_id) \ 185 ASSERT_EQ(0, _test_cmd_destroy_access_pages(self->fd, access_id, \ 186 access_pages_id)) 187 #define test_err_destroy_access_pages(_errno, access_id, access_pages_id) \ 188 EXPECT_ERRNO(_errno, _test_cmd_destroy_access_pages( \ 189 self->fd, access_id, access_pages_id)) 190 191 static int _test_ioctl_destroy(int fd, unsigned int id) 192 { 193 struct iommu_destroy cmd = { 194 .size = sizeof(cmd), 195 .id = id, 196 }; 197 return ioctl(fd, IOMMU_DESTROY, &cmd); 198 } 199 #define test_ioctl_destroy(id) ASSERT_EQ(0, _test_ioctl_destroy(self->fd, id)) 200 201 static int _test_ioctl_ioas_alloc(int fd, __u32 *id) 202 { 203 struct iommu_ioas_alloc cmd = { 204 .size = sizeof(cmd), 205 }; 206 int ret; 207 208 ret = ioctl(fd, IOMMU_IOAS_ALLOC, &cmd); 209 if (ret) 210 return ret; 211 *id = cmd.out_ioas_id; 212 return 0; 213 } 214 #define test_ioctl_ioas_alloc(id) \ 215 ({ \ 216 ASSERT_EQ(0, _test_ioctl_ioas_alloc(self->fd, id)); \ 217 ASSERT_NE(0, *(id)); \ 218 }) 219 220 static int _test_ioctl_ioas_map(int fd, unsigned int ioas_id, void *buffer, 221 size_t length, __u64 *iova, unsigned int flags) 222 { 223 struct iommu_ioas_map cmd = { 224 .size = sizeof(cmd), 225 .flags = flags, 226 .ioas_id = ioas_id, 227 .user_va = (uintptr_t)buffer, 228 .length = length, 229 }; 230 int ret; 231 232 if (flags & IOMMU_IOAS_MAP_FIXED_IOVA) 233 cmd.iova = *iova; 234 235 ret = ioctl(fd, IOMMU_IOAS_MAP, &cmd); 236 *iova = cmd.iova; 237 return ret; 238 } 239 #define test_ioctl_ioas_map(buffer, length, iova_p) \ 240 ASSERT_EQ(0, _test_ioctl_ioas_map(self->fd, self->ioas_id, buffer, \ 241 length, iova_p, \ 242 IOMMU_IOAS_MAP_WRITEABLE | \ 243 IOMMU_IOAS_MAP_READABLE)) 244 245 #define test_err_ioctl_ioas_map(_errno, buffer, length, iova_p) \ 246 EXPECT_ERRNO(_errno, \ 247 _test_ioctl_ioas_map(self->fd, self->ioas_id, buffer, \ 248 length, iova_p, \ 249 IOMMU_IOAS_MAP_WRITEABLE | \ 250 IOMMU_IOAS_MAP_READABLE)) 251 252 #define test_ioctl_ioas_map_id(ioas_id, buffer, length, iova_p) \ 253 ASSERT_EQ(0, _test_ioctl_ioas_map(self->fd, ioas_id, buffer, length, \ 254 iova_p, \ 255 IOMMU_IOAS_MAP_WRITEABLE | \ 256 IOMMU_IOAS_MAP_READABLE)) 257 258 #define test_ioctl_ioas_map_fixed(buffer, length, iova) \ 259 ({ \ 260 __u64 __iova = iova; \ 261 ASSERT_EQ(0, _test_ioctl_ioas_map( \ 262 self->fd, self->ioas_id, buffer, length, \ 263 &__iova, \ 264 IOMMU_IOAS_MAP_FIXED_IOVA | \ 265 IOMMU_IOAS_MAP_WRITEABLE | \ 266 IOMMU_IOAS_MAP_READABLE)); \ 267 }) 268 269 #define test_err_ioctl_ioas_map_fixed(_errno, buffer, length, iova) \ 270 ({ \ 271 __u64 __iova = iova; \ 272 EXPECT_ERRNO(_errno, \ 273 _test_ioctl_ioas_map( \ 274 self->fd, self->ioas_id, buffer, length, \ 275 &__iova, \ 276 IOMMU_IOAS_MAP_FIXED_IOVA | \ 277 IOMMU_IOAS_MAP_WRITEABLE | \ 278 IOMMU_IOAS_MAP_READABLE)); \ 279 }) 280 281 static int _test_ioctl_ioas_unmap(int fd, unsigned int ioas_id, uint64_t iova, 282 size_t length, uint64_t *out_len) 283 { 284 struct iommu_ioas_unmap cmd = { 285 .size = sizeof(cmd), 286 .ioas_id = ioas_id, 287 .iova = iova, 288 .length = length, 289 }; 290 int ret; 291 292 ret = ioctl(fd, IOMMU_IOAS_UNMAP, &cmd); 293 if (out_len) 294 *out_len = cmd.length; 295 return ret; 296 } 297 #define test_ioctl_ioas_unmap(iova, length) \ 298 ASSERT_EQ(0, _test_ioctl_ioas_unmap(self->fd, self->ioas_id, iova, \ 299 length, NULL)) 300 301 #define test_ioctl_ioas_unmap_id(ioas_id, iova, length) \ 302 ASSERT_EQ(0, _test_ioctl_ioas_unmap(self->fd, ioas_id, iova, length, \ 303 NULL)) 304 305 #define test_err_ioctl_ioas_unmap(_errno, iova, length) \ 306 EXPECT_ERRNO(_errno, _test_ioctl_ioas_unmap(self->fd, self->ioas_id, \ 307 iova, length, NULL)) 308 309 static int _test_ioctl_set_temp_memory_limit(int fd, unsigned int limit) 310 { 311 struct iommu_test_cmd memlimit_cmd = { 312 .size = sizeof(memlimit_cmd), 313 .op = IOMMU_TEST_OP_SET_TEMP_MEMORY_LIMIT, 314 .memory_limit = { .limit = limit }, 315 }; 316 317 return ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_SET_TEMP_MEMORY_LIMIT), 318 &memlimit_cmd); 319 } 320 321 #define test_ioctl_set_temp_memory_limit(limit) \ 322 ASSERT_EQ(0, _test_ioctl_set_temp_memory_limit(self->fd, limit)) 323 324 #define test_ioctl_set_default_memory_limit() \ 325 test_ioctl_set_temp_memory_limit(65536) 326 327 static void teardown_iommufd(int fd, struct __test_metadata *_metadata) 328 { 329 struct iommu_test_cmd test_cmd = { 330 .size = sizeof(test_cmd), 331 .op = IOMMU_TEST_OP_MD_CHECK_REFS, 332 .check_refs = { .length = BUFFER_SIZE, 333 .uptr = (uintptr_t)buffer }, 334 }; 335 336 if (fd == -1) 337 return; 338 339 EXPECT_EQ(0, close(fd)); 340 341 fd = open("/dev/iommu", O_RDWR); 342 EXPECT_NE(-1, fd); 343 EXPECT_EQ(0, ioctl(fd, _IOMMU_TEST_CMD(IOMMU_TEST_OP_MD_CHECK_REFS), 344 &test_cmd)); 345 EXPECT_EQ(0, close(fd)); 346 } 347 348 #define EXPECT_ERRNO(expected_errno, cmd) \ 349 ({ \ 350 ASSERT_EQ(-1, cmd); \ 351 EXPECT_EQ(expected_errno, errno); \ 352 }) 353 354 #endif 355 356 /* @data can be NULL */ 357 static int _test_cmd_get_hw_info(int fd, __u32 device_id, 358 void *data, size_t data_len) 359 { 360 struct iommu_test_hw_info *info = (struct iommu_test_hw_info *)data; 361 struct iommu_hw_info cmd = { 362 .size = sizeof(cmd), 363 .dev_id = device_id, 364 .data_len = data_len, 365 .data_uptr = (uint64_t)data, 366 }; 367 int ret; 368 369 ret = ioctl(fd, IOMMU_GET_HW_INFO, &cmd); 370 if (ret) 371 return ret; 372 373 assert(cmd.out_data_type == IOMMU_HW_INFO_TYPE_SELFTEST); 374 375 /* 376 * The struct iommu_test_hw_info should be the one defined 377 * by the current kernel. 378 */ 379 assert(cmd.data_len == sizeof(struct iommu_test_hw_info)); 380 381 /* 382 * Trailing bytes should be 0 if user buffer is larger than 383 * the data that kernel reports. 384 */ 385 if (data_len > cmd.data_len) { 386 char *ptr = (char *)(data + cmd.data_len); 387 int idx = 0; 388 389 while (idx < data_len - cmd.data_len) { 390 assert(!*(ptr + idx)); 391 idx++; 392 } 393 } 394 395 if (info) { 396 if (data_len >= offsetofend(struct iommu_test_hw_info, test_reg)) 397 assert(info->test_reg == IOMMU_HW_INFO_SELFTEST_REGVAL); 398 if (data_len >= offsetofend(struct iommu_test_hw_info, flags)) 399 assert(!info->flags); 400 } 401 402 return 0; 403 } 404 405 #define test_cmd_get_hw_info(device_id, data, data_len) \ 406 ASSERT_EQ(0, _test_cmd_get_hw_info(self->fd, device_id, \ 407 data, data_len)) 408 409 #define test_err_get_hw_info(_errno, device_id, data, data_len) \ 410 EXPECT_ERRNO(_errno, \ 411 _test_cmd_get_hw_info(self->fd, device_id, \ 412 data, data_len)) 413