Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iommufd/selftest: Add coverage for IOMMU_GET_HW_INFO ioctl

Add a mock_domain_hw_info function and an iommu_test_hw_info data
structure. This allows to test the IOMMU_GET_HW_INFO ioctl passing the
test_reg value for the mock_dev.

Link: https://lore.kernel.org/r/20230818101033.4100-5-yi.l.liu@intel.com
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Signed-off-by: Yi Liu <yi.l.liu@intel.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>

authored by

Nicolin Chen and committed by
Jason Gunthorpe
af4fde93 55dd4023

+128 -1
+9
drivers/iommu/iommufd/iommufd_test.h
··· 100 100 }; 101 101 #define IOMMU_TEST_CMD _IO(IOMMUFD_TYPE, IOMMUFD_CMD_BASE + 32) 102 102 103 + /* Mock structs for IOMMU_DEVICE_GET_HW_INFO ioctl */ 104 + #define IOMMU_HW_INFO_TYPE_SELFTEST 0xfeedbeef 105 + #define IOMMU_HW_INFO_SELFTEST_REGVAL 0xdeadbeef 106 + 107 + struct iommu_test_hw_info { 108 + __u32 flags; 109 + __u32 test_reg; 110 + }; 111 + 103 112 #endif
+16
drivers/iommu/iommufd/selftest.c
··· 131 131 .ops = &mock_blocking_ops, 132 132 }; 133 133 134 + static void *mock_domain_hw_info(struct device *dev, u32 *length, u32 *type) 135 + { 136 + struct iommu_test_hw_info *info; 137 + 138 + info = kzalloc(sizeof(*info), GFP_KERNEL); 139 + if (!info) 140 + return ERR_PTR(-ENOMEM); 141 + 142 + info->test_reg = IOMMU_HW_INFO_SELFTEST_REGVAL; 143 + *length = sizeof(*info); 144 + *type = IOMMU_HW_INFO_TYPE_SELFTEST; 145 + 146 + return info; 147 + } 148 + 134 149 static struct iommu_domain *mock_domain_alloc(unsigned int iommu_domain_type) 135 150 { 136 151 struct mock_iommu_domain *mock; ··· 305 290 static const struct iommu_ops mock_ops = { 306 291 .owner = THIS_MODULE, 307 292 .pgsize_bitmap = MOCK_IO_PAGE_SIZE, 293 + .hw_info = mock_domain_hw_info, 308 294 .domain_alloc = mock_domain_alloc, 309 295 .capable = mock_domain_capable, 310 296 .set_platform_dma_ops = mock_domain_set_plaform_dma_ops,
+37 -1
tools/testing/selftests/iommu/iommufd.c
··· 113 113 } 114 114 115 115 TEST_LENGTH(iommu_destroy, IOMMU_DESTROY); 116 + TEST_LENGTH(iommu_hw_info, IOMMU_GET_HW_INFO); 116 117 TEST_LENGTH(iommu_ioas_alloc, IOMMU_IOAS_ALLOC); 117 118 TEST_LENGTH(iommu_ioas_iova_ranges, IOMMU_IOAS_IOVA_RANGES); 118 119 TEST_LENGTH(iommu_ioas_allow_iovas, IOMMU_IOAS_ALLOW_IOVAS); ··· 186 185 uint32_t ioas_id; 187 186 uint32_t stdev_id; 188 187 uint32_t hwpt_id; 188 + uint32_t device_id; 189 189 uint64_t base_iova; 190 190 }; 191 191 ··· 213 211 214 212 for (i = 0; i != variant->mock_domains; i++) { 215 213 test_cmd_mock_domain(self->ioas_id, &self->stdev_id, 216 - &self->hwpt_id, NULL); 214 + &self->hwpt_id, &self->device_id); 217 215 self->base_iova = MOCK_APERTURE_START; 218 216 } 219 217 } ··· 289 287 for (i = 0; i != 10; i++) { 290 288 test_ioctl_ioas_map_fixed(buffer, PAGE_SIZE, 291 289 self->base_iova + i * PAGE_SIZE); 290 + } 291 + } 292 + 293 + TEST_F(iommufd_ioas, get_hw_info) 294 + { 295 + struct iommu_test_hw_info buffer_exact; 296 + struct iommu_test_hw_info_buffer_larger { 297 + struct iommu_test_hw_info info; 298 + uint64_t trailing_bytes; 299 + } buffer_larger; 300 + struct iommu_test_hw_info_buffer_smaller { 301 + __u32 flags; 302 + } buffer_smaller; 303 + 304 + if (self->device_id) { 305 + /* Provide a zero-size user_buffer */ 306 + test_cmd_get_hw_info(self->device_id, NULL, 0); 307 + /* Provide a user_buffer with exact size */ 308 + test_cmd_get_hw_info(self->device_id, &buffer_exact, sizeof(buffer_exact)); 309 + /* 310 + * Provide a user_buffer with size larger than the exact size to check if 311 + * kernel zero the trailing bytes. 312 + */ 313 + test_cmd_get_hw_info(self->device_id, &buffer_larger, sizeof(buffer_larger)); 314 + /* 315 + * Provide a user_buffer with size smaller than the exact size to check if 316 + * the fields within the size range still gets updated. 317 + */ 318 + test_cmd_get_hw_info(self->device_id, &buffer_smaller, sizeof(buffer_smaller)); 319 + } else { 320 + test_err_get_hw_info(ENOENT, self->device_id, 321 + &buffer_exact, sizeof(buffer_exact)); 322 + test_err_get_hw_info(ENOENT, self->device_id, 323 + &buffer_larger, sizeof(buffer_larger)); 292 324 } 293 325 } 294 326
+4
tools/testing/selftests/iommu/iommufd_fail_nth.c
··· 576 576 /* device.c */ 577 577 TEST_FAIL_NTH(basic_fail_nth, device) 578 578 { 579 + struct iommu_test_hw_info info; 579 580 uint32_t ioas_id; 580 581 uint32_t ioas_id2; 581 582 uint32_t stdev_id; ··· 610 609 611 610 if (_test_cmd_mock_domain(self->fd, ioas_id, &stdev_id, NULL, 612 611 &idev_id)) 612 + return -1; 613 + 614 + if (_test_cmd_get_hw_info(self->fd, idev_id, &info, sizeof(info))) 613 615 return -1; 614 616 615 617 if (_test_cmd_hwpt_alloc(self->fd, idev_id, ioas_id, &hwpt_id))
+62
tools/testing/selftests/iommu/iommufd_utils.h
··· 21 21 22 22 static unsigned long PAGE_SIZE; 23 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 + 24 28 /* 25 29 * Have the kernel check the refcount on pages. I don't know why a freshly 26 30 * mmap'd anon non-compound page starts out with a ref of 3 ··· 352 348 }) 353 349 354 350 #endif 351 + 352 + /* @data can be NULL */ 353 + static int _test_cmd_get_hw_info(int fd, __u32 device_id, 354 + void *data, size_t data_len) 355 + { 356 + struct iommu_test_hw_info *info = (struct iommu_test_hw_info *)data; 357 + struct iommu_hw_info cmd = { 358 + .size = sizeof(cmd), 359 + .dev_id = device_id, 360 + .data_len = data_len, 361 + .data_uptr = (uint64_t)data, 362 + }; 363 + int ret; 364 + 365 + ret = ioctl(fd, IOMMU_GET_HW_INFO, &cmd); 366 + if (ret) 367 + return ret; 368 + 369 + assert(cmd.out_data_type == IOMMU_HW_INFO_TYPE_SELFTEST); 370 + 371 + /* 372 + * The struct iommu_test_hw_info should be the one defined 373 + * by the current kernel. 374 + */ 375 + assert(cmd.data_len == sizeof(struct iommu_test_hw_info)); 376 + 377 + /* 378 + * Trailing bytes should be 0 if user buffer is larger than 379 + * the data that kernel reports. 380 + */ 381 + if (data_len > cmd.data_len) { 382 + char *ptr = (char *)(data + cmd.data_len); 383 + int idx = 0; 384 + 385 + while (idx < data_len - cmd.data_len) { 386 + assert(!*(ptr + idx)); 387 + idx++; 388 + } 389 + } 390 + 391 + if (info) { 392 + if (data_len >= offsetofend(struct iommu_test_hw_info, test_reg)) 393 + assert(info->test_reg == IOMMU_HW_INFO_SELFTEST_REGVAL); 394 + if (data_len >= offsetofend(struct iommu_test_hw_info, flags)) 395 + assert(!info->flags); 396 + } 397 + 398 + return 0; 399 + } 400 + 401 + #define test_cmd_get_hw_info(device_id, data, data_len) \ 402 + ASSERT_EQ(0, _test_cmd_get_hw_info(self->fd, device_id, \ 403 + data, data_len)) 404 + 405 + #define test_err_get_hw_info(_errno, device_id, data, data_len) \ 406 + EXPECT_ERRNO(_errno, \ 407 + _test_cmd_get_hw_info(self->fd, device_id, \ 408 + data, data_len))