Skip to content

Commit af4fde9

Browse files
nicolincjgunthorpe
authored andcommitted
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>
1 parent 55dd402 commit af4fde9

File tree

5 files changed

+128
-1
lines changed

5 files changed

+128
-1
lines changed

drivers/iommu/iommufd/iommufd_test.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,13 @@ struct iommu_test_cmd {
100100
};
101101
#define IOMMU_TEST_CMD _IO(IOMMUFD_TYPE, IOMMUFD_CMD_BASE + 32)
102102

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+
103112
#endif

drivers/iommu/iommufd/selftest.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,21 @@ static struct iommu_domain mock_blocking_domain = {
131131
.ops = &mock_blocking_ops,
132132
};
133133

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+
134149
static struct iommu_domain *mock_domain_alloc(unsigned int iommu_domain_type)
135150
{
136151
struct mock_iommu_domain *mock;
@@ -290,6 +305,7 @@ static struct iommu_device *mock_probe_device(struct device *dev)
290305
static const struct iommu_ops mock_ops = {
291306
.owner = THIS_MODULE,
292307
.pgsize_bitmap = MOCK_IO_PAGE_SIZE,
308+
.hw_info = mock_domain_hw_info,
293309
.domain_alloc = mock_domain_alloc,
294310
.capable = mock_domain_capable,
295311
.set_platform_dma_ops = mock_domain_set_plaform_dma_ops,

tools/testing/selftests/iommu/iommufd.c

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ TEST_F(iommufd, cmd_length)
113113
}
114114

115115
TEST_LENGTH(iommu_destroy, IOMMU_DESTROY);
116+
TEST_LENGTH(iommu_hw_info, IOMMU_GET_HW_INFO);
116117
TEST_LENGTH(iommu_ioas_alloc, IOMMU_IOAS_ALLOC);
117118
TEST_LENGTH(iommu_ioas_iova_ranges, IOMMU_IOAS_IOVA_RANGES);
118119
TEST_LENGTH(iommu_ioas_allow_iovas, IOMMU_IOAS_ALLOW_IOVAS);
@@ -185,6 +186,7 @@ FIXTURE(iommufd_ioas)
185186
uint32_t ioas_id;
186187
uint32_t stdev_id;
187188
uint32_t hwpt_id;
189+
uint32_t device_id;
188190
uint64_t base_iova;
189191
};
190192

@@ -211,7 +213,7 @@ FIXTURE_SETUP(iommufd_ioas)
211213

212214
for (i = 0; i != variant->mock_domains; i++) {
213215
test_cmd_mock_domain(self->ioas_id, &self->stdev_id,
214-
&self->hwpt_id, NULL);
216+
&self->hwpt_id, &self->device_id);
215217
self->base_iova = MOCK_APERTURE_START;
216218
}
217219
}
@@ -290,6 +292,40 @@ TEST_F(iommufd_ioas, ioas_area_auto_destroy)
290292
}
291293
}
292294

295+
TEST_F(iommufd_ioas, get_hw_info)
296+
{
297+
struct iommu_test_hw_info buffer_exact;
298+
struct iommu_test_hw_info_buffer_larger {
299+
struct iommu_test_hw_info info;
300+
uint64_t trailing_bytes;
301+
} buffer_larger;
302+
struct iommu_test_hw_info_buffer_smaller {
303+
__u32 flags;
304+
} buffer_smaller;
305+
306+
if (self->device_id) {
307+
/* Provide a zero-size user_buffer */
308+
test_cmd_get_hw_info(self->device_id, NULL, 0);
309+
/* Provide a user_buffer with exact size */
310+
test_cmd_get_hw_info(self->device_id, &buffer_exact, sizeof(buffer_exact));
311+
/*
312+
* Provide a user_buffer with size larger than the exact size to check if
313+
* kernel zero the trailing bytes.
314+
*/
315+
test_cmd_get_hw_info(self->device_id, &buffer_larger, sizeof(buffer_larger));
316+
/*
317+
* Provide a user_buffer with size smaller than the exact size to check if
318+
* the fields within the size range still gets updated.
319+
*/
320+
test_cmd_get_hw_info(self->device_id, &buffer_smaller, sizeof(buffer_smaller));
321+
} else {
322+
test_err_get_hw_info(ENOENT, self->device_id,
323+
&buffer_exact, sizeof(buffer_exact));
324+
test_err_get_hw_info(ENOENT, self->device_id,
325+
&buffer_larger, sizeof(buffer_larger));
326+
}
327+
}
328+
293329
TEST_F(iommufd_ioas, area)
294330
{
295331
int i;

tools/testing/selftests/iommu/iommufd_fail_nth.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,7 @@ TEST_FAIL_NTH(basic_fail_nth, access_pin_domain)
576576
/* device.c */
577577
TEST_FAIL_NTH(basic_fail_nth, device)
578578
{
579+
struct iommu_test_hw_info info;
579580
uint32_t ioas_id;
580581
uint32_t ioas_id2;
581582
uint32_t stdev_id;
@@ -611,6 +612,9 @@ TEST_FAIL_NTH(basic_fail_nth, device)
611612
&idev_id))
612613
return -1;
613614

615+
if (_test_cmd_get_hw_info(self->fd, idev_id, &info, sizeof(info)))
616+
return -1;
617+
614618
if (_test_cmd_hwpt_alloc(self->fd, idev_id, ioas_id, &hwpt_id))
615619
return -1;
616620

tools/testing/selftests/iommu/iommufd_utils.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ static unsigned long BUFFER_SIZE;
2121

2222
static unsigned long PAGE_SIZE;
2323

24+
#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER))
25+
#define offsetofend(TYPE, MEMBER) \
26+
(offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER))
27+
2428
/*
2529
* Have the kernel check the refcount on pages. I don't know why a freshly
2630
* mmap'd anon non-compound page starts out with a ref of 3
@@ -348,3 +352,61 @@ static void teardown_iommufd(int fd, struct __test_metadata *_metadata)
348352
})
349353

350354
#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))

0 commit comments

Comments
 (0)