Skip to content

Commit f4b20bb

Browse files
committed
iommufd: Add kernel support for testing iommufd
Provide a mock kernel module for the iommu_domain that allows it to run without any HW and the mocking provides a way to directly validate that the PFNs loaded into the iommu_domain are correct. This exposes the access kAPI toward userspace to allow userspace to explore the functionality of pages.c and io_pagetable.c The mock also simulates the rare case of PAGE_SIZE > iommu page size as the mock will operate at a 2K iommu page size. This allows exercising all of the calculations to support this mismatch. This is also intended to support syzkaller exploring the same space. However, it is an unusually invasive config option to enable all of this. The config option should not be enabled in a production kernel. Link: https://lore.kernel.org/r/[email protected] Tested-by: Matthew Rosato <[email protected]> # s390 Tested-by: Eric Auger <[email protected]> # aarch64 Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent d624d66 commit f4b20bb

File tree

10 files changed

+1061
-0
lines changed

10 files changed

+1061
-0
lines changed

drivers/iommu/iommufd/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,15 @@ config IOMMUFD
1010
it relates to managing IO page tables that point at user space memory.
1111

1212
If you don't know what to do here, say N.
13+
14+
if IOMMUFD
15+
config IOMMUFD_TEST
16+
bool "IOMMU Userspace API Test support"
17+
depends on DEBUG_KERNEL
18+
depends on FAULT_INJECTION
19+
depends on RUNTIME_TESTING_MENU
20+
default n
21+
help
22+
This is dangerous, do not enable unless running
23+
tools/testing/selftests/iommu
24+
endif

drivers/iommu/iommufd/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ iommufd-y := \
88
pages.o \
99
vfio_compat.o
1010

11+
iommufd-$(CONFIG_IOMMUFD_TEST) += selftest.o
12+
1113
obj-$(CONFIG_IOMMUFD) += iommufd.o

drivers/iommu/iommufd/device.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,3 +733,41 @@ int iommufd_access_rw(struct iommufd_access *access, unsigned long iova,
733733
return rc;
734734
}
735735
EXPORT_SYMBOL_NS_GPL(iommufd_access_rw, IOMMUFD);
736+
737+
#ifdef CONFIG_IOMMUFD_TEST
738+
/*
739+
* Creating a real iommufd_device is too hard, bypass creating a iommufd_device
740+
* and go directly to attaching a domain.
741+
*/
742+
struct iommufd_hw_pagetable *
743+
iommufd_device_selftest_attach(struct iommufd_ctx *ictx,
744+
struct iommufd_ioas *ioas,
745+
struct device *mock_dev)
746+
{
747+
struct iommufd_hw_pagetable *hwpt;
748+
int rc;
749+
750+
hwpt = iommufd_hw_pagetable_alloc(ictx, ioas, mock_dev);
751+
if (IS_ERR(hwpt))
752+
return hwpt;
753+
754+
rc = iopt_table_add_domain(&hwpt->ioas->iopt, hwpt->domain);
755+
if (rc)
756+
goto out_hwpt;
757+
758+
refcount_inc(&hwpt->obj.users);
759+
iommufd_object_finalize(ictx, &hwpt->obj);
760+
return hwpt;
761+
762+
out_hwpt:
763+
iommufd_object_abort_and_destroy(ictx, &hwpt->obj);
764+
return ERR_PTR(rc);
765+
}
766+
767+
void iommufd_device_selftest_detach(struct iommufd_ctx *ictx,
768+
struct iommufd_hw_pagetable *hwpt)
769+
{
770+
iopt_table_remove_domain(&hwpt->ioas->iopt, hwpt->domain);
771+
refcount_dec(&hwpt->obj.users);
772+
}
773+
#endif

drivers/iommu/iommufd/ioas.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,9 @@ int iommufd_ioas_copy(struct iommufd_ucmd *ucmd)
242242
unsigned long iova;
243243
int rc;
244244

245+
iommufd_test_syz_conv_iova_id(ucmd, cmd->src_ioas_id, &cmd->src_iova,
246+
&cmd->flags);
247+
245248
if ((cmd->flags &
246249
~(IOMMU_IOAS_MAP_FIXED_IOVA | IOMMU_IOAS_MAP_WRITEABLE |
247250
IOMMU_IOAS_MAP_READABLE)))

drivers/iommu/iommufd/iommufd_private.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ enum iommufd_object_type {
113113
IOMMUFD_OBJ_HW_PAGETABLE,
114114
IOMMUFD_OBJ_IOAS,
115115
IOMMUFD_OBJ_ACCESS,
116+
#ifdef CONFIG_IOMMUFD_TEST
117+
IOMMUFD_OBJ_SELFTEST,
118+
#endif
116119
};
117120

118121
/* Base struct for all objects with a userspace ID handle. */
@@ -269,4 +272,36 @@ void iopt_remove_access(struct io_pagetable *iopt,
269272
struct iommufd_access *access);
270273
void iommufd_access_destroy_object(struct iommufd_object *obj);
271274

275+
#ifdef CONFIG_IOMMUFD_TEST
276+
struct iommufd_hw_pagetable *
277+
iommufd_device_selftest_attach(struct iommufd_ctx *ictx,
278+
struct iommufd_ioas *ioas,
279+
struct device *mock_dev);
280+
void iommufd_device_selftest_detach(struct iommufd_ctx *ictx,
281+
struct iommufd_hw_pagetable *hwpt);
282+
int iommufd_test(struct iommufd_ucmd *ucmd);
283+
void iommufd_selftest_destroy(struct iommufd_object *obj);
284+
extern size_t iommufd_test_memory_limit;
285+
void iommufd_test_syz_conv_iova_id(struct iommufd_ucmd *ucmd,
286+
unsigned int ioas_id, u64 *iova, u32 *flags);
287+
bool iommufd_should_fail(void);
288+
void __init iommufd_test_init(void);
289+
void iommufd_test_exit(void);
290+
#else
291+
static inline void iommufd_test_syz_conv_iova_id(struct iommufd_ucmd *ucmd,
292+
unsigned int ioas_id,
293+
u64 *iova, u32 *flags)
294+
{
295+
}
296+
static inline bool iommufd_should_fail(void)
297+
{
298+
return false;
299+
}
300+
static inline void __init iommufd_test_init(void)
301+
{
302+
}
303+
static inline void iommufd_test_exit(void)
304+
{
305+
}
306+
#endif
272307
#endif
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES.
3+
*/
4+
#ifndef _UAPI_IOMMUFD_TEST_H
5+
#define _UAPI_IOMMUFD_TEST_H
6+
7+
#include <linux/types.h>
8+
#include <linux/iommufd.h>
9+
10+
enum {
11+
IOMMU_TEST_OP_ADD_RESERVED = 1,
12+
IOMMU_TEST_OP_MOCK_DOMAIN,
13+
IOMMU_TEST_OP_MD_CHECK_MAP,
14+
IOMMU_TEST_OP_MD_CHECK_REFS,
15+
IOMMU_TEST_OP_CREATE_ACCESS,
16+
IOMMU_TEST_OP_DESTROY_ACCESS_PAGES,
17+
IOMMU_TEST_OP_ACCESS_PAGES,
18+
IOMMU_TEST_OP_ACCESS_RW,
19+
IOMMU_TEST_OP_SET_TEMP_MEMORY_LIMIT,
20+
};
21+
22+
enum {
23+
MOCK_APERTURE_START = 1UL << 24,
24+
MOCK_APERTURE_LAST = (1UL << 31) - 1,
25+
};
26+
27+
enum {
28+
MOCK_FLAGS_ACCESS_WRITE = 1 << 0,
29+
MOCK_FLAGS_ACCESS_SYZ = 1 << 16,
30+
};
31+
32+
enum {
33+
MOCK_ACCESS_RW_WRITE = 1 << 0,
34+
MOCK_ACCESS_RW_SLOW_PATH = 1 << 2,
35+
};
36+
37+
enum {
38+
MOCK_FLAGS_ACCESS_CREATE_NEEDS_PIN_PAGES = 1 << 0,
39+
};
40+
41+
struct iommu_test_cmd {
42+
__u32 size;
43+
__u32 op;
44+
__u32 id;
45+
__u32 __reserved;
46+
union {
47+
struct {
48+
__aligned_u64 start;
49+
__aligned_u64 length;
50+
} add_reserved;
51+
struct {
52+
__u32 out_device_id;
53+
__u32 out_hwpt_id;
54+
} mock_domain;
55+
struct {
56+
__aligned_u64 iova;
57+
__aligned_u64 length;
58+
__aligned_u64 uptr;
59+
} check_map;
60+
struct {
61+
__aligned_u64 length;
62+
__aligned_u64 uptr;
63+
__u32 refs;
64+
} check_refs;
65+
struct {
66+
__u32 out_access_fd;
67+
__u32 flags;
68+
} create_access;
69+
struct {
70+
__u32 access_pages_id;
71+
} destroy_access_pages;
72+
struct {
73+
__u32 flags;
74+
__u32 out_access_pages_id;
75+
__aligned_u64 iova;
76+
__aligned_u64 length;
77+
__aligned_u64 uptr;
78+
} access_pages;
79+
struct {
80+
__aligned_u64 iova;
81+
__aligned_u64 length;
82+
__aligned_u64 uptr;
83+
__u32 flags;
84+
} access_rw;
85+
struct {
86+
__u32 limit;
87+
} memory_limit;
88+
};
89+
__u32 last;
90+
};
91+
#define IOMMU_TEST_CMD _IO(IOMMUFD_TYPE, IOMMUFD_CMD_BASE + 32)
92+
93+
#endif

drivers/iommu/iommufd/main.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <linux/iommufd.h>
2020

2121
#include "iommufd_private.h"
22+
#include "iommufd_test.h"
2223

2324
struct iommufd_object_ops {
2425
void (*destroy)(struct iommufd_object *obj);
@@ -239,6 +240,9 @@ union ucmd_buffer {
239240
struct iommu_ioas_iova_ranges iova_ranges;
240241
struct iommu_ioas_map map;
241242
struct iommu_ioas_unmap unmap;
243+
#ifdef CONFIG_IOMMUFD_TEST
244+
struct iommu_test_cmd test;
245+
#endif
242246
};
243247

244248
struct iommufd_ioctl_op {
@@ -275,6 +279,9 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = {
275279
val64),
276280
IOCTL_OP(IOMMU_VFIO_IOAS, iommufd_vfio_ioas, struct iommu_vfio_ioas,
277281
__reserved),
282+
#ifdef CONFIG_IOMMUFD_TEST
283+
IOCTL_OP(IOMMU_TEST_CMD, iommufd_test, struct iommu_test_cmd, last),
284+
#endif
278285
};
279286

280287
static long iommufd_fops_ioctl(struct file *filp, unsigned int cmd,
@@ -375,6 +382,11 @@ static const struct iommufd_object_ops iommufd_object_ops[] = {
375382
[IOMMUFD_OBJ_HW_PAGETABLE] = {
376383
.destroy = iommufd_hw_pagetable_destroy,
377384
},
385+
#ifdef CONFIG_IOMMUFD_TEST
386+
[IOMMUFD_OBJ_SELFTEST] = {
387+
.destroy = iommufd_selftest_destroy,
388+
},
389+
#endif
378390
};
379391

380392
static struct miscdevice iommu_misc_dev = {
@@ -392,11 +404,13 @@ static int __init iommufd_init(void)
392404
ret = misc_register(&iommu_misc_dev);
393405
if (ret)
394406
return ret;
407+
iommufd_test_init();
395408
return 0;
396409
}
397410

398411
static void __exit iommufd_exit(void)
399412
{
413+
iommufd_test_exit();
400414
misc_deregister(&iommu_misc_dev);
401415
}
402416

drivers/iommu/iommufd/pages.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,11 @@
5656
#include "io_pagetable.h"
5757
#include "double_span.h"
5858

59+
#ifndef CONFIG_IOMMUFD_TEST
5960
#define TEMP_MEMORY_LIMIT 65536
61+
#else
62+
#define TEMP_MEMORY_LIMIT iommufd_test_memory_limit
63+
#endif
6064
#define BATCH_BACKUP_SIZE 32
6165

6266
/*
@@ -1756,6 +1760,10 @@ int iopt_pages_rw_access(struct iopt_pages *pages, unsigned long start_byte,
17561760
bool change_mm = current->mm != pages->source_mm;
17571761
int rc = 0;
17581762

1763+
if (IS_ENABLED(CONFIG_IOMMUFD_TEST) &&
1764+
(flags & __IOMMUFD_ACCESS_RW_SLOW_PATH))
1765+
change_mm = true;
1766+
17591767
if ((flags & IOMMUFD_ACCESS_RW_WRITE) && !pages->writable)
17601768
return -EPERM;
17611769

0 commit comments

Comments
 (0)