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>pull/806/head
parent
55dd4023ce
commit
af4fde93c3
|
|
@ -100,4 +100,13 @@ struct iommu_test_cmd {
|
||||||
};
|
};
|
||||||
#define IOMMU_TEST_CMD _IO(IOMMUFD_TYPE, IOMMUFD_CMD_BASE + 32)
|
#define IOMMU_TEST_CMD _IO(IOMMUFD_TYPE, IOMMUFD_CMD_BASE + 32)
|
||||||
|
|
||||||
|
/* Mock structs for IOMMU_DEVICE_GET_HW_INFO ioctl */
|
||||||
|
#define IOMMU_HW_INFO_TYPE_SELFTEST 0xfeedbeef
|
||||||
|
#define IOMMU_HW_INFO_SELFTEST_REGVAL 0xdeadbeef
|
||||||
|
|
||||||
|
struct iommu_test_hw_info {
|
||||||
|
__u32 flags;
|
||||||
|
__u32 test_reg;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,21 @@ static struct iommu_domain mock_blocking_domain = {
|
||||||
.ops = &mock_blocking_ops,
|
.ops = &mock_blocking_ops,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void *mock_domain_hw_info(struct device *dev, u32 *length, u32 *type)
|
||||||
|
{
|
||||||
|
struct iommu_test_hw_info *info;
|
||||||
|
|
||||||
|
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||||
|
if (!info)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
info->test_reg = IOMMU_HW_INFO_SELFTEST_REGVAL;
|
||||||
|
*length = sizeof(*info);
|
||||||
|
*type = IOMMU_HW_INFO_TYPE_SELFTEST;
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
static struct iommu_domain *mock_domain_alloc(unsigned int iommu_domain_type)
|
static struct iommu_domain *mock_domain_alloc(unsigned int iommu_domain_type)
|
||||||
{
|
{
|
||||||
struct mock_iommu_domain *mock;
|
struct mock_iommu_domain *mock;
|
||||||
|
|
@ -290,6 +305,7 @@ static struct iommu_device *mock_probe_device(struct device *dev)
|
||||||
static const struct iommu_ops mock_ops = {
|
static const struct iommu_ops mock_ops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.pgsize_bitmap = MOCK_IO_PAGE_SIZE,
|
.pgsize_bitmap = MOCK_IO_PAGE_SIZE,
|
||||||
|
.hw_info = mock_domain_hw_info,
|
||||||
.domain_alloc = mock_domain_alloc,
|
.domain_alloc = mock_domain_alloc,
|
||||||
.capable = mock_domain_capable,
|
.capable = mock_domain_capable,
|
||||||
.set_platform_dma_ops = mock_domain_set_plaform_dma_ops,
|
.set_platform_dma_ops = mock_domain_set_plaform_dma_ops,
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,7 @@ TEST_F(iommufd, cmd_length)
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_LENGTH(iommu_destroy, IOMMU_DESTROY);
|
TEST_LENGTH(iommu_destroy, IOMMU_DESTROY);
|
||||||
|
TEST_LENGTH(iommu_hw_info, IOMMU_GET_HW_INFO);
|
||||||
TEST_LENGTH(iommu_ioas_alloc, IOMMU_IOAS_ALLOC);
|
TEST_LENGTH(iommu_ioas_alloc, IOMMU_IOAS_ALLOC);
|
||||||
TEST_LENGTH(iommu_ioas_iova_ranges, IOMMU_IOAS_IOVA_RANGES);
|
TEST_LENGTH(iommu_ioas_iova_ranges, IOMMU_IOAS_IOVA_RANGES);
|
||||||
TEST_LENGTH(iommu_ioas_allow_iovas, IOMMU_IOAS_ALLOW_IOVAS);
|
TEST_LENGTH(iommu_ioas_allow_iovas, IOMMU_IOAS_ALLOW_IOVAS);
|
||||||
|
|
@ -185,6 +186,7 @@ FIXTURE(iommufd_ioas)
|
||||||
uint32_t ioas_id;
|
uint32_t ioas_id;
|
||||||
uint32_t stdev_id;
|
uint32_t stdev_id;
|
||||||
uint32_t hwpt_id;
|
uint32_t hwpt_id;
|
||||||
|
uint32_t device_id;
|
||||||
uint64_t base_iova;
|
uint64_t base_iova;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -211,7 +213,7 @@ FIXTURE_SETUP(iommufd_ioas)
|
||||||
|
|
||||||
for (i = 0; i != variant->mock_domains; i++) {
|
for (i = 0; i != variant->mock_domains; i++) {
|
||||||
test_cmd_mock_domain(self->ioas_id, &self->stdev_id,
|
test_cmd_mock_domain(self->ioas_id, &self->stdev_id,
|
||||||
&self->hwpt_id, NULL);
|
&self->hwpt_id, &self->device_id);
|
||||||
self->base_iova = MOCK_APERTURE_START;
|
self->base_iova = MOCK_APERTURE_START;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -290,6 +292,40 @@ TEST_F(iommufd_ioas, ioas_area_auto_destroy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(iommufd_ioas, get_hw_info)
|
||||||
|
{
|
||||||
|
struct iommu_test_hw_info buffer_exact;
|
||||||
|
struct iommu_test_hw_info_buffer_larger {
|
||||||
|
struct iommu_test_hw_info info;
|
||||||
|
uint64_t trailing_bytes;
|
||||||
|
} buffer_larger;
|
||||||
|
struct iommu_test_hw_info_buffer_smaller {
|
||||||
|
__u32 flags;
|
||||||
|
} buffer_smaller;
|
||||||
|
|
||||||
|
if (self->device_id) {
|
||||||
|
/* Provide a zero-size user_buffer */
|
||||||
|
test_cmd_get_hw_info(self->device_id, NULL, 0);
|
||||||
|
/* Provide a user_buffer with exact size */
|
||||||
|
test_cmd_get_hw_info(self->device_id, &buffer_exact, sizeof(buffer_exact));
|
||||||
|
/*
|
||||||
|
* Provide a user_buffer with size larger than the exact size to check if
|
||||||
|
* kernel zero the trailing bytes.
|
||||||
|
*/
|
||||||
|
test_cmd_get_hw_info(self->device_id, &buffer_larger, sizeof(buffer_larger));
|
||||||
|
/*
|
||||||
|
* Provide a user_buffer with size smaller than the exact size to check if
|
||||||
|
* the fields within the size range still gets updated.
|
||||||
|
*/
|
||||||
|
test_cmd_get_hw_info(self->device_id, &buffer_smaller, sizeof(buffer_smaller));
|
||||||
|
} else {
|
||||||
|
test_err_get_hw_info(ENOENT, self->device_id,
|
||||||
|
&buffer_exact, sizeof(buffer_exact));
|
||||||
|
test_err_get_hw_info(ENOENT, self->device_id,
|
||||||
|
&buffer_larger, sizeof(buffer_larger));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(iommufd_ioas, area)
|
TEST_F(iommufd_ioas, area)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
|
||||||
|
|
@ -576,6 +576,7 @@ TEST_FAIL_NTH(basic_fail_nth, access_pin_domain)
|
||||||
/* device.c */
|
/* device.c */
|
||||||
TEST_FAIL_NTH(basic_fail_nth, device)
|
TEST_FAIL_NTH(basic_fail_nth, device)
|
||||||
{
|
{
|
||||||
|
struct iommu_test_hw_info info;
|
||||||
uint32_t ioas_id;
|
uint32_t ioas_id;
|
||||||
uint32_t ioas_id2;
|
uint32_t ioas_id2;
|
||||||
uint32_t stdev_id;
|
uint32_t stdev_id;
|
||||||
|
|
@ -611,6 +612,9 @@ TEST_FAIL_NTH(basic_fail_nth, device)
|
||||||
&idev_id))
|
&idev_id))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
if (_test_cmd_get_hw_info(self->fd, idev_id, &info, sizeof(info)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
if (_test_cmd_hwpt_alloc(self->fd, idev_id, ioas_id, &hwpt_id))
|
if (_test_cmd_hwpt_alloc(self->fd, idev_id, ioas_id, &hwpt_id))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,10 @@ static unsigned long BUFFER_SIZE;
|
||||||
|
|
||||||
static unsigned long PAGE_SIZE;
|
static unsigned long PAGE_SIZE;
|
||||||
|
|
||||||
|
#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER))
|
||||||
|
#define offsetofend(TYPE, MEMBER) \
|
||||||
|
(offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Have the kernel check the refcount on pages. I don't know why a freshly
|
* Have the kernel check the refcount on pages. I don't know why a freshly
|
||||||
* mmap'd anon non-compound page starts out with a ref of 3
|
* 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)
|
||||||
})
|
})
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* @data can be NULL */
|
||||||
|
static int _test_cmd_get_hw_info(int fd, __u32 device_id,
|
||||||
|
void *data, size_t data_len)
|
||||||
|
{
|
||||||
|
struct iommu_test_hw_info *info = (struct iommu_test_hw_info *)data;
|
||||||
|
struct iommu_hw_info cmd = {
|
||||||
|
.size = sizeof(cmd),
|
||||||
|
.dev_id = device_id,
|
||||||
|
.data_len = data_len,
|
||||||
|
.data_uptr = (uint64_t)data,
|
||||||
|
};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = ioctl(fd, IOMMU_GET_HW_INFO, &cmd);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
assert(cmd.out_data_type == IOMMU_HW_INFO_TYPE_SELFTEST);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The struct iommu_test_hw_info should be the one defined
|
||||||
|
* by the current kernel.
|
||||||
|
*/
|
||||||
|
assert(cmd.data_len == sizeof(struct iommu_test_hw_info));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Trailing bytes should be 0 if user buffer is larger than
|
||||||
|
* the data that kernel reports.
|
||||||
|
*/
|
||||||
|
if (data_len > cmd.data_len) {
|
||||||
|
char *ptr = (char *)(data + cmd.data_len);
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
while (idx < data_len - cmd.data_len) {
|
||||||
|
assert(!*(ptr + idx));
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info) {
|
||||||
|
if (data_len >= offsetofend(struct iommu_test_hw_info, test_reg))
|
||||||
|
assert(info->test_reg == IOMMU_HW_INFO_SELFTEST_REGVAL);
|
||||||
|
if (data_len >= offsetofend(struct iommu_test_hw_info, flags))
|
||||||
|
assert(!info->flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define test_cmd_get_hw_info(device_id, data, data_len) \
|
||||||
|
ASSERT_EQ(0, _test_cmd_get_hw_info(self->fd, device_id, \
|
||||||
|
data, data_len))
|
||||||
|
|
||||||
|
#define test_err_get_hw_info(_errno, device_id, data, data_len) \
|
||||||
|
EXPECT_ERRNO(_errno, \
|
||||||
|
_test_cmd_get_hw_info(self->fd, device_id, \
|
||||||
|
data, data_len))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue