xref: /linux/drivers/iommu/iommufd/selftest.c (revision 9f2347842b526cbc2655068591fb0166362d2999)
1f4b20bb3SJason Gunthorpe // SPDX-License-Identifier: GPL-2.0
2f4b20bb3SJason Gunthorpe /* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES.
3f4b20bb3SJason Gunthorpe  *
4f4b20bb3SJason Gunthorpe  * Kernel side components to support tools/testing/selftests/iommu
5f4b20bb3SJason Gunthorpe  */
6f4b20bb3SJason Gunthorpe #include <linux/anon_inodes.h>
7f4b20bb3SJason Gunthorpe #include <linux/debugfs.h>
8f4b20bb3SJason Gunthorpe #include <linux/fault-inject.h>
9f4b20bb3SJason Gunthorpe #include <linux/file.h>
10f4b20bb3SJason Gunthorpe #include <linux/iommu.h>
11f4b20bb3SJason Gunthorpe #include <linux/platform_device.h>
1223a1b46fSJason Gunthorpe #include <linux/slab.h>
13f4b20bb3SJason Gunthorpe #include <linux/xarray.h>
14f4b20bb3SJason Gunthorpe #include <uapi/linux/iommufd.h>
1523a1b46fSJason Gunthorpe 
16f4b20bb3SJason Gunthorpe #include "../iommu-priv.h"
17f4b20bb3SJason Gunthorpe #include "io_pagetable.h"
18f4b20bb3SJason Gunthorpe #include "iommufd_private.h"
19f4b20bb3SJason Gunthorpe #include "iommufd_test.h"
20f4b20bb3SJason Gunthorpe 
21f4b20bb3SJason Gunthorpe static DECLARE_FAULT_ATTR(fail_iommufd);
2223a1b46fSJason Gunthorpe static struct dentry *dbgfs_root;
23b2b67c99SJason Gunthorpe static struct platform_device *selftest_iommu_dev;
24b2b67c99SJason Gunthorpe static const struct iommu_ops mock_ops;
25f4b20bb3SJason Gunthorpe static struct iommu_domain_ops domain_nested_ops;
26f4b20bb3SJason Gunthorpe 
27f4b20bb3SJason Gunthorpe size_t iommufd_test_memory_limit = 65536;
2847f2bd2fSJason Gunthorpe 
2947f2bd2fSJason Gunthorpe struct mock_bus_type {
3047f2bd2fSJason Gunthorpe 	struct bus_type bus;
3147f2bd2fSJason Gunthorpe 	struct notifier_block nb;
3247f2bd2fSJason Gunthorpe };
3347f2bd2fSJason Gunthorpe 
3447f2bd2fSJason Gunthorpe static struct mock_bus_type iommufd_mock_bus_type = {
3547f2bd2fSJason Gunthorpe 	.bus = {
3647f2bd2fSJason Gunthorpe 		.name = "iommufd_mock",
3747f2bd2fSJason Gunthorpe 	},
3847f2bd2fSJason Gunthorpe };
39fde372dfSNicolin Chen 
4047f2bd2fSJason Gunthorpe static DEFINE_IDA(mock_dev_ida);
41f4b20bb3SJason Gunthorpe 
427adf267dSJoao Martins enum {
43f4b20bb3SJason Gunthorpe 	MOCK_DIRTY_TRACK = 1,
447db521e2SJoao Martins 	MOCK_IO_PAGE_SIZE = PAGE_SIZE / 2,
45f4b20bb3SJason Gunthorpe 	MOCK_HUGE_PAGE_SIZE = 512 * MOCK_IO_PAGE_SIZE,
46f4b20bb3SJason Gunthorpe 
47f4b20bb3SJason Gunthorpe 	/*
48f4b20bb3SJason Gunthorpe 	 * Like a real page table alignment requires the low bits of the address
49f4b20bb3SJason Gunthorpe 	 * to be zero. xarray also requires the high bit to be zero, so we store
50f4b20bb3SJason Gunthorpe 	 * the pfns shifted. The upper bits are used for metadata.
51f4b20bb3SJason Gunthorpe 	 */
52f4b20bb3SJason Gunthorpe 	MOCK_PFN_MASK = ULONG_MAX / MOCK_IO_PAGE_SIZE,
53f4b20bb3SJason Gunthorpe 
54f4b20bb3SJason Gunthorpe 	_MOCK_PFN_START = MOCK_PFN_MASK + 1,
55f4b20bb3SJason Gunthorpe 	MOCK_PFN_START_IOVA = _MOCK_PFN_START,
56a9af47e3SJoao Martins 	MOCK_PFN_LAST_IOVA = _MOCK_PFN_START,
577db521e2SJoao Martins 	MOCK_PFN_DIRTY_IOVA = _MOCK_PFN_START << 1,
58f4b20bb3SJason Gunthorpe 	MOCK_PFN_HUGE_IOVA = _MOCK_PFN_START << 2,
59f4b20bb3SJason Gunthorpe };
60f4b20bb3SJason Gunthorpe 
61f4b20bb3SJason Gunthorpe static int mock_dev_enable_iopf(struct device *dev, struct iommu_domain *domain);
62f4b20bb3SJason Gunthorpe static void mock_dev_disable_iopf(struct device *dev, struct iommu_domain *domain);
63f4b20bb3SJason Gunthorpe 
64f4b20bb3SJason Gunthorpe /*
65f4b20bb3SJason Gunthorpe  * Syzkaller has trouble randomizing the correct iova to use since it is linked
66cf7c2789SNicolin Chen  * to the map ioctl's output, and it has no ide about that. So, simplify things.
67f4b20bb3SJason Gunthorpe  * In syzkaller mode the 64 bit IOVA is converted into an nth area and offset
68f4b20bb3SJason Gunthorpe  * value. This has a much smaller randomization space and syzkaller can hit it.
69f4b20bb3SJason Gunthorpe  */
__iommufd_test_syz_conv_iova(struct io_pagetable * iopt,u64 * iova)70f4b20bb3SJason Gunthorpe static unsigned long __iommufd_test_syz_conv_iova(struct io_pagetable *iopt,
71f4b20bb3SJason Gunthorpe 						  u64 *iova)
72f4b20bb3SJason Gunthorpe {
73f4b20bb3SJason Gunthorpe 	struct syz_layout {
74f4b20bb3SJason Gunthorpe 		__u32 nth_area;
75f4b20bb3SJason Gunthorpe 		__u32 offset;
76f4b20bb3SJason Gunthorpe 	};
77f4b20bb3SJason Gunthorpe 	struct syz_layout *syz = (void *)iova;
78f4b20bb3SJason Gunthorpe 	unsigned int nth = syz->nth_area;
79f4b20bb3SJason Gunthorpe 	struct iopt_area *area;
80f4b20bb3SJason Gunthorpe 
81f4b20bb3SJason Gunthorpe 	down_read(&iopt->iova_rwsem);
82f4b20bb3SJason Gunthorpe 	for (area = iopt_area_iter_first(iopt, 0, ULONG_MAX); area;
83f4b20bb3SJason Gunthorpe 	     area = iopt_area_iter_next(area, 0, ULONG_MAX)) {
84f4b20bb3SJason Gunthorpe 		if (nth == 0) {
85f4b20bb3SJason Gunthorpe 			up_read(&iopt->iova_rwsem);
86f4b20bb3SJason Gunthorpe 			return iopt_area_iova(area) + syz->offset;
87f4b20bb3SJason Gunthorpe 		}
88f4b20bb3SJason Gunthorpe 		nth--;
89f4b20bb3SJason Gunthorpe 	}
90f4b20bb3SJason Gunthorpe 	up_read(&iopt->iova_rwsem);
91cf7c2789SNicolin Chen 
92cf7c2789SNicolin Chen 	return 0;
93cf7c2789SNicolin Chen }
94cf7c2789SNicolin Chen 
iommufd_test_syz_conv_iova(struct iommufd_access * access,u64 * iova)95cf7c2789SNicolin Chen static unsigned long iommufd_test_syz_conv_iova(struct iommufd_access *access,
96cf7c2789SNicolin Chen 						u64 *iova)
97cf7c2789SNicolin Chen {
98cf7c2789SNicolin Chen 	unsigned long ret;
99cf7c2789SNicolin Chen 
100cf7c2789SNicolin Chen 	mutex_lock(&access->ioas_lock);
101cf7c2789SNicolin Chen 	if (!access->ioas) {
102cf7c2789SNicolin Chen 		mutex_unlock(&access->ioas_lock);
103cf7c2789SNicolin Chen 		return 0;
104cf7c2789SNicolin Chen 	}
105cf7c2789SNicolin Chen 	ret = __iommufd_test_syz_conv_iova(&access->ioas->iopt, iova);
106f4b20bb3SJason Gunthorpe 	mutex_unlock(&access->ioas_lock);
107f4b20bb3SJason Gunthorpe 	return ret;
108f4b20bb3SJason Gunthorpe }
109f4b20bb3SJason Gunthorpe 
iommufd_test_syz_conv_iova_id(struct iommufd_ucmd * ucmd,unsigned int ioas_id,u64 * iova,u32 * flags)110f4b20bb3SJason Gunthorpe void iommufd_test_syz_conv_iova_id(struct iommufd_ucmd *ucmd,
111f4b20bb3SJason Gunthorpe 				   unsigned int ioas_id, u64 *iova, u32 *flags)
112f4b20bb3SJason Gunthorpe {
113f4b20bb3SJason Gunthorpe 	struct iommufd_ioas *ioas;
114f4b20bb3SJason Gunthorpe 
115325de950SYi Liu 	if (!(*flags & MOCK_FLAGS_ACCESS_SYZ))
116f4b20bb3SJason Gunthorpe 		return;
117f4b20bb3SJason Gunthorpe 	*flags &= ~(u32)MOCK_FLAGS_ACCESS_SYZ;
118cf7c2789SNicolin Chen 
119bd7a2826SJason Gunthorpe 	ioas = iommufd_get_ioas(ucmd->ictx, ioas_id);
120f4b20bb3SJason Gunthorpe 	if (IS_ERR(ioas))
121f4b20bb3SJason Gunthorpe 		return;
122f4b20bb3SJason Gunthorpe 	*iova = __iommufd_test_syz_conv_iova(&ioas->iopt, iova);
1237adf267dSJoao Martins 	iommufd_put_object(ucmd->ictx, &ioas->obj);
124f4b20bb3SJason Gunthorpe }
125f4b20bb3SJason Gunthorpe 
126f4b20bb3SJason Gunthorpe struct mock_iommu_domain {
127f4b20bb3SJason Gunthorpe 	unsigned long flags;
12865fe32f7SNicolin Chen 	struct iommu_domain domain;
12965fe32f7SNicolin Chen 	struct xarray pfns;
13065fe32f7SNicolin Chen };
13165fe32f7SNicolin Chen 
13265fe32f7SNicolin Chen static inline struct mock_iommu_domain *
to_mock_domain(struct iommu_domain * domain)13365fe32f7SNicolin Chen to_mock_domain(struct iommu_domain *domain)
134f4b20bb3SJason Gunthorpe {
135f4b20bb3SJason Gunthorpe 	return container_of(domain, struct mock_iommu_domain, domain);
136f4b20bb3SJason Gunthorpe }
137f4b20bb3SJason Gunthorpe 
13865c619aeSJason Gunthorpe struct mock_iommu_domain_nested {
13965c619aeSJason Gunthorpe 	struct iommu_domain domain;
140e04b23c8SJoao Martins 	struct mock_viommu *mock_viommu;
141fde372dfSNicolin Chen 	u32 iotlb[MOCK_NESTED_DOMAIN_IOTLB_NUM];
14265c619aeSJason Gunthorpe };
14365c619aeSJason Gunthorpe 
144f4b20bb3SJason Gunthorpe static inline struct mock_iommu_domain_nested *
to_mock_nested(struct iommu_domain * domain)145f4b20bb3SJason Gunthorpe to_mock_nested(struct iommu_domain *domain)
146f4b20bb3SJason Gunthorpe {
147f4b20bb3SJason Gunthorpe 	return container_of(domain, struct mock_iommu_domain_nested, domain);
148f4b20bb3SJason Gunthorpe }
149f4b20bb3SJason Gunthorpe 
15065c619aeSJason Gunthorpe struct mock_viommu {
151f4b20bb3SJason Gunthorpe 	struct iommufd_viommu core;
15265c619aeSJason Gunthorpe 	struct mock_iommu_domain *s2_parent;
153f4b20bb3SJason Gunthorpe 	struct mock_hw_queue *hw_queue[IOMMU_TEST_HW_QUEUE_MAX];
154f4b20bb3SJason Gunthorpe 	struct mutex queue_mutex;
155f4b20bb3SJason Gunthorpe 
156f4b20bb3SJason Gunthorpe 	unsigned long mmap_offset;
15765c619aeSJason Gunthorpe 	u32 *page; /* Mmap page to test u32 type of in_data */
15865c619aeSJason Gunthorpe };
15965c619aeSJason Gunthorpe 
to_mock_viommu(struct iommufd_viommu * viommu)160266ce589SJoao Martins static inline struct mock_viommu *to_mock_viommu(struct iommufd_viommu *viommu)
161266ce589SJoao Martins {
162266ce589SJoao Martins 	return container_of(viommu, struct mock_viommu, core);
163266ce589SJoao Martins }
164266ce589SJoao Martins 
16565c619aeSJason Gunthorpe struct mock_hw_queue {
16665c619aeSJason Gunthorpe 	struct iommufd_hw_queue core;
16765c619aeSJason Gunthorpe 	struct mock_viommu *mock_viommu;
16865c619aeSJason Gunthorpe 	struct mock_hw_queue *prev;
16965c619aeSJason Gunthorpe 	u16 index;
17065c619aeSJason Gunthorpe };
17165c619aeSJason Gunthorpe 
17265c619aeSJason Gunthorpe static inline struct mock_hw_queue *
to_mock_hw_queue(struct iommufd_hw_queue * hw_queue)17365c619aeSJason Gunthorpe to_mock_hw_queue(struct iommufd_hw_queue *hw_queue)
17465c619aeSJason Gunthorpe {
17565c619aeSJason Gunthorpe 	return container_of(hw_queue, struct mock_hw_queue, core);
17665c619aeSJason Gunthorpe }
177af4fde93SNicolin Chen 
178af4fde93SNicolin Chen enum selftest_obj_type {
179af4fde93SNicolin Chen 	TYPE_IDEV,
180af4fde93SNicolin Chen };
181af4fde93SNicolin Chen 
182af4fde93SNicolin Chen struct mock_dev {
183af4fde93SNicolin Chen 	struct device dev;
184af4fde93SNicolin Chen 	struct mock_viommu *viommu;
185af4fde93SNicolin Chen 	struct rw_semaphore viommu_rwsem;
186af4fde93SNicolin Chen 	unsigned long flags;
187af4fde93SNicolin Chen 	unsigned long vdev_id;
188af4fde93SNicolin Chen 	int id;
189af4fde93SNicolin Chen 	u32 cache[MOCK_DEV_CACHE_NUM];
190af4fde93SNicolin Chen 	atomic_t pasid_1024_fake_error;
191af4fde93SNicolin Chen 	unsigned int iopf_refcount;
192266ce589SJoao Martins 	struct iommu_domain *domain;
193266ce589SJoao Martins };
194266ce589SJoao Martins 
to_mock_dev(struct device * dev)1957adf267dSJoao Martins static inline struct mock_dev *to_mock_dev(struct device *dev)
1967adf267dSJoao Martins {
1977adf267dSJoao Martins 	return container_of(dev, struct mock_dev, dev);
1987adf267dSJoao Martins }
1997adf267dSJoao Martins 
2007adf267dSJoao Martins struct selftest_obj {
2017adf267dSJoao Martins 	struct iommufd_object obj;
2027adf267dSJoao Martins 	enum selftest_obj_type type;
2037adf267dSJoao Martins 
2047adf267dSJoao Martins 	union {
2057adf267dSJoao Martins 		struct {
2067adf267dSJoao Martins 			struct iommufd_device *idev;
2077adf267dSJoao Martins 			struct iommufd_ctx *ictx;
2087adf267dSJoao Martins 			struct mock_dev *mock_dev;
209266ce589SJoao Martins 		} idev;
210266ce589SJoao Martins 	};
211266ce589SJoao Martins };
21202a8c61aSJoao Martins 
to_selftest_obj(struct iommufd_object * obj)21302a8c61aSJoao Martins static inline struct selftest_obj *to_selftest_obj(struct iommufd_object *obj)
21402a8c61aSJoao Martins {
21502a8c61aSJoao Martins 	return container_of(obj, struct selftest_obj, obj);
21602a8c61aSJoao Martins }
21702a8c61aSJoao Martins 
mock_domain_nop_attach(struct iommu_domain * domain,struct device * dev)21802a8c61aSJoao Martins static int mock_domain_nop_attach(struct iommu_domain *domain,
21902a8c61aSJoao Martins 				  struct device *dev)
22002a8c61aSJoao Martins {
22102a8c61aSJoao Martins 	struct mock_dev *mdev = to_mock_dev(dev);
22202a8c61aSJoao Martins 	struct mock_viommu *new_viommu = NULL;
22302a8c61aSJoao Martins 	unsigned long vdev_id = 0;
22402a8c61aSJoao Martins 	int rc;
22502a8c61aSJoao Martins 
22602a8c61aSJoao Martins 	if (domain->dirty_ops && (mdev->flags & MOCK_FLAGS_DEVICE_NO_DIRTY))
22702a8c61aSJoao Martins 		return -EINVAL;
22802a8c61aSJoao Martins 
22902a8c61aSJoao Martins 	iommu_group_mutex_assert(dev);
23002a8c61aSJoao Martins 	if (domain->type == IOMMU_DOMAIN_NESTED) {
23102a8c61aSJoao Martins 		new_viommu = to_mock_nested(domain)->mock_viommu;
23202a8c61aSJoao Martins 		if (new_viommu) {
23302a8c61aSJoao Martins 			rc = iommufd_viommu_get_vdev_id(&new_viommu->core, dev,
23402a8c61aSJoao Martins 							&vdev_id);
23502a8c61aSJoao Martins 			if (rc)
23602a8c61aSJoao Martins 				return rc;
23702a8c61aSJoao Martins 		}
23802a8c61aSJoao Martins 	}
23902a8c61aSJoao Martins 	if (new_viommu != mdev->viommu) {
240266ce589SJoao Martins 		down_write(&mdev->viommu_rwsem);
241266ce589SJoao Martins 		mdev->viommu = new_viommu;
242266ce589SJoao Martins 		mdev->vdev_id = vdev_id;
243266ce589SJoao Martins 		up_write(&mdev->viommu_rwsem);
244266ce589SJoao Martins 	}
245a9af47e3SJoao Martins 
246a9af47e3SJoao Martins 	rc = mock_dev_enable_iopf(dev, domain);
24702a8c61aSJoao Martins 	if (rc)
24802a8c61aSJoao Martins 		return rc;
249a9af47e3SJoao Martins 
250a9af47e3SJoao Martins 	mock_dev_disable_iopf(dev, mdev->domain);
251a9af47e3SJoao Martins 	mdev->domain = domain;
252a9af47e3SJoao Martins 
25302a8c61aSJoao Martins 	return 0;
25402a8c61aSJoao Martins }
25502a8c61aSJoao Martins 
mock_domain_set_dev_pasid_nop(struct iommu_domain * domain,struct device * dev,ioasid_t pasid,struct iommu_domain * old)256a9af47e3SJoao Martins static int mock_domain_set_dev_pasid_nop(struct iommu_domain *domain,
25702a8c61aSJoao Martins 					 struct device *dev, ioasid_t pasid,
25802a8c61aSJoao Martins 					 struct iommu_domain *old)
25902a8c61aSJoao Martins {
26002a8c61aSJoao Martins 	struct mock_dev *mdev = to_mock_dev(dev);
26102a8c61aSJoao Martins 	int rc;
26202a8c61aSJoao Martins 
2637db521e2SJoao Martins 	/*
2647db521e2SJoao Martins 	 * Per the first attach with pasid 1024, set the
26502a8c61aSJoao Martins 	 * mdev->pasid_1024_fake_error. Hence the second call of this op
26602a8c61aSJoao Martins 	 * can fake an error to validate the error path of the core. This
2670795b305SJoao Martins 	 * is helpful to test the case in which the iommu core needs to
26802a8c61aSJoao Martins 	 * rollback to the old domain due to driver failure. e.g. replace.
26902a8c61aSJoao Martins 	 * User should be careful about the third call of this op, it shall
27002a8c61aSJoao Martins 	 * succeed since the mdev->pasid_1024_fake_error is cleared in the
27102a8c61aSJoao Martins 	 * second call.
272a9af47e3SJoao Martins 	 */
273266ce589SJoao Martins 	if (pasid == 1024) {
274266ce589SJoao Martins 		if (domain->type == IOMMU_DOMAIN_BLOCKED) {
275266ce589SJoao Martins 			atomic_set(&mdev->pasid_1024_fake_error, 0);
276266ce589SJoao Martins 		} else if (atomic_read(&mdev->pasid_1024_fake_error)) {
277266ce589SJoao Martins 			/*
278266ce589SJoao Martins 			 * Clear the flag, and fake an error to fail the
279266ce589SJoao Martins 			 * replacement.
280266ce589SJoao Martins 			 */
281b2b67c99SJason Gunthorpe 			atomic_set(&mdev->pasid_1024_fake_error, 0);
282f4b20bb3SJason Gunthorpe 			return -ENOMEM;
2837db521e2SJoao Martins 		} else {
284f4b20bb3SJason Gunthorpe 			/* Set the flag to fake an error in next call */
285f4b20bb3SJason Gunthorpe 			atomic_set(&mdev->pasid_1024_fake_error, 1);
286f4b20bb3SJason Gunthorpe 		}
287f4b20bb3SJason Gunthorpe 	}
288b2b67c99SJason Gunthorpe 
289f4b20bb3SJason Gunthorpe 	rc = mock_dev_enable_iopf(dev, domain);
290f4b20bb3SJason Gunthorpe 	if (rc)
291f4b20bb3SJason Gunthorpe 		return rc;
2927db521e2SJoao Martins 
2937db521e2SJoao Martins 	mock_dev_disable_iopf(dev, old);
29440866361SYi Liu 
295b2b67c99SJason Gunthorpe 	return 0;
296f4b20bb3SJason Gunthorpe }
297f4b20bb3SJason Gunthorpe 
298f4b20bb3SJason Gunthorpe static const struct iommu_domain_ops mock_blocking_ops = {
299f4b20bb3SJason Gunthorpe 	.attach_dev = mock_domain_nop_attach,
30040866361SYi Liu 	.set_dev_pasid = mock_domain_set_dev_pasid_nop
30165fe32f7SNicolin Chen };
30265fe32f7SNicolin Chen 
30365fe32f7SNicolin Chen static struct iommu_domain mock_blocking_domain = {
30465fe32f7SNicolin Chen 	.type = IOMMU_DOMAIN_BLOCKED,
30565fe32f7SNicolin Chen 	.ops = &mock_blocking_ops,
30665fe32f7SNicolin Chen };
30765fe32f7SNicolin Chen 
mock_domain_hw_info(struct device * dev,u32 * length,enum iommu_hw_info_type * type)30865fe32f7SNicolin Chen static void *mock_domain_hw_info(struct device *dev, u32 *length,
30965fe32f7SNicolin Chen 				 enum iommu_hw_info_type *type)
31065fe32f7SNicolin Chen {
31165fe32f7SNicolin Chen 	struct iommu_test_hw_info *info;
31265fe32f7SNicolin Chen 
31365fe32f7SNicolin Chen 	if (*type != IOMMU_HW_INFO_TYPE_DEFAULT &&
31465fe32f7SNicolin Chen 	    *type != IOMMU_HW_INFO_TYPE_SELFTEST)
31565fe32f7SNicolin Chen 		return ERR_PTR(-EOPNOTSUPP);
31665fe32f7SNicolin Chen 
31765fe32f7SNicolin Chen 	info = kzalloc(sizeof(*info), GFP_KERNEL);
31865fe32f7SNicolin Chen 	if (!info)
3192bdabb8eSYi Liu 		return ERR_PTR(-ENOMEM);
3202bdabb8eSYi Liu 
3212bdabb8eSYi Liu 	info->test_reg = IOMMU_HW_INFO_SELFTEST_REGVAL;
32240866361SYi Liu 	*length = sizeof(*info);
32365fe32f7SNicolin Chen 	*type = IOMMU_HW_INFO_TYPE_SELFTEST;
32465fe32f7SNicolin Chen 
32565fe32f7SNicolin Chen 	return info;
32665fe32f7SNicolin Chen }
32765fe32f7SNicolin Chen 
mock_domain_set_dirty_tracking(struct iommu_domain * domain,bool enable)32865fe32f7SNicolin Chen static int mock_domain_set_dirty_tracking(struct iommu_domain *domain,
329266ce589SJoao Martins 					  bool enable)
33065fe32f7SNicolin Chen {
33165fe32f7SNicolin Chen 	struct mock_iommu_domain *mock = to_mock_domain(domain);
332b2b67c99SJason Gunthorpe 	unsigned long flags = mock->flags;
33340866361SYi Liu 
33465fe32f7SNicolin Chen 	if (enable && !domain->dirty_ops)
33565fe32f7SNicolin Chen 		return -EINVAL;
336266ce589SJoao Martins 
33765fe32f7SNicolin Chen 	/* No change? */
3382bdabb8eSYi Liu 	if (!(enable ^ !!(flags & MOCK_DIRTY_TRACK)))
3397db521e2SJoao Martins 		return 0;
340b2b67c99SJason Gunthorpe 
341b2b67c99SJason Gunthorpe 	flags = (enable ? flags | MOCK_DIRTY_TRACK : flags & ~MOCK_DIRTY_TRACK);
342b2b67c99SJason Gunthorpe 
343b2b67c99SJason Gunthorpe 	mock->flags = flags;
344b2b67c99SJason Gunthorpe 	return 0;
345b2b67c99SJason Gunthorpe }
34665fe32f7SNicolin Chen 
mock_test_and_clear_dirty(struct mock_iommu_domain * mock,unsigned long iova,size_t page_size,unsigned long flags)3472bdabb8eSYi Liu static bool mock_test_and_clear_dirty(struct mock_iommu_domain *mock,
34865fe32f7SNicolin Chen 				      unsigned long iova, size_t page_size,
34965fe32f7SNicolin Chen 				      unsigned long flags)
35040866361SYi Liu {
35165fe32f7SNicolin Chen 	unsigned long cur, end = iova + page_size - 1;
35265fe32f7SNicolin Chen 	bool dirty = false;
35340866361SYi Liu 	void *ent, *old;
35465fe32f7SNicolin Chen 
35565fe32f7SNicolin Chen 	for (cur = iova; cur < end; cur += MOCK_IO_PAGE_SIZE) {
35665fe32f7SNicolin Chen 		ent = xa_load(&mock->pfns, cur / MOCK_IO_PAGE_SIZE);
35765fe32f7SNicolin Chen 		if (!ent || !(xa_to_value(ent) & MOCK_PFN_DIRTY_IOVA))
35865fe32f7SNicolin Chen 			continue;
35965fe32f7SNicolin Chen 
36065fe32f7SNicolin Chen 		dirty = true;
36165fe32f7SNicolin Chen 		/* Clear dirty */
36265fe32f7SNicolin Chen 		if (!(flags & IOMMU_DIRTY_NO_CLEAR)) {
36365fe32f7SNicolin Chen 			unsigned long val;
36440866361SYi Liu 
36540866361SYi Liu 			val = xa_to_value(ent) & ~MOCK_PFN_DIRTY_IOVA;
366f4b20bb3SJason Gunthorpe 			old = xa_store(&mock->pfns, cur / MOCK_IO_PAGE_SIZE,
367f4b20bb3SJason Gunthorpe 				       xa_mk_value(val), GFP_KERNEL);
368f4b20bb3SJason Gunthorpe 			WARN_ON_ONCE(ent != old);
369f4b20bb3SJason Gunthorpe 		}
370f4b20bb3SJason Gunthorpe 	}
371f4b20bb3SJason Gunthorpe 
372f4b20bb3SJason Gunthorpe 	return dirty;
373f4b20bb3SJason Gunthorpe }
374f4b20bb3SJason Gunthorpe 
mock_domain_read_and_clear_dirty(struct iommu_domain * domain,unsigned long iova,size_t size,unsigned long flags,struct iommu_dirty_bitmap * dirty)375f4b20bb3SJason Gunthorpe static int mock_domain_read_and_clear_dirty(struct iommu_domain *domain,
376f4b20bb3SJason Gunthorpe 					    unsigned long iova, size_t size,
377f4b20bb3SJason Gunthorpe 					    unsigned long flags,
378f4b20bb3SJason Gunthorpe 					    struct iommu_dirty_bitmap *dirty)
379f4b20bb3SJason Gunthorpe {
380f4b20bb3SJason Gunthorpe 	struct mock_iommu_domain *mock = to_mock_domain(domain);
381f4b20bb3SJason Gunthorpe 	unsigned long end = iova + size;
382f4b20bb3SJason Gunthorpe 	void *ent;
383f4b20bb3SJason Gunthorpe 
384f4b20bb3SJason Gunthorpe 	if (!(mock->flags & MOCK_DIRTY_TRACK) && dirty->bitmap)
385f4b20bb3SJason Gunthorpe 		return -EINVAL;
386f4b20bb3SJason Gunthorpe 
387f4b20bb3SJason Gunthorpe 	do {
388f4b20bb3SJason Gunthorpe 		unsigned long pgsize = MOCK_IO_PAGE_SIZE;
389f4b20bb3SJason Gunthorpe 		unsigned long head;
390f4b20bb3SJason Gunthorpe 
391f4b20bb3SJason Gunthorpe 		ent = xa_load(&mock->pfns, iova / MOCK_IO_PAGE_SIZE);
392f4b20bb3SJason Gunthorpe 		if (!ent) {
393f4b20bb3SJason Gunthorpe 			iova += pgsize;
394f4b20bb3SJason Gunthorpe 			continue;
395f4b20bb3SJason Gunthorpe 		}
396f4b20bb3SJason Gunthorpe 
397f4b20bb3SJason Gunthorpe 		if (xa_to_value(ent) & MOCK_PFN_HUGE_IOVA)
398f4b20bb3SJason Gunthorpe 			pgsize = MOCK_HUGE_PAGE_SIZE;
399f4b20bb3SJason Gunthorpe 		head = iova & ~(pgsize - 1);
400f4b20bb3SJason Gunthorpe 
401f4b20bb3SJason Gunthorpe 		/* Clear dirty */
4027db521e2SJoao Martins 		if (mock_test_and_clear_dirty(mock, head, pgsize, flags))
4037db521e2SJoao Martins 			iommu_dirty_bitmap_record(dirty, iova, pgsize);
4047db521e2SJoao Martins 		iova += pgsize;
405f4b20bb3SJason Gunthorpe 	} while (iova < end);
406f4b20bb3SJason Gunthorpe 
407f4b20bb3SJason Gunthorpe 	return 0;
408f4b20bb3SJason Gunthorpe }
409f4b20bb3SJason Gunthorpe 
410f4b20bb3SJason Gunthorpe static const struct iommu_dirty_ops dirty_ops = {
411f4b20bb3SJason Gunthorpe 	.set_dirty_tracking = mock_domain_set_dirty_tracking,
412f4b20bb3SJason Gunthorpe 	.read_and_clear_dirty = mock_domain_read_and_clear_dirty,
413f4b20bb3SJason Gunthorpe };
414f4b20bb3SJason Gunthorpe 
415f4b20bb3SJason Gunthorpe static struct mock_iommu_domain_nested *
__mock_domain_alloc_nested(const struct iommu_user_data * user_data)416f4b20bb3SJason Gunthorpe __mock_domain_alloc_nested(const struct iommu_user_data *user_data)
417f4b20bb3SJason Gunthorpe {
418f4b20bb3SJason Gunthorpe 	struct mock_iommu_domain_nested *mock_nested;
419f4b20bb3SJason Gunthorpe 	struct iommu_hwpt_selftest user_cfg;
420f4b20bb3SJason Gunthorpe 	int rc, i;
421f4b20bb3SJason Gunthorpe 
422f4b20bb3SJason Gunthorpe 	if (user_data->type != IOMMU_HWPT_DATA_SELFTEST)
423f4b20bb3SJason Gunthorpe 		return ERR_PTR(-EOPNOTSUPP);
424f4b20bb3SJason Gunthorpe 
425f4b20bb3SJason Gunthorpe 	rc = iommu_copy_struct_from_user(&user_cfg, user_data,
426f4b20bb3SJason Gunthorpe 					 IOMMU_HWPT_DATA_SELFTEST, iotlb);
427f4b20bb3SJason Gunthorpe 	if (rc)
428f4b20bb3SJason Gunthorpe 		return ERR_PTR(rc);
429f4b20bb3SJason Gunthorpe 
430f4b20bb3SJason Gunthorpe 	mock_nested = kzalloc(sizeof(*mock_nested), GFP_KERNEL);
431f4b20bb3SJason Gunthorpe 	if (!mock_nested)
432f4b20bb3SJason Gunthorpe 		return ERR_PTR(-ENOMEM);
433f4b20bb3SJason Gunthorpe 	mock_nested->domain.ops = &domain_nested_ops;
434f4b20bb3SJason Gunthorpe 	mock_nested->domain.type = IOMMU_DOMAIN_NESTED;
435f4b20bb3SJason Gunthorpe 	for (i = 0; i < MOCK_NESTED_DOMAIN_IOTLB_NUM; i++)
436f4b20bb3SJason Gunthorpe 		mock_nested->iotlb[i] = user_cfg.iotlb;
437f4b20bb3SJason Gunthorpe 	return mock_nested;
438f4b20bb3SJason Gunthorpe }
439f4b20bb3SJason Gunthorpe 
440f4b20bb3SJason Gunthorpe static struct iommu_domain *
mock_domain_alloc_nested(struct device * dev,struct iommu_domain * parent,u32 flags,const struct iommu_user_data * user_data)441f4b20bb3SJason Gunthorpe mock_domain_alloc_nested(struct device *dev, struct iommu_domain *parent,
442f4b20bb3SJason Gunthorpe 			 u32 flags, const struct iommu_user_data *user_data)
443f4b20bb3SJason Gunthorpe {
444f4b20bb3SJason Gunthorpe 	struct mock_iommu_domain_nested *mock_nested;
445f4b20bb3SJason Gunthorpe 	struct mock_iommu_domain *mock_parent;
446a9af47e3SJoao Martins 
447f4b20bb3SJason Gunthorpe 	if (flags & ~IOMMU_HWPT_ALLOC_PASID)
448f4b20bb3SJason Gunthorpe 		return ERR_PTR(-EOPNOTSUPP);
449*bb04d133SJason Gunthorpe 	if (!parent || parent->ops != mock_ops.default_domain_ops)
450*bb04d133SJason Gunthorpe 		return ERR_PTR(-EINVAL);
451*bb04d133SJason Gunthorpe 
452f4b20bb3SJason Gunthorpe 	mock_parent = to_mock_domain(parent);
453*bb04d133SJason Gunthorpe 	if (!mock_parent)
454*bb04d133SJason Gunthorpe 		return ERR_PTR(-EINVAL);
455*bb04d133SJason Gunthorpe 
456*bb04d133SJason Gunthorpe 	mock_nested = __mock_domain_alloc_nested(user_data);
457*bb04d133SJason Gunthorpe 	if (IS_ERR(mock_nested))
458f4b20bb3SJason Gunthorpe 		return ERR_CAST(mock_nested);
459*bb04d133SJason Gunthorpe 	return &mock_nested->domain;
460f4b20bb3SJason Gunthorpe }
461a9af47e3SJoao Martins 
462f4b20bb3SJason Gunthorpe static struct iommu_domain *
mock_domain_alloc_paging_flags(struct device * dev,u32 flags,const struct iommu_user_data * user_data)463f4b20bb3SJason Gunthorpe mock_domain_alloc_paging_flags(struct device *dev, u32 flags,
464f4b20bb3SJason Gunthorpe 			       const struct iommu_user_data *user_data)
465*bb04d133SJason Gunthorpe {
466*bb04d133SJason Gunthorpe 	bool has_dirty_flag = flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
467a9af47e3SJoao Martins 	const u32 PAGING_FLAGS = IOMMU_HWPT_ALLOC_DIRTY_TRACKING |
468f4b20bb3SJason Gunthorpe 				 IOMMU_HWPT_ALLOC_NEST_PARENT |
469*bb04d133SJason Gunthorpe 				 IOMMU_HWPT_ALLOC_PASID;
470f4b20bb3SJason Gunthorpe 	struct mock_dev *mdev = to_mock_dev(dev);
471f4b20bb3SJason Gunthorpe 	bool no_dirty_ops = mdev->flags & MOCK_FLAGS_DEVICE_NO_DIRTY;
472f4b20bb3SJason Gunthorpe 	struct mock_iommu_domain *mock;
473f4b20bb3SJason Gunthorpe 
474f4b20bb3SJason Gunthorpe 	if (user_data)
475f4b20bb3SJason Gunthorpe 		return ERR_PTR(-EOPNOTSUPP);
476f4b20bb3SJason Gunthorpe 	if ((flags & ~PAGING_FLAGS) || (has_dirty_flag && no_dirty_ops))
477f4b20bb3SJason Gunthorpe 		return ERR_PTR(-EOPNOTSUPP);
478f4b20bb3SJason Gunthorpe 
479f4b20bb3SJason Gunthorpe 	mock = kzalloc(sizeof(*mock), GFP_KERNEL);
480f4b20bb3SJason Gunthorpe 	if (!mock)
481f4b20bb3SJason Gunthorpe 		return ERR_PTR(-ENOMEM);
482f4b20bb3SJason Gunthorpe 	mock->domain.geometry.aperture_start = MOCK_APERTURE_START;
483f4b20bb3SJason Gunthorpe 	mock->domain.geometry.aperture_end = MOCK_APERTURE_LAST;
484f4b20bb3SJason Gunthorpe 	mock->domain.pgsize_bitmap = MOCK_IO_PAGE_SIZE;
485f4b20bb3SJason Gunthorpe 	if (dev && mdev->flags & MOCK_FLAGS_DEVICE_HUGE_IOVA)
486f4b20bb3SJason Gunthorpe 		mock->domain.pgsize_bitmap |= MOCK_HUGE_PAGE_SIZE;
487f4b20bb3SJason Gunthorpe 	mock->domain.ops = mock_ops.default_domain_ops;
488f4b20bb3SJason Gunthorpe 	mock->domain.type = IOMMU_DOMAIN_UNMANAGED;
489f4b20bb3SJason Gunthorpe 	xa_init(&mock->pfns);
490f4b20bb3SJason Gunthorpe 
49165c619aeSJason Gunthorpe 	if (has_dirty_flag)
49265c619aeSJason Gunthorpe 		mock->domain.dirty_ops = &dirty_ops;
493ae36fe70SJoao Martins 	return &mock->domain;
494ae36fe70SJoao Martins }
495ae36fe70SJoao Martins 
mock_domain_free(struct iommu_domain * domain)496ae36fe70SJoao Martins static void mock_domain_free(struct iommu_domain *domain)
497ae36fe70SJoao Martins {
498ae36fe70SJoao Martins 	struct mock_iommu_domain *mock = to_mock_domain(domain);
499ae36fe70SJoao Martins 
500ae36fe70SJoao Martins 	WARN_ON(!xa_empty(&mock->pfns));
501ae36fe70SJoao Martins 	kfree(mock);
502ae36fe70SJoao Martins }
503ae36fe70SJoao Martins 
mock_domain_map_pages(struct iommu_domain * domain,unsigned long iova,phys_addr_t paddr,size_t pgsize,size_t pgcount,int prot,gfp_t gfp,size_t * mapped)504ae36fe70SJoao Martins static int mock_domain_map_pages(struct iommu_domain *domain,
50565c619aeSJason Gunthorpe 				 unsigned long iova, phys_addr_t paddr,
50665c619aeSJason Gunthorpe 				 size_t pgsize, size_t pgcount, int prot,
50723a1b46fSJason Gunthorpe 				 gfp_t gfp, size_t *mapped)
50823a1b46fSJason Gunthorpe {
50923a1b46fSJason Gunthorpe 	struct mock_iommu_domain *mock = to_mock_domain(domain);
51023a1b46fSJason Gunthorpe 	unsigned long flags = MOCK_PFN_START_IOVA;
51123a1b46fSJason Gunthorpe 	unsigned long start_iova = iova;
51247f2bd2fSJason Gunthorpe 
51347f2bd2fSJason Gunthorpe 	/*
51423a1b46fSJason Gunthorpe 	 * xarray does not reliably work with fault injection because it does a
51523a1b46fSJason Gunthorpe 	 * retry allocation, so put our own failure point.
51623a1b46fSJason Gunthorpe 	 */
517f4b20bb3SJason Gunthorpe 	if (iommufd_should_fail())
5181c68cbc6SJason Gunthorpe 		return -ENOENT;
5191c68cbc6SJason Gunthorpe 
5201c68cbc6SJason Gunthorpe 	WARN_ON(iova % MOCK_IO_PAGE_SIZE);
5211c68cbc6SJason Gunthorpe 	WARN_ON(pgsize % MOCK_IO_PAGE_SIZE);
5221c68cbc6SJason Gunthorpe 	for (; pgcount; pgcount--) {
52313fbceb1SJason Gunthorpe 		size_t cur;
524f4b20bb3SJason Gunthorpe 
525f4b20bb3SJason Gunthorpe 		for (cur = 0; cur != pgsize; cur += MOCK_IO_PAGE_SIZE) {
526af4fde93SNicolin Chen 			void *old;
52713fbceb1SJason Gunthorpe 
52840866361SYi Liu 			if (pgcount == 1 && cur + MOCK_IO_PAGE_SIZE == pgsize)
52965c619aeSJason Gunthorpe 				flags = MOCK_PFN_LAST_IOVA;
53023a1b46fSJason Gunthorpe 			if (pgsize != MOCK_IO_PAGE_SIZE) {
53123a1b46fSJason Gunthorpe 				flags |= MOCK_PFN_HUGE_IOVA;
532f4b20bb3SJason Gunthorpe 			}
533f4b20bb3SJason Gunthorpe 			old = xa_store(&mock->pfns, iova / MOCK_IO_PAGE_SIZE,
534f4b20bb3SJason Gunthorpe 				       xa_mk_value((paddr / MOCK_IO_PAGE_SIZE) |
53565c619aeSJason Gunthorpe 						   flags),
536f4b20bb3SJason Gunthorpe 				       gfp);
537f4b20bb3SJason Gunthorpe 			if (xa_is_err(old)) {
538f4b20bb3SJason Gunthorpe 				for (; start_iova != iova;
539f4b20bb3SJason Gunthorpe 				     start_iova += MOCK_IO_PAGE_SIZE)
540f4b20bb3SJason Gunthorpe 					xa_erase(&mock->pfns,
541f4b20bb3SJason Gunthorpe 						 start_iova /
54265fe32f7SNicolin Chen 							 MOCK_IO_PAGE_SIZE);
54365fe32f7SNicolin Chen 				return xa_err(old);
54465fe32f7SNicolin Chen 			}
54565fe32f7SNicolin Chen 			WARN_ON(old);
54665fe32f7SNicolin Chen 			iova += MOCK_IO_PAGE_SIZE;
54765fe32f7SNicolin Chen 			paddr += MOCK_IO_PAGE_SIZE;
54865fe32f7SNicolin Chen 			*mapped += MOCK_IO_PAGE_SIZE;
54965fe32f7SNicolin Chen 			flags = 0;
550ac869120SNicolin Chen 		}
551ac869120SNicolin Chen 	}
552ac869120SNicolin Chen 	return 0;
553ac869120SNicolin Chen }
554ac869120SNicolin Chen 
mock_domain_unmap_pages(struct iommu_domain * domain,unsigned long iova,size_t pgsize,size_t pgcount,struct iommu_iotlb_gather * iotlb_gather)555ac869120SNicolin Chen static size_t mock_domain_unmap_pages(struct iommu_domain *domain,
556ac869120SNicolin Chen 				      unsigned long iova, size_t pgsize,
557ac869120SNicolin Chen 				      size_t pgcount,
558ac869120SNicolin Chen 				      struct iommu_iotlb_gather *iotlb_gather)
559ac869120SNicolin Chen {
560ac869120SNicolin Chen 	struct mock_iommu_domain *mock = to_mock_domain(domain);
561ac869120SNicolin Chen 	bool first = true;
562ac869120SNicolin Chen 	size_t ret = 0;
563ac869120SNicolin Chen 	void *ent;
564ac869120SNicolin Chen 
565ac869120SNicolin Chen 	WARN_ON(iova % MOCK_IO_PAGE_SIZE);
566ac869120SNicolin Chen 	WARN_ON(pgsize % MOCK_IO_PAGE_SIZE);
567ac869120SNicolin Chen 
568ac869120SNicolin Chen 	for (; pgcount; pgcount--) {
569ac869120SNicolin Chen 		size_t cur;
570ac869120SNicolin Chen 
571ac869120SNicolin Chen 		for (cur = 0; cur != pgsize; cur += MOCK_IO_PAGE_SIZE) {
572ac869120SNicolin Chen 			ent = xa_erase(&mock->pfns, iova / MOCK_IO_PAGE_SIZE);
573ac869120SNicolin Chen 
574ac869120SNicolin Chen 			/*
575ac869120SNicolin Chen 			 * iommufd generates unmaps that must be a strict
576ac869120SNicolin Chen 			 * superset of the map's performend So every
577ac869120SNicolin Chen 			 * starting/ending IOVA should have been an iova passed
578ac869120SNicolin Chen 			 * to map.
579ac869120SNicolin Chen 			 *
580ac869120SNicolin Chen 			 * This simple logic doesn't work when the HUGE_PAGE is
581ac869120SNicolin Chen 			 * turned on since the core code will automatically
582ac869120SNicolin Chen 			 * switch between the two page sizes creating a break in
583ac869120SNicolin Chen 			 * the unmap calls. The break can land in the middle of
584ac869120SNicolin Chen 			 * contiguous IOVA.
585ac869120SNicolin Chen 			 */
586ac869120SNicolin Chen 			if (!(domain->pgsize_bitmap & MOCK_HUGE_PAGE_SIZE)) {
587ac869120SNicolin Chen 				if (first) {
588ac869120SNicolin Chen 					WARN_ON(ent && !(xa_to_value(ent) &
589ac869120SNicolin Chen 							 MOCK_PFN_START_IOVA));
590ac869120SNicolin Chen 					first = false;
591ac869120SNicolin Chen 				}
592ac869120SNicolin Chen 				if (pgcount == 1 &&
593ac869120SNicolin Chen 				    cur + MOCK_IO_PAGE_SIZE == pgsize)
594ac869120SNicolin Chen 					WARN_ON(ent && !(xa_to_value(ent) &
595ac869120SNicolin Chen 							 MOCK_PFN_LAST_IOVA));
596ac869120SNicolin Chen 			}
597ac869120SNicolin Chen 
598ac869120SNicolin Chen 			iova += MOCK_IO_PAGE_SIZE;
59965fe32f7SNicolin Chen 			ret += MOCK_IO_PAGE_SIZE;
60065fe32f7SNicolin Chen 		}
60165fe32f7SNicolin Chen 	}
602ac869120SNicolin Chen 	return ret;
60365fe32f7SNicolin Chen }
60465fe32f7SNicolin Chen 
mock_domain_iova_to_phys(struct iommu_domain * domain,dma_addr_t iova)60565fe32f7SNicolin Chen static phys_addr_t mock_domain_iova_to_phys(struct iommu_domain *domain,
60665fe32f7SNicolin Chen 					    dma_addr_t iova)
60765fe32f7SNicolin Chen {
60865fe32f7SNicolin Chen 	struct mock_iommu_domain *mock = to_mock_domain(domain);
60965fe32f7SNicolin Chen 	void *ent;
61065fe32f7SNicolin Chen 
61165fe32f7SNicolin Chen 	WARN_ON(iova % MOCK_IO_PAGE_SIZE);
61265fe32f7SNicolin Chen 	ent = xa_load(&mock->pfns, iova / MOCK_IO_PAGE_SIZE);
61365fe32f7SNicolin Chen 	WARN_ON(!ent);
61465fe32f7SNicolin Chen 	return (xa_to_value(ent) & MOCK_PFN_MASK) * MOCK_IO_PAGE_SIZE;
61565fe32f7SNicolin Chen }
616f4b20bb3SJason Gunthorpe 
mock_domain_capable(struct device * dev,enum iommu_cap cap)617f4b20bb3SJason Gunthorpe static bool mock_domain_capable(struct device *dev, enum iommu_cap cap)
618f4b20bb3SJason Gunthorpe {
619f4b20bb3SJason Gunthorpe 	struct mock_dev *mdev = to_mock_dev(dev);
620f4b20bb3SJason Gunthorpe 
621f4b20bb3SJason Gunthorpe 	switch (cap) {
62265fe32f7SNicolin Chen 	case IOMMU_CAP_CACHE_COHERENCY:
62365fe32f7SNicolin Chen 		return true;
62465fe32f7SNicolin Chen 	case IOMMU_CAP_DIRTY_TRACKING:
62565fe32f7SNicolin Chen 		return !(mdev->flags & MOCK_FLAGS_DEVICE_NO_DIRTY);
62665fe32f7SNicolin Chen 	default:
627bd7a2826SJason Gunthorpe 		break;
628f4b20bb3SJason Gunthorpe 	}
629f4b20bb3SJason Gunthorpe 
630f4b20bb3SJason Gunthorpe 	return false;
631f4b20bb3SJason Gunthorpe }
632f4b20bb3SJason Gunthorpe 
633f4b20bb3SJason Gunthorpe static struct iopf_queue *mock_iommu_iopf_queue;
63465fe32f7SNicolin Chen 
63565fe32f7SNicolin Chen static struct mock_iommu_device {
63665fe32f7SNicolin Chen 	struct iommu_device iommu_dev;
63765fe32f7SNicolin Chen 	struct completion complete;
63865fe32f7SNicolin Chen 	refcount_t users;
63965fe32f7SNicolin Chen } mock_iommu;
64065fe32f7SNicolin Chen 
mock_probe_device(struct device * dev)64165fe32f7SNicolin Chen static struct iommu_device *mock_probe_device(struct device *dev)
64265fe32f7SNicolin Chen {
64365fe32f7SNicolin Chen 	if (dev->bus != &iommufd_mock_bus_type.bus)
64465fe32f7SNicolin Chen 		return ERR_PTR(-ENODEV);
645bd7a2826SJason Gunthorpe 	return &mock_iommu.iommu_dev;
64665fe32f7SNicolin Chen }
64765fe32f7SNicolin Chen 
mock_domain_page_response(struct device * dev,struct iopf_fault * evt,struct iommu_page_response * msg)64865fe32f7SNicolin Chen static void mock_domain_page_response(struct device *dev, struct iopf_fault *evt,
64965fe32f7SNicolin Chen 				      struct iommu_page_response *msg)
65065fe32f7SNicolin Chen {
65165fe32f7SNicolin Chen }
65265fe32f7SNicolin Chen 
mock_dev_enable_iopf(struct device * dev,struct iommu_domain * domain)65365c619aeSJason Gunthorpe static int mock_dev_enable_iopf(struct device *dev, struct iommu_domain *domain)
65465c619aeSJason Gunthorpe {
65565c619aeSJason Gunthorpe 	struct mock_dev *mdev = to_mock_dev(dev);
65665c619aeSJason Gunthorpe 	int ret;
657fde372dfSNicolin Chen 
65865c619aeSJason Gunthorpe 	if (!domain || !domain->iopf_handler)
65965c619aeSJason Gunthorpe 		return 0;
66065c619aeSJason Gunthorpe 
661e04b23c8SJoao Martins 	if (!mock_iommu_iopf_queue)
66265c619aeSJason Gunthorpe 		return -ENODEV;
66365c619aeSJason Gunthorpe 
66465c619aeSJason Gunthorpe 	if (mdev->iopf_refcount) {
66565c619aeSJason Gunthorpe 		mdev->iopf_refcount++;
6667db521e2SJoao Martins 		return 0;
6677db521e2SJoao Martins 	}
668266ce589SJoao Martins 
669266ce589SJoao Martins 	ret = iopf_queue_add_device(mock_iommu_iopf_queue, dev);
67065c619aeSJason Gunthorpe 	if (ret)
67165c619aeSJason Gunthorpe 		return ret;
67265c619aeSJason Gunthorpe 
67365c619aeSJason Gunthorpe 	mdev->iopf_refcount = 1;
67465c619aeSJason Gunthorpe 
675e04b23c8SJoao Martins 	return 0;
67665c619aeSJason Gunthorpe }
67723a1b46fSJason Gunthorpe 
mock_dev_disable_iopf(struct device * dev,struct iommu_domain * domain)67865c619aeSJason Gunthorpe static void mock_dev_disable_iopf(struct device *dev, struct iommu_domain *domain)
679fde372dfSNicolin Chen {
680fde372dfSNicolin Chen 	struct mock_dev *mdev = to_mock_dev(dev);
681fde372dfSNicolin Chen 
682fde372dfSNicolin Chen 	if (!domain || !domain->iopf_handler)
683fde372dfSNicolin Chen 		return;
684fde372dfSNicolin Chen 
68565c619aeSJason Gunthorpe 	if (--mdev->iopf_refcount)
68623a1b46fSJason Gunthorpe 		return;
68765c619aeSJason Gunthorpe 
68865c619aeSJason Gunthorpe 	iopf_queue_remove_device(mock_iommu_iopf_queue, dev);
68965c619aeSJason Gunthorpe }
69023a1b46fSJason Gunthorpe 
mock_viommu_destroy(struct iommufd_viommu * viommu)69165c619aeSJason Gunthorpe static void mock_viommu_destroy(struct iommufd_viommu *viommu)
69265c619aeSJason Gunthorpe {
69365c619aeSJason Gunthorpe 	struct mock_iommu_device *mock_iommu = container_of(
69465c619aeSJason Gunthorpe 		viommu->iommu_dev, struct mock_iommu_device, iommu_dev);
69565c619aeSJason Gunthorpe 	struct mock_viommu *mock_viommu = to_mock_viommu(viommu);
69665c619aeSJason Gunthorpe 
69765c619aeSJason Gunthorpe 	if (refcount_dec_and_test(&mock_iommu->users))
69865c619aeSJason Gunthorpe 		complete(&mock_iommu->complete);
69965c619aeSJason Gunthorpe 	if (mock_viommu->mmap_offset)
70023a1b46fSJason Gunthorpe 		iommufd_viommu_destroy_mmap(&mock_viommu->core,
70165c619aeSJason Gunthorpe 					    mock_viommu->mmap_offset);
70265c619aeSJason Gunthorpe 	free_page((unsigned long)mock_viommu->page);
70365c619aeSJason Gunthorpe 	mutex_destroy(&mock_viommu->queue_mutex);
70465c619aeSJason Gunthorpe 
70565c619aeSJason Gunthorpe 	/* iommufd core frees mock_viommu and viommu */
70665c619aeSJason Gunthorpe }
70765c619aeSJason Gunthorpe 
708f4b20bb3SJason Gunthorpe static struct iommu_domain *
mock_viommu_alloc_domain_nested(struct iommufd_viommu * viommu,u32 flags,const struct iommu_user_data * user_data)709f4b20bb3SJason Gunthorpe mock_viommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
710f4b20bb3SJason Gunthorpe 				const struct iommu_user_data *user_data)
711f4b20bb3SJason Gunthorpe {
71265c619aeSJason Gunthorpe 	struct mock_viommu *mock_viommu = to_mock_viommu(viommu);
713f4b20bb3SJason Gunthorpe 	struct mock_iommu_domain_nested *mock_nested;
71465c619aeSJason Gunthorpe 
715e04b23c8SJoao Martins 	if (flags & ~IOMMU_HWPT_ALLOC_PASID)
71665c619aeSJason Gunthorpe 		return ERR_PTR(-EOPNOTSUPP);
717f4b20bb3SJason Gunthorpe 
718f4b20bb3SJason Gunthorpe 	mock_nested = __mock_domain_alloc_nested(user_data);
719f4b20bb3SJason Gunthorpe 	if (IS_ERR(mock_nested))
72065c619aeSJason Gunthorpe 		return ERR_CAST(mock_nested);
72165c619aeSJason Gunthorpe 	mock_nested->mock_viommu = mock_viommu;
72265c619aeSJason Gunthorpe 	return &mock_nested->domain;
723f4b20bb3SJason Gunthorpe }
724f4b20bb3SJason Gunthorpe 
mock_viommu_cache_invalidate(struct iommufd_viommu * viommu,struct iommu_user_data_array * array)725f4b20bb3SJason Gunthorpe static int mock_viommu_cache_invalidate(struct iommufd_viommu *viommu,
726e04b23c8SJoao Martins 					struct iommu_user_data_array *array)
727e04b23c8SJoao Martins {
728e04b23c8SJoao Martins 	struct iommu_viommu_invalidate_selftest *cmds;
729e04b23c8SJoao Martins 	struct iommu_viommu_invalidate_selftest *cur;
73065c619aeSJason Gunthorpe 	struct iommu_viommu_invalidate_selftest *end;
73165c619aeSJason Gunthorpe 	int rc;
732f4b20bb3SJason Gunthorpe 
733f4b20bb3SJason Gunthorpe 	/* A zero-length array is allowed to validate the array type */
734f4b20bb3SJason Gunthorpe 	if (array->entry_num == 0 &&
73565c619aeSJason Gunthorpe 	    array->type == IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST) {
73665c619aeSJason Gunthorpe 		array->entry_num = 0;
73765c619aeSJason Gunthorpe 		return 0;
73865c619aeSJason Gunthorpe 	}
73965c619aeSJason Gunthorpe 
74065c619aeSJason Gunthorpe 	cmds = kcalloc(array->entry_num, sizeof(*cmds), GFP_KERNEL);
74165c619aeSJason Gunthorpe 	if (!cmds)
74265c619aeSJason Gunthorpe 		return -ENOMEM;
74365c619aeSJason Gunthorpe 	cur = cmds;
74465c619aeSJason Gunthorpe 	end = cmds + array->entry_num;
74565c619aeSJason Gunthorpe 
74665c619aeSJason Gunthorpe 	static_assert(sizeof(*cmds) == 3 * sizeof(u32));
74765c619aeSJason Gunthorpe 	rc = iommu_copy_struct_from_full_user_array(
74865c619aeSJason Gunthorpe 		cmds, sizeof(*cmds), array,
7492cfdeaa0SJason Gunthorpe 		IOMMU_VIOMMU_INVALIDATE_DATA_SELFTEST);
7507a467e02SJason Gunthorpe 	if (rc)
75123a1b46fSJason Gunthorpe 		goto out;
75223a1b46fSJason Gunthorpe 
75323a1b46fSJason Gunthorpe 	while (cur != end) {
754f4b20bb3SJason Gunthorpe 		struct mock_dev *mdev;
75523a1b46fSJason Gunthorpe 		struct device *dev;
756f4b20bb3SJason Gunthorpe 		int i;
75723a1b46fSJason Gunthorpe 
75823a1b46fSJason Gunthorpe 		if (cur->flags & ~IOMMU_TEST_INVALIDATE_FLAG_ALL) {
75965c619aeSJason Gunthorpe 			rc = -EOPNOTSUPP;
76065c619aeSJason Gunthorpe 			goto out;
76165c619aeSJason Gunthorpe 		}
76265c619aeSJason Gunthorpe 
763f4b20bb3SJason Gunthorpe 		if (cur->cache_id > MOCK_DEV_CACHE_ID_MAX) {
764f4b20bb3SJason Gunthorpe 			rc = -EINVAL;
765f4b20bb3SJason Gunthorpe 			goto out;
766f4b20bb3SJason Gunthorpe 		}
767f4b20bb3SJason Gunthorpe 
768fa1ffdb9SNicolin Chen 		xa_lock(&viommu->vdevs);
769fa1ffdb9SNicolin Chen 		dev = iommufd_viommu_find_dev(viommu,
770fa1ffdb9SNicolin Chen 					      (unsigned long)cur->vdev_id);
771fa1ffdb9SNicolin Chen 		if (!dev) {
772fa1ffdb9SNicolin Chen 			xa_unlock(&viommu->vdevs);
773fa1ffdb9SNicolin Chen 			rc = -EINVAL;
774fa1ffdb9SNicolin Chen 			goto out;
775fa1ffdb9SNicolin Chen 		}
776fa1ffdb9SNicolin Chen 		mdev = container_of(dev, struct mock_dev, dev);
777fa1ffdb9SNicolin Chen 
778fa1ffdb9SNicolin Chen 		if (cur->flags & IOMMU_TEST_INVALIDATE_FLAG_ALL) {
779fa1ffdb9SNicolin Chen 			/* Invalidate all cache entries and ignore cache_id */
780fa1ffdb9SNicolin Chen 			for (i = 0; i < MOCK_DEV_CACHE_NUM; i++)
781fa1ffdb9SNicolin Chen 				mdev->cache[i] = 0;
782fa1ffdb9SNicolin Chen 		} else {
783fa1ffdb9SNicolin Chen 			mdev->cache[cur->cache_id] = 0;
784fa1ffdb9SNicolin Chen 		}
785fa1ffdb9SNicolin Chen 		xa_unlock(&viommu->vdevs);
786fa1ffdb9SNicolin Chen 
787fa1ffdb9SNicolin Chen 		cur++;
788fa1ffdb9SNicolin Chen 	}
789fa1ffdb9SNicolin Chen out:
790fa1ffdb9SNicolin Chen 	array->entry_num = cur - cmds;
791fa1ffdb9SNicolin Chen 	kfree(cmds);
792fa1ffdb9SNicolin Chen 	return rc;
793fa1ffdb9SNicolin Chen }
794fa1ffdb9SNicolin Chen 
mock_viommu_get_hw_queue_size(struct iommufd_viommu * viommu,enum iommu_hw_queue_type queue_type)795fa1ffdb9SNicolin Chen static size_t mock_viommu_get_hw_queue_size(struct iommufd_viommu *viommu,
796fa1ffdb9SNicolin Chen 					    enum iommu_hw_queue_type queue_type)
797fa1ffdb9SNicolin Chen {
798fa1ffdb9SNicolin Chen 	if (queue_type != IOMMU_HW_QUEUE_TYPE_SELFTEST)
799fa1ffdb9SNicolin Chen 		return 0;
800bd7a2826SJason Gunthorpe 	return HW_QUEUE_STRUCT_SIZE(struct mock_hw_queue, core);
801fa1ffdb9SNicolin Chen }
802fa1ffdb9SNicolin Chen 
mock_hw_queue_destroy(struct iommufd_hw_queue * hw_queue)803fa1ffdb9SNicolin Chen static void mock_hw_queue_destroy(struct iommufd_hw_queue *hw_queue)
804f4b20bb3SJason Gunthorpe {
805f4b20bb3SJason Gunthorpe 	struct mock_hw_queue *mock_hw_queue = to_mock_hw_queue(hw_queue);
806f4b20bb3SJason Gunthorpe 	struct mock_viommu *mock_viommu = mock_hw_queue->mock_viommu;
807f4b20bb3SJason Gunthorpe 
808f4b20bb3SJason Gunthorpe 	mutex_lock(&mock_viommu->queue_mutex);
809f4b20bb3SJason Gunthorpe 	mock_viommu->hw_queue[mock_hw_queue->index] = NULL;
810f4b20bb3SJason Gunthorpe 	if (mock_hw_queue->prev)
811f4b20bb3SJason Gunthorpe 		iommufd_hw_queue_undepend(mock_hw_queue, mock_hw_queue->prev,
812325de950SYi Liu 					  core);
813f4b20bb3SJason Gunthorpe 	mutex_unlock(&mock_viommu->queue_mutex);
814f4b20bb3SJason Gunthorpe }
815f4b20bb3SJason Gunthorpe 
816f4b20bb3SJason Gunthorpe /* Test iommufd_hw_queue_depend/undepend() */
mock_hw_queue_init_phys(struct iommufd_hw_queue * hw_queue,u32 index,phys_addr_t base_addr_pa)817f4b20bb3SJason Gunthorpe static int mock_hw_queue_init_phys(struct iommufd_hw_queue *hw_queue, u32 index,
818bd7a2826SJason Gunthorpe 				   phys_addr_t base_addr_pa)
819f4b20bb3SJason Gunthorpe {
820f4b20bb3SJason Gunthorpe 	struct mock_viommu *mock_viommu = to_mock_viommu(hw_queue->viommu);
821f4b20bb3SJason Gunthorpe 	struct mock_hw_queue *mock_hw_queue = to_mock_hw_queue(hw_queue);
822f4b20bb3SJason Gunthorpe 	struct mock_hw_queue *prev = NULL;
823f4b20bb3SJason Gunthorpe 	int rc = 0;
824f4b20bb3SJason Gunthorpe 
825f4b20bb3SJason Gunthorpe 	if (index >= IOMMU_TEST_HW_QUEUE_MAX)
826f4b20bb3SJason Gunthorpe 		return -EINVAL;
827f4b20bb3SJason Gunthorpe 
828f4b20bb3SJason Gunthorpe 	mutex_lock(&mock_viommu->queue_mutex);
829fd8c1a4aSJason Gunthorpe 
830f4b20bb3SJason Gunthorpe 	if (mock_viommu->hw_queue[index]) {
831f4b20bb3SJason Gunthorpe 		rc = -EEXIST;
832f4b20bb3SJason Gunthorpe 		goto unlock;
833fd8c1a4aSJason Gunthorpe 	}
834fd8c1a4aSJason Gunthorpe 
835f4b20bb3SJason Gunthorpe 	if (index) {
836f4b20bb3SJason Gunthorpe 		prev = mock_viommu->hw_queue[index - 1];
837f4b20bb3SJason Gunthorpe 		if (!prev) {
838f4b20bb3SJason Gunthorpe 			rc = -EIO;
839f4b20bb3SJason Gunthorpe 			goto unlock;
840f4b20bb3SJason Gunthorpe 		}
841f4b20bb3SJason Gunthorpe 	}
842f4b20bb3SJason Gunthorpe 
843f4b20bb3SJason Gunthorpe 	/*
844f4b20bb3SJason Gunthorpe 	 * Test to catch a kernel bug if the core converted the physical address
845f4b20bb3SJason Gunthorpe 	 * incorrectly. Let mock_domain_iova_to_phys() WARN_ON if it fails.
846f4b20bb3SJason Gunthorpe 	 */
847f4b20bb3SJason Gunthorpe 	if (base_addr_pa != iommu_iova_to_phys(&mock_viommu->s2_parent->domain,
848f4b20bb3SJason Gunthorpe 					       hw_queue->base_addr)) {
849f4b20bb3SJason Gunthorpe 		rc = -EFAULT;
850f4b20bb3SJason Gunthorpe 		goto unlock;
851f4b20bb3SJason Gunthorpe 	}
852f4b20bb3SJason Gunthorpe 
853f4b20bb3SJason Gunthorpe 	if (prev) {
854f4b20bb3SJason Gunthorpe 		rc = iommufd_hw_queue_depend(mock_hw_queue, prev, core);
855f4b20bb3SJason Gunthorpe 		if (rc)
856f4b20bb3SJason Gunthorpe 			goto unlock;
857f4b20bb3SJason Gunthorpe 	}
858f4b20bb3SJason Gunthorpe 
859f4b20bb3SJason Gunthorpe 	mock_hw_queue->prev = prev;
860f4b20bb3SJason Gunthorpe 	mock_hw_queue->mock_viommu = mock_viommu;
861f4b20bb3SJason Gunthorpe 	mock_viommu->hw_queue[index] = mock_hw_queue;
862f4b20bb3SJason Gunthorpe 
863f4b20bb3SJason Gunthorpe 	hw_queue->destroy = &mock_hw_queue_destroy;
864f4b20bb3SJason Gunthorpe unlock:
865f4b20bb3SJason Gunthorpe 	mutex_unlock(&mock_viommu->queue_mutex);
866f4b20bb3SJason Gunthorpe 	return rc;
867f4b20bb3SJason Gunthorpe }
868f4b20bb3SJason Gunthorpe 
869f4b20bb3SJason Gunthorpe static struct iommufd_viommu_ops mock_viommu_ops = {
870f4b20bb3SJason Gunthorpe 	.destroy = mock_viommu_destroy,
871f4b20bb3SJason Gunthorpe 	.alloc_domain_nested = mock_viommu_alloc_domain_nested,
872f4b20bb3SJason Gunthorpe 	.cache_invalidate = mock_viommu_cache_invalidate,
873bd7a2826SJason Gunthorpe 	.get_hw_queue_size = mock_viommu_get_hw_queue_size,
874f4b20bb3SJason Gunthorpe 	.hw_queue_init_phys = mock_hw_queue_init_phys,
875f4b20bb3SJason Gunthorpe };
876f4b20bb3SJason Gunthorpe 
mock_get_viommu_size(struct device * dev,enum iommu_viommu_type viommu_type)877f4b20bb3SJason Gunthorpe static size_t mock_get_viommu_size(struct device *dev,
878f4b20bb3SJason Gunthorpe 				   enum iommu_viommu_type viommu_type)
879f4b20bb3SJason Gunthorpe {
880f4b20bb3SJason Gunthorpe 	if (viommu_type != IOMMU_VIOMMU_TYPE_SELFTEST)
881f4b20bb3SJason Gunthorpe 		return 0;
882fd8c1a4aSJason Gunthorpe 	return VIOMMU_STRUCT_SIZE(struct mock_viommu, core);
883fd8c1a4aSJason Gunthorpe }
884fd8c1a4aSJason Gunthorpe 
mock_viommu_init(struct iommufd_viommu * viommu,struct iommu_domain * parent_domain,const struct iommu_user_data * user_data)885fd8c1a4aSJason Gunthorpe static int mock_viommu_init(struct iommufd_viommu *viommu,
886f4b20bb3SJason Gunthorpe 			    struct iommu_domain *parent_domain,
887f4b20bb3SJason Gunthorpe 			    const struct iommu_user_data *user_data)
888f4b20bb3SJason Gunthorpe {
889f4b20bb3SJason Gunthorpe 	struct mock_iommu_device *mock_iommu = container_of(
890f4b20bb3SJason Gunthorpe 		viommu->iommu_dev, struct mock_iommu_device, iommu_dev);
891f4b20bb3SJason Gunthorpe 	struct mock_viommu *mock_viommu = to_mock_viommu(viommu);
892f4b20bb3SJason Gunthorpe 	struct iommu_viommu_selftest data;
893f4b20bb3SJason Gunthorpe 	int rc;
894f4b20bb3SJason Gunthorpe 
895f4b20bb3SJason Gunthorpe 	if (user_data) {
896f4b20bb3SJason Gunthorpe 		rc = iommu_copy_struct_from_user(
897f4b20bb3SJason Gunthorpe 			&data, user_data, IOMMU_VIOMMU_TYPE_SELFTEST, out_data);
898f4b20bb3SJason Gunthorpe 		if (rc)
899f4b20bb3SJason Gunthorpe 			return rc;
900f4b20bb3SJason Gunthorpe 
901f4b20bb3SJason Gunthorpe 		/* Allocate two pages */
902f4b20bb3SJason Gunthorpe 		mock_viommu->page =
903f4b20bb3SJason Gunthorpe 			(u32 *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
904f4b20bb3SJason Gunthorpe 		if (!mock_viommu->page)
905f4b20bb3SJason Gunthorpe 			return -ENOMEM;
906f4b20bb3SJason Gunthorpe 
907f4b20bb3SJason Gunthorpe 		rc = iommufd_viommu_alloc_mmap(&mock_viommu->core,
908f4b20bb3SJason Gunthorpe 					       __pa(mock_viommu->page),
909f4b20bb3SJason Gunthorpe 					       PAGE_SIZE * 2,
910f4b20bb3SJason Gunthorpe 					       &mock_viommu->mmap_offset);
911f4b20bb3SJason Gunthorpe 		if (rc)
912e1fa6640SNicolin Chen 			goto err_free_page;
913e1fa6640SNicolin Chen 
914e1fa6640SNicolin Chen 		/* For loopback tests on both the page and out_data */
915e1fa6640SNicolin Chen 		*mock_viommu->page = data.in_data;
916e1fa6640SNicolin Chen 		data.out_data = data.in_data;
917e1fa6640SNicolin Chen 		data.out_mmap_length = PAGE_SIZE * 2;
918e1fa6640SNicolin Chen 		data.out_mmap_offset = mock_viommu->mmap_offset;
919e1fa6640SNicolin Chen 		rc = iommu_copy_struct_to_user(
920e1fa6640SNicolin Chen 			user_data, &data, IOMMU_VIOMMU_TYPE_SELFTEST, out_data);
921e1fa6640SNicolin Chen 		if (rc)
922e1fa6640SNicolin Chen 			goto err_destroy_mmap;
923e1fa6640SNicolin Chen 	}
924e1fa6640SNicolin Chen 
925e1fa6640SNicolin Chen 	refcount_inc(&mock_iommu->users);
926e1fa6640SNicolin Chen 	mutex_init(&mock_viommu->queue_mutex);
927e1fa6640SNicolin Chen 	mock_viommu->s2_parent = to_mock_domain(parent_domain);
928e1fa6640SNicolin Chen 
929e1fa6640SNicolin Chen 	viommu->ops = &mock_viommu_ops;
930e1fa6640SNicolin Chen 	return 0;
931e1fa6640SNicolin Chen 
932e1fa6640SNicolin Chen err_destroy_mmap:
933e1fa6640SNicolin Chen 	iommufd_viommu_destroy_mmap(&mock_viommu->core,
934f4b20bb3SJason Gunthorpe 				    mock_viommu->mmap_offset);
935f4b20bb3SJason Gunthorpe err_free_page:
936f4b20bb3SJason Gunthorpe 	free_page((unsigned long)mock_viommu->page);
937f4b20bb3SJason Gunthorpe 	return rc;
938f4b20bb3SJason Gunthorpe }
939f4b20bb3SJason Gunthorpe 
940f4b20bb3SJason Gunthorpe static const struct iommu_ops mock_ops = {
941f4b20bb3SJason Gunthorpe 	/*
942f4b20bb3SJason Gunthorpe 	 * IOMMU_DOMAIN_BLOCKED cannot be returned from def_domain_type()
943f4b20bb3SJason Gunthorpe 	 * because it is zero.
944f4b20bb3SJason Gunthorpe 	 */
945f4b20bb3SJason Gunthorpe 	.default_domain = &mock_blocking_domain,
946f4b20bb3SJason Gunthorpe 	.blocked_domain = &mock_blocking_domain,
947f4b20bb3SJason Gunthorpe 	.owner = THIS_MODULE,
948f4b20bb3SJason Gunthorpe 	.hw_info = mock_domain_hw_info,
949f4b20bb3SJason Gunthorpe 	.domain_alloc_paging_flags = mock_domain_alloc_paging_flags,
950f4b20bb3SJason Gunthorpe 	.domain_alloc_nested = mock_domain_alloc_nested,
951f4b20bb3SJason Gunthorpe 	.capable = mock_domain_capable,
952f4b20bb3SJason Gunthorpe 	.device_group = generic_device_group,
953f4b20bb3SJason Gunthorpe 	.probe_device = mock_probe_device,
954f4b20bb3SJason Gunthorpe 	.page_response = mock_domain_page_response,
955f4b20bb3SJason Gunthorpe 	.user_pasid_table = true,
956f4b20bb3SJason Gunthorpe 	.get_viommu_size = mock_get_viommu_size,
957f4b20bb3SJason Gunthorpe 	.viommu_init = mock_viommu_init,
958f4b20bb3SJason Gunthorpe 	.default_domain_ops =
959f4b20bb3SJason Gunthorpe 		&(struct iommu_domain_ops){
960f4b20bb3SJason Gunthorpe 			.free = mock_domain_free,
961f4b20bb3SJason Gunthorpe 			.attach_dev = mock_domain_nop_attach,
962f4b20bb3SJason Gunthorpe 			.map_pages = mock_domain_map_pages,
963f4b20bb3SJason Gunthorpe 			.unmap_pages = mock_domain_unmap_pages,
964f4b20bb3SJason Gunthorpe 			.iova_to_phys = mock_domain_iova_to_phys,
965f4b20bb3SJason Gunthorpe 			.set_dev_pasid = mock_domain_set_dev_pasid_nop,
966f4b20bb3SJason Gunthorpe 		},
967f4b20bb3SJason Gunthorpe };
968f4b20bb3SJason Gunthorpe 
mock_domain_free_nested(struct iommu_domain * domain)969f4b20bb3SJason Gunthorpe static void mock_domain_free_nested(struct iommu_domain *domain)
970f4b20bb3SJason Gunthorpe {
971f4b20bb3SJason Gunthorpe 	kfree(to_mock_nested(domain));
972f4b20bb3SJason Gunthorpe }
973f4b20bb3SJason Gunthorpe 
974f4b20bb3SJason Gunthorpe static int
mock_domain_cache_invalidate_user(struct iommu_domain * domain,struct iommu_user_data_array * array)975f4b20bb3SJason Gunthorpe mock_domain_cache_invalidate_user(struct iommu_domain *domain,
976f4b20bb3SJason Gunthorpe 				  struct iommu_user_data_array *array)
977f4b20bb3SJason Gunthorpe {
978f4b20bb3SJason Gunthorpe 	struct mock_iommu_domain_nested *mock_nested = to_mock_nested(domain);
979f4b20bb3SJason Gunthorpe 	struct iommu_hwpt_invalidate_selftest inv;
980f4b20bb3SJason Gunthorpe 	u32 processed = 0;
981f4b20bb3SJason Gunthorpe 	int i = 0, j;
982f4b20bb3SJason Gunthorpe 	int rc = 0;
983f4b20bb3SJason Gunthorpe 
984f4b20bb3SJason Gunthorpe 	if (array->type != IOMMU_HWPT_INVALIDATE_DATA_SELFTEST) {
985f4b20bb3SJason Gunthorpe 		rc = -EINVAL;
986f4b20bb3SJason Gunthorpe 		goto out;
987f4b20bb3SJason Gunthorpe 	}
988f4b20bb3SJason Gunthorpe 
989f4b20bb3SJason Gunthorpe 	for ( ; i < array->entry_num; i++) {
990f4b20bb3SJason Gunthorpe 		rc = iommu_copy_struct_from_user_array(&inv, array,
991f4b20bb3SJason Gunthorpe 						       IOMMU_HWPT_INVALIDATE_DATA_SELFTEST,
992f4b20bb3SJason Gunthorpe 						       i, iotlb_id);
993f4b20bb3SJason Gunthorpe 		if (rc)
994f4b20bb3SJason Gunthorpe 			break;
995f4b20bb3SJason Gunthorpe 
996f4b20bb3SJason Gunthorpe 		if (inv.flags & ~IOMMU_TEST_INVALIDATE_FLAG_ALL) {
997f4b20bb3SJason Gunthorpe 			rc = -EOPNOTSUPP;
998f4b20bb3SJason Gunthorpe 			break;
999f4b20bb3SJason Gunthorpe 		}
1000f4b20bb3SJason Gunthorpe 
1001f4b20bb3SJason Gunthorpe 		if (inv.iotlb_id > MOCK_NESTED_DOMAIN_IOTLB_ID_MAX) {
1002f4b20bb3SJason Gunthorpe 			rc = -EINVAL;
1003f4b20bb3SJason Gunthorpe 			break;
1004f4b20bb3SJason Gunthorpe 		}
1005f4b20bb3SJason Gunthorpe 
1006f4b20bb3SJason Gunthorpe 		if (inv.flags & IOMMU_TEST_INVALIDATE_FLAG_ALL) {
1007f4b20bb3SJason Gunthorpe 			/* Invalidate all mock iotlb entries and ignore iotlb_id */
1008f4b20bb3SJason Gunthorpe 			for (j = 0; j < MOCK_NESTED_DOMAIN_IOTLB_NUM; j++)
1009f4b20bb3SJason Gunthorpe 				mock_nested->iotlb[j] = 0;
1010f4b20bb3SJason Gunthorpe 		} else {
1011f4b20bb3SJason Gunthorpe 			mock_nested->iotlb[inv.iotlb_id] = 0;
1012f4b20bb3SJason Gunthorpe 		}
1013f4b20bb3SJason Gunthorpe 
1014f4b20bb3SJason Gunthorpe 		processed++;
1015f4b20bb3SJason Gunthorpe 	}
1016f4b20bb3SJason Gunthorpe 
1017f4b20bb3SJason Gunthorpe out:
1018f4b20bb3SJason Gunthorpe 	array->entry_num = processed;
1019f4b20bb3SJason Gunthorpe 	return rc;
1020f4b20bb3SJason Gunthorpe }
1021f4b20bb3SJason Gunthorpe 
1022f4b20bb3SJason Gunthorpe static struct iommu_domain_ops domain_nested_ops = {
1023f4b20bb3SJason Gunthorpe 	.free = mock_domain_free_nested,
1024f4b20bb3SJason Gunthorpe 	.attach_dev = mock_domain_nop_attach,
1025f4b20bb3SJason Gunthorpe 	.cache_invalidate_user = mock_domain_cache_invalidate_user,
1026f4b20bb3SJason Gunthorpe 	.set_dev_pasid = mock_domain_set_dev_pasid_nop,
1027f4b20bb3SJason Gunthorpe };
1028f4b20bb3SJason Gunthorpe 
1029f4b20bb3SJason Gunthorpe static inline struct iommufd_hw_pagetable *
__get_md_pagetable(struct iommufd_ucmd * ucmd,u32 mockpt_id,u32 hwpt_type)1030f4b20bb3SJason Gunthorpe __get_md_pagetable(struct iommufd_ucmd *ucmd, u32 mockpt_id, u32 hwpt_type)
1031f4b20bb3SJason Gunthorpe {
1032f4b20bb3SJason Gunthorpe 	struct iommufd_object *obj;
1033f4b20bb3SJason Gunthorpe 
1034f4b20bb3SJason Gunthorpe 	obj = iommufd_get_object(ucmd->ictx, mockpt_id, hwpt_type);
1035f4b20bb3SJason Gunthorpe 	if (IS_ERR(obj))
1036f4b20bb3SJason Gunthorpe 		return ERR_CAST(obj);
1037f4b20bb3SJason Gunthorpe 	return container_of(obj, struct iommufd_hw_pagetable, obj);
1038f4b20bb3SJason Gunthorpe }
1039f4b20bb3SJason Gunthorpe 
1040f4b20bb3SJason Gunthorpe static inline struct iommufd_hw_pagetable *
get_md_pagetable(struct iommufd_ucmd * ucmd,u32 mockpt_id,struct mock_iommu_domain ** mock)1041f4b20bb3SJason Gunthorpe get_md_pagetable(struct iommufd_ucmd *ucmd, u32 mockpt_id,
1042f4b20bb3SJason Gunthorpe 		 struct mock_iommu_domain **mock)
1043f4b20bb3SJason Gunthorpe {
1044f4b20bb3SJason Gunthorpe 	struct iommufd_hw_pagetable *hwpt;
1045f4b20bb3SJason Gunthorpe 
1046f4b20bb3SJason Gunthorpe 	hwpt = __get_md_pagetable(ucmd, mockpt_id, IOMMUFD_OBJ_HWPT_PAGING);
1047f4b20bb3SJason Gunthorpe 	if (IS_ERR(hwpt))
1048f4b20bb3SJason Gunthorpe 		return hwpt;
1049f4b20bb3SJason Gunthorpe 	if (hwpt->domain->type != IOMMU_DOMAIN_UNMANAGED ||
1050f4b20bb3SJason Gunthorpe 	    hwpt->domain->ops != mock_ops.default_domain_ops) {
1051f4b20bb3SJason Gunthorpe 		iommufd_put_object(ucmd->ictx, &hwpt->obj);
1052f4b20bb3SJason Gunthorpe 		return ERR_PTR(-EINVAL);
1053f4b20bb3SJason Gunthorpe 	}
1054f4b20bb3SJason Gunthorpe 	*mock = to_mock_domain(hwpt->domain);
1055f4b20bb3SJason Gunthorpe 	return hwpt;
1056f4b20bb3SJason Gunthorpe }
1057f4b20bb3SJason Gunthorpe 
1058f4b20bb3SJason Gunthorpe static inline struct iommufd_hw_pagetable *
get_md_pagetable_nested(struct iommufd_ucmd * ucmd,u32 mockpt_id,struct mock_iommu_domain_nested ** mock_nested)1059f4b20bb3SJason Gunthorpe get_md_pagetable_nested(struct iommufd_ucmd *ucmd, u32 mockpt_id,
1060f4b20bb3SJason Gunthorpe 			struct mock_iommu_domain_nested **mock_nested)
1061f4b20bb3SJason Gunthorpe {
1062f4b20bb3SJason Gunthorpe 	struct iommufd_hw_pagetable *hwpt;
1063f4b20bb3SJason Gunthorpe 
1064f4b20bb3SJason Gunthorpe 	hwpt = __get_md_pagetable(ucmd, mockpt_id, IOMMUFD_OBJ_HWPT_NESTED);
1065f4b20bb3SJason Gunthorpe 	if (IS_ERR(hwpt))
1066f4b20bb3SJason Gunthorpe 		return hwpt;
1067f4b20bb3SJason Gunthorpe 	if (hwpt->domain->type != IOMMU_DOMAIN_NESTED ||
1068f4b20bb3SJason Gunthorpe 	    hwpt->domain->ops != &domain_nested_ops) {
1069f4b20bb3SJason Gunthorpe 		iommufd_put_object(ucmd->ictx, &hwpt->obj);
1070f4b20bb3SJason Gunthorpe 		return ERR_PTR(-EINVAL);
1071632fda7fSYi Liu 	}
1072f4b20bb3SJason Gunthorpe 	*mock_nested = to_mock_nested(hwpt->domain);
1073f4b20bb3SJason Gunthorpe 	return hwpt;
1074f4b20bb3SJason Gunthorpe }
1075f4b20bb3SJason Gunthorpe 
mock_dev_release(struct device * dev)1076f4b20bb3SJason Gunthorpe static void mock_dev_release(struct device *dev)
1077f4b20bb3SJason Gunthorpe {
1078f4b20bb3SJason Gunthorpe 	struct mock_dev *mdev = to_mock_dev(dev);
1079f4b20bb3SJason Gunthorpe 
1080f4b20bb3SJason Gunthorpe 	ida_free(&mock_dev_ida, mdev->id);
1081f4b20bb3SJason Gunthorpe 	kfree(mdev);
1082f4b20bb3SJason Gunthorpe }
1083f4b20bb3SJason Gunthorpe 
mock_dev_create(unsigned long dev_flags)1084f4b20bb3SJason Gunthorpe static struct mock_dev *mock_dev_create(unsigned long dev_flags)
1085f4b20bb3SJason Gunthorpe {
1086f4b20bb3SJason Gunthorpe 	struct property_entry prop[] = {
1087f4b20bb3SJason Gunthorpe 		PROPERTY_ENTRY_U32("pasid-num-bits", 0),
1088f4b20bb3SJason Gunthorpe 		{},
108954b47585SNicolin Chen 	};
1090f4b20bb3SJason Gunthorpe 	const u32 valid_flags = MOCK_FLAGS_DEVICE_NO_DIRTY |
1091f4b20bb3SJason Gunthorpe 				MOCK_FLAGS_DEVICE_HUGE_IOVA |
1092f4b20bb3SJason Gunthorpe 				MOCK_FLAGS_DEVICE_PASID;
1093632fda7fSYi Liu 	struct mock_dev *mdev;
1094f4b20bb3SJason Gunthorpe 	int rc, i;
1095f4b20bb3SJason Gunthorpe 
1096f4b20bb3SJason Gunthorpe 	if (dev_flags & ~valid_flags)
1097f4b20bb3SJason Gunthorpe 		return ERR_PTR(-EINVAL);
109854b47585SNicolin Chen 
109954b47585SNicolin Chen 	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
110054b47585SNicolin Chen 	if (!mdev)
1101f4b20bb3SJason Gunthorpe 		return ERR_PTR(-ENOMEM);
1102f4b20bb3SJason Gunthorpe 
1103f4b20bb3SJason Gunthorpe 	init_rwsem(&mdev->viommu_rwsem);
1104f4b20bb3SJason Gunthorpe 	device_initialize(&mdev->dev);
1105f4b20bb3SJason Gunthorpe 	mdev->flags = dev_flags;
1106f4b20bb3SJason Gunthorpe 	mdev->dev.release = mock_dev_release;
1107f4b20bb3SJason Gunthorpe 	mdev->dev.bus = &iommufd_mock_bus_type.bus;
1108f4b20bb3SJason Gunthorpe 	for (i = 0; i < MOCK_DEV_CACHE_NUM; i++)
1109f4b20bb3SJason Gunthorpe 		mdev->cache[i] = IOMMU_TEST_DEV_CACHE_DEFAULT;
1110f4b20bb3SJason Gunthorpe 
1111f4b20bb3SJason Gunthorpe 	rc = ida_alloc(&mock_dev_ida, GFP_KERNEL);
1112f4b20bb3SJason Gunthorpe 	if (rc < 0)
1113f4b20bb3SJason Gunthorpe 		goto err_put;
1114f4b20bb3SJason Gunthorpe 	mdev->id = rc;
1115f4b20bb3SJason Gunthorpe 
1116f4b20bb3SJason Gunthorpe 	rc = dev_set_name(&mdev->dev, "iommufd_mock%u", mdev->id);
1117f4b20bb3SJason Gunthorpe 	if (rc)
1118f4b20bb3SJason Gunthorpe 		goto err_put;
1119c154660bSNicolin Chen 
1120c154660bSNicolin Chen 	if (dev_flags & MOCK_FLAGS_DEVICE_PASID)
1121c154660bSNicolin Chen 		prop[0] = PROPERTY_ENTRY_U32("pasid-num-bits", MOCK_PASID_WIDTH);
1122c154660bSNicolin Chen 
1123c154660bSNicolin Chen 	rc = device_create_managed_software_node(&mdev->dev, prop, NULL);
1124c154660bSNicolin Chen 	if (rc) {
1125c154660bSNicolin Chen 		dev_err(&mdev->dev, "add pasid-num-bits property failed, rc: %d", rc);
1126c154660bSNicolin Chen 		goto err_put;
1127c154660bSNicolin Chen 	}
1128c154660bSNicolin Chen 
1129c154660bSNicolin Chen 	rc = iommu_mock_device_add(&mdev->dev, &mock_iommu.iommu_dev);
1130c154660bSNicolin Chen 	if (rc)
1131c154660bSNicolin Chen 		goto err_put;
1132c154660bSNicolin Chen 	return mdev;
1133c154660bSNicolin Chen 
1134c154660bSNicolin Chen err_put:
1135f4b20bb3SJason Gunthorpe 	put_device(&mdev->dev);
1136f4b20bb3SJason Gunthorpe 	return ERR_PTR(rc);
1137f4b20bb3SJason Gunthorpe }
1138f4b20bb3SJason Gunthorpe 
mock_dev_destroy(struct mock_dev * mdev)1139f4b20bb3SJason Gunthorpe static void mock_dev_destroy(struct mock_dev *mdev)
1140f4b20bb3SJason Gunthorpe {
1141f4b20bb3SJason Gunthorpe 	device_unregister(&mdev->dev);
1142f4b20bb3SJason Gunthorpe }
1143f4b20bb3SJason Gunthorpe 
iommufd_selftest_is_mock_dev(struct device * dev)1144f4b20bb3SJason Gunthorpe bool iommufd_selftest_is_mock_dev(struct device *dev)
1145f4b20bb3SJason Gunthorpe {
1146f4b20bb3SJason Gunthorpe 	return dev->release == mock_dev_release;
1147f4b20bb3SJason Gunthorpe }
1148f4b20bb3SJason Gunthorpe 
1149f4b20bb3SJason Gunthorpe /* Create an hw_pagetable with the mock domain so we can test the domain ops */
iommufd_test_mock_domain(struct iommufd_ucmd * ucmd,struct iommu_test_cmd * cmd)1150f4b20bb3SJason Gunthorpe static int iommufd_test_mock_domain(struct iommufd_ucmd *ucmd,
1151f4b20bb3SJason Gunthorpe 				    struct iommu_test_cmd *cmd)
1152f4b20bb3SJason Gunthorpe {
1153f4b20bb3SJason Gunthorpe 	struct iommufd_device *idev;
1154f4b20bb3SJason Gunthorpe 	struct selftest_obj *sobj;
1155f4b20bb3SJason Gunthorpe 	u32 pt_id = cmd->id;
1156f4b20bb3SJason Gunthorpe 	u32 dev_flags = 0;
1157f4b20bb3SJason Gunthorpe 	u32 idev_id;
1158f4b20bb3SJason Gunthorpe 	int rc;
1159f4b20bb3SJason Gunthorpe 
1160f4b20bb3SJason Gunthorpe 	sobj = iommufd_object_alloc(ucmd->ictx, sobj, IOMMUFD_OBJ_SELFTEST);
1161f4b20bb3SJason Gunthorpe 	if (IS_ERR(sobj))
1162f4b20bb3SJason Gunthorpe 		return PTR_ERR(sobj);
1163f4b20bb3SJason Gunthorpe 
1164f4b20bb3SJason Gunthorpe 	sobj->idev.ictx = ucmd->ictx;
1165f4b20bb3SJason Gunthorpe 	sobj->type = TYPE_IDEV;
1166f4b20bb3SJason Gunthorpe 
1167f4b20bb3SJason Gunthorpe 	if (cmd->op == IOMMU_TEST_OP_MOCK_DOMAIN_FLAGS)
1168f4b20bb3SJason Gunthorpe 		dev_flags = cmd->mock_domain_flags.dev_flags;
1169f4b20bb3SJason Gunthorpe 
1170f4b20bb3SJason Gunthorpe 	sobj->idev.mock_dev = mock_dev_create(dev_flags);
1171f4b20bb3SJason Gunthorpe 	if (IS_ERR(sobj->idev.mock_dev)) {
1172f4b20bb3SJason Gunthorpe 		rc = PTR_ERR(sobj->idev.mock_dev);
1173f4b20bb3SJason Gunthorpe 		goto out_sobj;
1174f4b20bb3SJason Gunthorpe 	}
1175f4b20bb3SJason Gunthorpe 
1176f4b20bb3SJason Gunthorpe 	idev = iommufd_device_bind(ucmd->ictx, &sobj->idev.mock_dev->dev,
1177f4b20bb3SJason Gunthorpe 				   &idev_id);
1178f4b20bb3SJason Gunthorpe 	if (IS_ERR(idev)) {
1179f4b20bb3SJason Gunthorpe 		rc = PTR_ERR(idev);
1180f4b20bb3SJason Gunthorpe 		goto out_mdev;
1181f4b20bb3SJason Gunthorpe 	}
1182f4b20bb3SJason Gunthorpe 	sobj->idev.idev = idev;
1183f4b20bb3SJason Gunthorpe 
1184f4b20bb3SJason Gunthorpe 	rc = iommufd_device_attach(idev, IOMMU_NO_PASID, &pt_id);
1185f4b20bb3SJason Gunthorpe 	if (rc)
1186cf7c2789SNicolin Chen 		goto out_unbind;
1187f4b20bb3SJason Gunthorpe 
1188f4b20bb3SJason Gunthorpe 	/* Userspace must destroy the device_id to destroy the object */
1189f4b20bb3SJason Gunthorpe 	cmd->mock_domain.out_hwpt_id = pt_id;
1190f4b20bb3SJason Gunthorpe 	cmd->mock_domain.out_stdev_id = sobj->obj.id;
1191f4b20bb3SJason Gunthorpe 	cmd->mock_domain.out_idev_id = idev_id;
1192f4b20bb3SJason Gunthorpe 	rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
1193f4b20bb3SJason Gunthorpe 	if (rc)
1194f4b20bb3SJason Gunthorpe 		goto out_detach;
1195f4b20bb3SJason Gunthorpe 	iommufd_object_finalize(ucmd->ictx, &sobj->obj);
1196f4b20bb3SJason Gunthorpe 	return 0;
1197f4b20bb3SJason Gunthorpe 
1198f4b20bb3SJason Gunthorpe out_detach:
1199f4b20bb3SJason Gunthorpe 	iommufd_device_detach(idev, IOMMU_NO_PASID);
1200f4b20bb3SJason Gunthorpe out_unbind:
1201f4b20bb3SJason Gunthorpe 	iommufd_device_unbind(idev);
1202f4b20bb3SJason Gunthorpe out_mdev:
1203f4b20bb3SJason Gunthorpe 	mock_dev_destroy(sobj->idev.mock_dev);
1204f4b20bb3SJason Gunthorpe out_sobj:
1205f4b20bb3SJason Gunthorpe 	iommufd_object_abort(ucmd->ictx, &sobj->obj);
1206f4b20bb3SJason Gunthorpe 	return rc;
1207f4b20bb3SJason Gunthorpe }
1208f4b20bb3SJason Gunthorpe 
1209f4b20bb3SJason Gunthorpe static struct selftest_obj *
iommufd_test_get_selftest_obj(struct iommufd_ctx * ictx,u32 id)1210f4b20bb3SJason Gunthorpe iommufd_test_get_selftest_obj(struct iommufd_ctx *ictx, u32 id)
1211f4b20bb3SJason Gunthorpe {
1212f4b20bb3SJason Gunthorpe 	struct iommufd_object *dev_obj;
1213f4b20bb3SJason Gunthorpe 	struct selftest_obj *sobj;
1214f4b20bb3SJason Gunthorpe 
1215f4b20bb3SJason Gunthorpe 	/*
1216f4b20bb3SJason Gunthorpe 	 * Prefer to use the OBJ_SELFTEST because the destroy_rwsem will ensure
1217f4b20bb3SJason Gunthorpe 	 * it doesn't race with detach, which is not allowed.
1218f4b20bb3SJason Gunthorpe 	 */
1219f4b20bb3SJason Gunthorpe 	dev_obj = iommufd_get_object(ictx, id, IOMMUFD_OBJ_SELFTEST);
1220f4b20bb3SJason Gunthorpe 	if (IS_ERR(dev_obj))
1221f4b20bb3SJason Gunthorpe 		return ERR_CAST(dev_obj);
1222f4b20bb3SJason Gunthorpe 
1223f4b20bb3SJason Gunthorpe 	sobj = to_selftest_obj(dev_obj);
1224f4b20bb3SJason Gunthorpe 	if (sobj->type != TYPE_IDEV) {
1225f4b20bb3SJason Gunthorpe 		iommufd_put_object(ictx, dev_obj);
1226f4b20bb3SJason Gunthorpe 		return ERR_PTR(-EINVAL);
1227f4b20bb3SJason Gunthorpe 	}
1228f4b20bb3SJason Gunthorpe 	return sobj;
1229f4b20bb3SJason Gunthorpe }
1230f4b20bb3SJason Gunthorpe 
1231f4b20bb3SJason Gunthorpe /* Replace the mock domain with a manually allocated hw_pagetable */
iommufd_test_mock_domain_replace(struct iommufd_ucmd * ucmd,unsigned int device_id,u32 pt_id,struct iommu_test_cmd * cmd)1232f4b20bb3SJason Gunthorpe static int iommufd_test_mock_domain_replace(struct iommufd_ucmd *ucmd,
1233f4b20bb3SJason Gunthorpe 					    unsigned int device_id, u32 pt_id,
1234f4b20bb3SJason Gunthorpe 					    struct iommu_test_cmd *cmd)
1235f4b20bb3SJason Gunthorpe {
1236f4b20bb3SJason Gunthorpe 	struct selftest_obj *sobj;
1237f4b20bb3SJason Gunthorpe 	int rc;
1238f4b20bb3SJason Gunthorpe 
1239f4b20bb3SJason Gunthorpe 	sobj = iommufd_test_get_selftest_obj(ucmd->ictx, device_id);
1240f4b20bb3SJason Gunthorpe 	if (IS_ERR(sobj))
1241f4b20bb3SJason Gunthorpe 		return PTR_ERR(sobj);
1242f4b20bb3SJason Gunthorpe 
1243f4b20bb3SJason Gunthorpe 	rc = iommufd_device_replace(sobj->idev.idev, IOMMU_NO_PASID, &pt_id);
1244f4b20bb3SJason Gunthorpe 	if (rc)
1245f4b20bb3SJason Gunthorpe 		goto out_sobj;
1246f4b20bb3SJason Gunthorpe 
1247f4b20bb3SJason Gunthorpe 	cmd->mock_domain_replace.pt_id = pt_id;
1248f4b20bb3SJason Gunthorpe 	rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
1249f4b20bb3SJason Gunthorpe 
1250f4b20bb3SJason Gunthorpe out_sobj:
1251f4b20bb3SJason Gunthorpe 	iommufd_put_object(ucmd->ictx, &sobj->obj);
1252f4b20bb3SJason Gunthorpe 	return rc;
1253f4b20bb3SJason Gunthorpe }
1254f4b20bb3SJason Gunthorpe 
1255f4b20bb3SJason Gunthorpe /* Add an additional reserved IOVA to the IOAS */
iommufd_test_add_reserved(struct iommufd_ucmd * ucmd,unsigned int mockpt_id,unsigned long start,size_t length)1256f4b20bb3SJason Gunthorpe static int iommufd_test_add_reserved(struct iommufd_ucmd *ucmd,
1257f4b20bb3SJason Gunthorpe 				     unsigned int mockpt_id,
1258f4b20bb3SJason Gunthorpe 				     unsigned long start, size_t length)
1259f4b20bb3SJason Gunthorpe {
1260f4b20bb3SJason Gunthorpe 	struct iommufd_ioas *ioas;
1261f4b20bb3SJason Gunthorpe 	int rc;
1262f4b20bb3SJason Gunthorpe 
1263f4b20bb3SJason Gunthorpe 	ioas = iommufd_get_ioas(ucmd->ictx, mockpt_id);
1264f4b20bb3SJason Gunthorpe 	if (IS_ERR(ioas))
1265f4b20bb3SJason Gunthorpe 		return PTR_ERR(ioas);
1266f4b20bb3SJason Gunthorpe 	down_write(&ioas->iopt.iova_rwsem);
1267f4b20bb3SJason Gunthorpe 	rc = iopt_reserve_iova(&ioas->iopt, start, start + length - 1, NULL);
1268f4b20bb3SJason Gunthorpe 	up_write(&ioas->iopt.iova_rwsem);
1269f4b20bb3SJason Gunthorpe 	iommufd_put_object(ucmd->ictx, &ioas->obj);
1270f4b20bb3SJason Gunthorpe 	return rc;
1271f4b20bb3SJason Gunthorpe }
1272f4b20bb3SJason Gunthorpe 
1273f4b20bb3SJason Gunthorpe /* Check that every pfn under each iova matches the pfn under a user VA */
iommufd_test_md_check_pa(struct iommufd_ucmd * ucmd,unsigned int mockpt_id,unsigned long iova,size_t length,void __user * uptr)1274f4b20bb3SJason Gunthorpe static int iommufd_test_md_check_pa(struct iommufd_ucmd *ucmd,
1275f4b20bb3SJason Gunthorpe 				    unsigned int mockpt_id, unsigned long iova,
1276f4b20bb3SJason Gunthorpe 				    size_t length, void __user *uptr)
1277f4b20bb3SJason Gunthorpe {
1278f4b20bb3SJason Gunthorpe 	struct iommufd_hw_pagetable *hwpt;
1279f4b20bb3SJason Gunthorpe 	struct mock_iommu_domain *mock;
1280f4b20bb3SJason Gunthorpe 	uintptr_t end;
1281f4b20bb3SJason Gunthorpe 	int rc;
1282f4b20bb3SJason Gunthorpe 
1283f4b20bb3SJason Gunthorpe 	if (iova % MOCK_IO_PAGE_SIZE || length % MOCK_IO_PAGE_SIZE ||
1284f4b20bb3SJason Gunthorpe 	    (uintptr_t)uptr % MOCK_IO_PAGE_SIZE ||
1285f4b20bb3SJason Gunthorpe 	    check_add_overflow((uintptr_t)uptr, (uintptr_t)length, &end))
1286f4b20bb3SJason Gunthorpe 		return -EINVAL;
1287f4b20bb3SJason Gunthorpe 
1288cf7c2789SNicolin Chen 	hwpt = get_md_pagetable(ucmd, mockpt_id, &mock);
1289f4b20bb3SJason Gunthorpe 	if (IS_ERR(hwpt))
1290f4b20bb3SJason Gunthorpe 		return PTR_ERR(hwpt);
1291f4b20bb3SJason Gunthorpe 
1292f4b20bb3SJason Gunthorpe 	for (; length; length -= MOCK_IO_PAGE_SIZE) {
1293f4b20bb3SJason Gunthorpe 		struct page *pages[1];
1294f4b20bb3SJason Gunthorpe 		unsigned long pfn;
1295f4b20bb3SJason Gunthorpe 		long npages;
1296f4b20bb3SJason Gunthorpe 		void *ent;
1297f4b20bb3SJason Gunthorpe 
1298f4b20bb3SJason Gunthorpe 		npages = get_user_pages_fast((uintptr_t)uptr & PAGE_MASK, 1, 0,
1299f4b20bb3SJason Gunthorpe 					     pages);
1300f4b20bb3SJason Gunthorpe 		if (npages < 0) {
1301f4b20bb3SJason Gunthorpe 			rc = npages;
1302f4b20bb3SJason Gunthorpe 			goto out_put;
1303f4b20bb3SJason Gunthorpe 		}
1304f4b20bb3SJason Gunthorpe 		if (WARN_ON(npages != 1)) {
1305f4b20bb3SJason Gunthorpe 			rc = -EFAULT;
1306f4b20bb3SJason Gunthorpe 			goto out_put;
1307f4b20bb3SJason Gunthorpe 		}
1308f4b20bb3SJason Gunthorpe 		pfn = page_to_pfn(pages[0]);
1309f4b20bb3SJason Gunthorpe 		put_page(pages[0]);
1310f4b20bb3SJason Gunthorpe 
1311a9af47e3SJoao Martins 		ent = xa_load(&mock->pfns, iova / MOCK_IO_PAGE_SIZE);
1312a9af47e3SJoao Martins 		if (!ent ||
1313a9af47e3SJoao Martins 		    (xa_to_value(ent) & MOCK_PFN_MASK) * MOCK_IO_PAGE_SIZE !=
1314a9af47e3SJoao Martins 			    pfn * PAGE_SIZE + ((uintptr_t)uptr % PAGE_SIZE)) {
1315a9af47e3SJoao Martins 			rc = -EINVAL;
13162e22aac3SJoao Martins 			goto out_put;
1317a9af47e3SJoao Martins 		}
1318a9af47e3SJoao Martins 		iova += MOCK_IO_PAGE_SIZE;
1319a9af47e3SJoao Martins 		uptr += MOCK_IO_PAGE_SIZE;
1320a9af47e3SJoao Martins 	}
1321a9af47e3SJoao Martins 	rc = 0;
1322a9af47e3SJoao Martins 
13232e22aac3SJoao Martins out_put:
13242e22aac3SJoao Martins 	iommufd_put_object(ucmd->ictx, &hwpt->obj);
1325a9af47e3SJoao Martins 	return rc;
1326a9af47e3SJoao Martins }
1327a9af47e3SJoao Martins 
1328a9af47e3SJoao Martins /* Check that the page ref count matches, to look for missing pin/unpins */
iommufd_test_md_check_refs(struct iommufd_ucmd * ucmd,void __user * uptr,size_t length,unsigned int refs)1329a9af47e3SJoao Martins static int iommufd_test_md_check_refs(struct iommufd_ucmd *ucmd,
1330a9af47e3SJoao Martins 				      void __user *uptr, size_t length,
1331a9af47e3SJoao Martins 				      unsigned int refs)
1332a9af47e3SJoao Martins {
1333a9af47e3SJoao Martins 	uintptr_t end;
1334a9af47e3SJoao Martins 
1335a9af47e3SJoao Martins 	if (length % PAGE_SIZE || (uintptr_t)uptr % PAGE_SIZE ||
13362e22aac3SJoao Martins 	    check_add_overflow((uintptr_t)uptr, (uintptr_t)length, &end))
1337a9af47e3SJoao Martins 		return -EINVAL;
1338a9af47e3SJoao Martins 
1339a9af47e3SJoao Martins 	for (; length; length -= PAGE_SIZE) {
1340a9af47e3SJoao Martins 		struct page *pages[1];
1341a9af47e3SJoao Martins 		long npages;
1342a9af47e3SJoao Martins 
1343a9af47e3SJoao Martins 		npages = get_user_pages_fast((uintptr_t)uptr, 1, 0, pages);
1344a9af47e3SJoao Martins 		if (npages < 0)
1345a9af47e3SJoao Martins 			return npages;
1346a9af47e3SJoao Martins 		if (WARN_ON(npages != 1))
1347a9af47e3SJoao Martins 			return -EFAULT;
1348a9af47e3SJoao Martins 		if (!PageCompound(pages[0])) {
1349a9af47e3SJoao Martins 			unsigned int count;
1350a9af47e3SJoao Martins 
1351a9af47e3SJoao Martins 			count = page_ref_count(pages[0]);
1352a9af47e3SJoao Martins 			if (count / GUP_PIN_COUNTING_BIAS != refs) {
1353a9af47e3SJoao Martins 				put_page(pages[0]);
1354a9af47e3SJoao Martins 				return -EIO;
1355a9af47e3SJoao Martins 			}
1356a9af47e3SJoao Martins 		}
1357a9af47e3SJoao Martins 		put_page(pages[0]);
1358a9af47e3SJoao Martins 		uptr += PAGE_SIZE;
1359a9af47e3SJoao Martins 	}
1360a9af47e3SJoao Martins 	return 0;
1361a9af47e3SJoao Martins }
1362a9af47e3SJoao Martins 
iommufd_test_md_check_iotlb(struct iommufd_ucmd * ucmd,u32 mockpt_id,unsigned int iotlb_id,u32 iotlb)1363a9af47e3SJoao Martins static int iommufd_test_md_check_iotlb(struct iommufd_ucmd *ucmd, u32 mockpt_id,
1364a9af47e3SJoao Martins 				       unsigned int iotlb_id, u32 iotlb)
1365a9af47e3SJoao Martins {
1366a9af47e3SJoao Martins 	struct mock_iommu_domain_nested *mock_nested;
1367a9af47e3SJoao Martins 	struct iommufd_hw_pagetable *hwpt;
1368a9af47e3SJoao Martins 	int rc = 0;
1369a9af47e3SJoao Martins 
1370a9af47e3SJoao Martins 	hwpt = get_md_pagetable_nested(ucmd, mockpt_id, &mock_nested);
1371a9af47e3SJoao Martins 	if (IS_ERR(hwpt))
1372a9af47e3SJoao Martins 		return PTR_ERR(hwpt);
1373a9af47e3SJoao Martins 
1374bd7a2826SJason Gunthorpe 	mock_nested = to_mock_nested(hwpt->domain);
1375a9af47e3SJoao Martins 
1376a9af47e3SJoao Martins 	if (iotlb_id > MOCK_NESTED_DOMAIN_IOTLB_ID_MAX ||
1377a9af47e3SJoao Martins 	    mock_nested->iotlb[iotlb_id] != iotlb)
1378f4b20bb3SJason Gunthorpe 		rc = -EINVAL;
1379f4b20bb3SJason Gunthorpe 	iommufd_put_object(ucmd->ictx, &hwpt->obj);
1380f4b20bb3SJason Gunthorpe 	return rc;
1381f4b20bb3SJason Gunthorpe }
1382f4b20bb3SJason Gunthorpe 
iommufd_test_dev_check_cache(struct iommufd_ucmd * ucmd,u32 idev_id,unsigned int cache_id,u32 cache)1383f4b20bb3SJason Gunthorpe static int iommufd_test_dev_check_cache(struct iommufd_ucmd *ucmd, u32 idev_id,
138465c619aeSJason Gunthorpe 					unsigned int cache_id, u32 cache)
138565c619aeSJason Gunthorpe {
138665c619aeSJason Gunthorpe 	struct iommufd_device *idev;
1387f4b20bb3SJason Gunthorpe 	struct mock_dev *mdev;
1388f4b20bb3SJason Gunthorpe 	int rc = 0;
1389f4b20bb3SJason Gunthorpe 
1390f4b20bb3SJason Gunthorpe 	idev = iommufd_get_device(ucmd, idev_id);
1391f4b20bb3SJason Gunthorpe 	if (IS_ERR(idev))
1392f4b20bb3SJason Gunthorpe 		return PTR_ERR(idev);
1393f4b20bb3SJason Gunthorpe 	mdev = container_of(idev->dev, struct mock_dev, dev);
1394f4b20bb3SJason Gunthorpe 
1395f4b20bb3SJason Gunthorpe 	if (cache_id > MOCK_DEV_CACHE_ID_MAX || mdev->cache[cache_id] != cache)
1396f4b20bb3SJason Gunthorpe 		rc = -EINVAL;
1397f4b20bb3SJason Gunthorpe 	iommufd_put_object(ucmd->ictx, &idev->obj);
1398f4b20bb3SJason Gunthorpe 	return rc;
1399f4b20bb3SJason Gunthorpe }
1400f4b20bb3SJason Gunthorpe 
1401e04b23c8SJoao Martins struct selftest_access {
1402f4b20bb3SJason Gunthorpe 	struct iommufd_access *access;
1403fa1ffdb9SNicolin Chen 	struct file *file;
1404fa1ffdb9SNicolin Chen 	struct mutex lock;
1405fa1ffdb9SNicolin Chen 	struct list_head items;
1406f4b20bb3SJason Gunthorpe 	unsigned int next_id;
1407f4b20bb3SJason Gunthorpe 	bool destroying;
1408f4b20bb3SJason Gunthorpe };
1409f4b20bb3SJason Gunthorpe 
1410f4b20bb3SJason Gunthorpe struct selftest_access_item {
1411f4b20bb3SJason Gunthorpe 	struct list_head items_elm;
1412f4b20bb3SJason Gunthorpe 	unsigned long iova;
1413f4b20bb3SJason Gunthorpe 	size_t length;
1414f4b20bb3SJason Gunthorpe 	unsigned int id;
1415e1fa6640SNicolin Chen };
1416e1fa6640SNicolin Chen 
1417e1fa6640SNicolin Chen static const struct file_operations iommfd_test_staccess_fops;
1418e1fa6640SNicolin Chen 
iommufd_access_get(int fd)1419f4b20bb3SJason Gunthorpe static struct selftest_access *iommufd_access_get(int fd)
1420f4b20bb3SJason Gunthorpe {
1421f4b20bb3SJason Gunthorpe 	struct file *file;
1422c154660bSNicolin Chen 
1423c154660bSNicolin Chen 	file = fget(fd);
1424c154660bSNicolin Chen 	if (!file)
1425f4b20bb3SJason Gunthorpe 		return ERR_PTR(-EBADFD);
1426f4b20bb3SJason Gunthorpe 
1427f4b20bb3SJason Gunthorpe 	if (file->f_op != &iommfd_test_staccess_fops) {
1428f4b20bb3SJason Gunthorpe 		fput(file);
1429f4b20bb3SJason Gunthorpe 		return ERR_PTR(-EBADFD);
1430f4b20bb3SJason Gunthorpe 	}
1431f4b20bb3SJason Gunthorpe 	return file->private_data;
1432f4b20bb3SJason Gunthorpe }
1433f4b20bb3SJason Gunthorpe 
iommufd_test_access_unmap(void * data,unsigned long iova,unsigned long length)1434f4b20bb3SJason Gunthorpe static void iommufd_test_access_unmap(void *data, unsigned long iova,
1435f4b20bb3SJason Gunthorpe 				      unsigned long length)
1436f4b20bb3SJason Gunthorpe {
1437f4b20bb3SJason Gunthorpe 	unsigned long iova_last = iova + length - 1;
1438f4b20bb3SJason Gunthorpe 	struct selftest_access *staccess = data;
1439f4b20bb3SJason Gunthorpe 	struct selftest_access_item *item;
1440f4b20bb3SJason Gunthorpe 	struct selftest_access_item *tmp;
1441f4b20bb3SJason Gunthorpe 
1442f4b20bb3SJason Gunthorpe 	mutex_lock(&staccess->lock);
1443f4b20bb3SJason Gunthorpe 	list_for_each_entry_safe(item, tmp, &staccess->items, items_elm) {
1444f4b20bb3SJason Gunthorpe 		if (iova > item->iova + item->length - 1 ||
1445f4b20bb3SJason Gunthorpe 		    iova_last < item->iova)
1446f4b20bb3SJason Gunthorpe 			continue;
1447a9af47e3SJoao Martins 		list_del(&item->items_elm);
1448a9af47e3SJoao Martins 		iommufd_access_unpin_pages(staccess->access, item->iova,
1449a9af47e3SJoao Martins 					   item->length);
1450a9af47e3SJoao Martins 		kfree(item);
1451a9af47e3SJoao Martins 	}
1452a9af47e3SJoao Martins 	mutex_unlock(&staccess->lock);
1453f4b20bb3SJason Gunthorpe }
1454f4b20bb3SJason Gunthorpe 
iommufd_test_access_item_destroy(struct iommufd_ucmd * ucmd,unsigned int access_id,unsigned int item_id)1455f4b20bb3SJason Gunthorpe static int iommufd_test_access_item_destroy(struct iommufd_ucmd *ucmd,
1456f4b20bb3SJason Gunthorpe 					    unsigned int access_id,
1457f4b20bb3SJason Gunthorpe 					    unsigned int item_id)
1458f4b20bb3SJason Gunthorpe {
1459f4b20bb3SJason Gunthorpe 	struct selftest_access_item *item;
1460f4b20bb3SJason Gunthorpe 	struct selftest_access *staccess;
1461f4b20bb3SJason Gunthorpe 
1462f4b20bb3SJason Gunthorpe 	staccess = iommufd_access_get(access_id);
146323a1b46fSJason Gunthorpe 	if (IS_ERR(staccess))
1464f4b20bb3SJason Gunthorpe 		return PTR_ERR(staccess);
146523a1b46fSJason Gunthorpe 
146623a1b46fSJason Gunthorpe 	mutex_lock(&staccess->lock);
146723a1b46fSJason Gunthorpe 	list_for_each_entry(item, &staccess->items, items_elm) {
146823a1b46fSJason Gunthorpe 		if (item->id == item_id) {
146923a1b46fSJason Gunthorpe 			list_del(&item->items_elm);
1470f4b20bb3SJason Gunthorpe 			iommufd_access_unpin_pages(staccess->access, item->iova,
1471f4b20bb3SJason Gunthorpe 						   item->length);
147223a1b46fSJason Gunthorpe 			mutex_unlock(&staccess->lock);
147323a1b46fSJason Gunthorpe 			kfree(item);
147423a1b46fSJason Gunthorpe 			fput(staccess->file);
147523a1b46fSJason Gunthorpe 			return 0;
147623a1b46fSJason Gunthorpe 		}
147723a1b46fSJason Gunthorpe 	}
147823a1b46fSJason Gunthorpe 	mutex_unlock(&staccess->lock);
147923a1b46fSJason Gunthorpe 	fput(staccess->file);
148023a1b46fSJason Gunthorpe 	return -ENOENT;
148123a1b46fSJason Gunthorpe }
148223a1b46fSJason Gunthorpe 
iommufd_test_staccess_release(struct inode * inode,struct file * filep)148323a1b46fSJason Gunthorpe static int iommufd_test_staccess_release(struct inode *inode,
148423a1b46fSJason Gunthorpe 					 struct file *filep)
148523a1b46fSJason Gunthorpe {
148623a1b46fSJason Gunthorpe 	struct selftest_access *staccess = filep->private_data;
148723a1b46fSJason Gunthorpe 
148823a1b46fSJason Gunthorpe 	if (staccess->access) {
148923a1b46fSJason Gunthorpe 		iommufd_test_access_unmap(staccess, 0, ULONG_MAX);
149023a1b46fSJason Gunthorpe 		iommufd_access_destroy(staccess->access);
149123a1b46fSJason Gunthorpe 	}
149223a1b46fSJason Gunthorpe 	mutex_destroy(&staccess->lock);
149323a1b46fSJason Gunthorpe 	kfree(staccess);
149423a1b46fSJason Gunthorpe 	return 0;
149523a1b46fSJason Gunthorpe }
149623a1b46fSJason Gunthorpe 
149723a1b46fSJason Gunthorpe static const struct iommufd_access_ops selftest_access_ops_pin = {
149823a1b46fSJason Gunthorpe 	.needs_pin_pages = 1,
149923a1b46fSJason Gunthorpe 	.unmap = iommufd_test_access_unmap,
150023a1b46fSJason Gunthorpe };
1501eb501c2dSYang Yingliang 
150223a1b46fSJason Gunthorpe static const struct iommufd_access_ops selftest_access_ops = {
150323a1b46fSJason Gunthorpe 	.unmap = iommufd_test_access_unmap,
150423a1b46fSJason Gunthorpe };
1505f4b20bb3SJason Gunthorpe 
1506f4b20bb3SJason Gunthorpe static const struct file_operations iommfd_test_staccess_fops = {
1507f4b20bb3SJason Gunthorpe 	.release = iommufd_test_staccess_release,
1508f4b20bb3SJason Gunthorpe };
150923a1b46fSJason Gunthorpe 
iommufd_test_alloc_access(void)151023a1b46fSJason Gunthorpe static struct selftest_access *iommufd_test_alloc_access(void)
151123a1b46fSJason Gunthorpe {
151223a1b46fSJason Gunthorpe 	struct selftest_access *staccess;
151323a1b46fSJason Gunthorpe 	struct file *filep;
1514eb501c2dSYang Yingliang 
1515f4b20bb3SJason Gunthorpe 	staccess = kzalloc(sizeof(*staccess), GFP_KERNEL_ACCOUNT);
1516f4b20bb3SJason Gunthorpe 	if (!staccess)
1517 		return ERR_PTR(-ENOMEM);
1518 	INIT_LIST_HEAD(&staccess->items);
1519 	mutex_init(&staccess->lock);
1520 
1521 	filep = anon_inode_getfile("[iommufd_test_staccess]",
1522 				   &iommfd_test_staccess_fops, staccess,
1523 				   O_RDWR);
1524 	if (IS_ERR(filep)) {
1525 		kfree(staccess);
1526 		return ERR_CAST(filep);
1527 	}
1528 	staccess->file = filep;
1529 	return staccess;
1530 }
1531 
iommufd_test_create_access(struct iommufd_ucmd * ucmd,unsigned int ioas_id,unsigned int flags)1532 static int iommufd_test_create_access(struct iommufd_ucmd *ucmd,
1533 				      unsigned int ioas_id, unsigned int flags)
1534 {
1535 	struct iommu_test_cmd *cmd = ucmd->cmd;
1536 	struct selftest_access *staccess;
1537 	struct iommufd_access *access;
1538 	u32 id;
1539 	int fdno;
1540 	int rc;
1541 
1542 	if (flags & ~MOCK_FLAGS_ACCESS_CREATE_NEEDS_PIN_PAGES)
1543 		return -EOPNOTSUPP;
1544 
1545 	staccess = iommufd_test_alloc_access();
1546 	if (IS_ERR(staccess))
1547 		return PTR_ERR(staccess);
1548 
1549 	fdno = get_unused_fd_flags(O_CLOEXEC);
1550 	if (fdno < 0) {
1551 		rc = -ENOMEM;
1552 		goto out_free_staccess;
1553 	}
1554 
1555 	access = iommufd_access_create(
1556 		ucmd->ictx,
1557 		(flags & MOCK_FLAGS_ACCESS_CREATE_NEEDS_PIN_PAGES) ?
1558 			&selftest_access_ops_pin :
1559 			&selftest_access_ops,
1560 		staccess, &id);
1561 	if (IS_ERR(access)) {
1562 		rc = PTR_ERR(access);
1563 		goto out_put_fdno;
1564 	}
1565 	rc = iommufd_access_attach(access, ioas_id);
1566 	if (rc)
1567 		goto out_destroy;
1568 	cmd->create_access.out_access_fd = fdno;
1569 	rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
1570 	if (rc)
1571 		goto out_destroy;
1572 
1573 	staccess->access = access;
1574 	fd_install(fdno, staccess->file);
1575 	return 0;
1576 
1577 out_destroy:
1578 	iommufd_access_destroy(access);
1579 out_put_fdno:
1580 	put_unused_fd(fdno);
1581 out_free_staccess:
1582 	fput(staccess->file);
1583 	return rc;
1584 }
1585 
iommufd_test_access_replace_ioas(struct iommufd_ucmd * ucmd,unsigned int access_id,unsigned int ioas_id)1586 static int iommufd_test_access_replace_ioas(struct iommufd_ucmd *ucmd,
1587 					    unsigned int access_id,
1588 					    unsigned int ioas_id)
1589 {
1590 	struct selftest_access *staccess;
1591 	int rc;
1592 
1593 	staccess = iommufd_access_get(access_id);
1594 	if (IS_ERR(staccess))
1595 		return PTR_ERR(staccess);
1596 
1597 	rc = iommufd_access_replace(staccess->access, ioas_id);
1598 	fput(staccess->file);
1599 	return rc;
1600 }
1601 
1602 /* Check that the pages in a page array match the pages in the user VA */
iommufd_test_check_pages(void __user * uptr,struct page ** pages,size_t npages)1603 static int iommufd_test_check_pages(void __user *uptr, struct page **pages,
1604 				    size_t npages)
1605 {
1606 	for (; npages; npages--) {
1607 		struct page *tmp_pages[1];
1608 		long rc;
1609 
1610 		rc = get_user_pages_fast((uintptr_t)uptr, 1, 0, tmp_pages);
1611 		if (rc < 0)
1612 			return rc;
1613 		if (WARN_ON(rc != 1))
1614 			return -EFAULT;
1615 		put_page(tmp_pages[0]);
1616 		if (tmp_pages[0] != *pages)
1617 			return -EBADE;
1618 		pages++;
1619 		uptr += PAGE_SIZE;
1620 	}
1621 	return 0;
1622 }
1623 
iommufd_test_access_pages(struct iommufd_ucmd * ucmd,unsigned int access_id,unsigned long iova,size_t length,void __user * uptr,u32 flags)1624 static int iommufd_test_access_pages(struct iommufd_ucmd *ucmd,
1625 				     unsigned int access_id, unsigned long iova,
1626 				     size_t length, void __user *uptr,
1627 				     u32 flags)
1628 {
1629 	struct iommu_test_cmd *cmd = ucmd->cmd;
1630 	struct selftest_access_item *item;
1631 	struct selftest_access *staccess;
1632 	struct page **pages;
1633 	size_t npages;
1634 	int rc;
1635 
1636 	/* Prevent syzkaller from triggering a WARN_ON in kvzalloc() */
1637 	if (length > 16 * 1024 * 1024)
1638 		return -ENOMEM;
1639 
1640 	if (flags & ~(MOCK_FLAGS_ACCESS_WRITE | MOCK_FLAGS_ACCESS_SYZ))
1641 		return -EOPNOTSUPP;
1642 
1643 	staccess = iommufd_access_get(access_id);
1644 	if (IS_ERR(staccess))
1645 		return PTR_ERR(staccess);
1646 
1647 	if (staccess->access->ops != &selftest_access_ops_pin) {
1648 		rc = -EOPNOTSUPP;
1649 		goto out_put;
1650 	}
1651 
1652 	if (flags & MOCK_FLAGS_ACCESS_SYZ)
1653 		iova = iommufd_test_syz_conv_iova(staccess->access,
1654 						  &cmd->access_pages.iova);
1655 
1656 	npages = (ALIGN(iova + length, PAGE_SIZE) -
1657 		  ALIGN_DOWN(iova, PAGE_SIZE)) /
1658 		 PAGE_SIZE;
1659 	pages = kvcalloc(npages, sizeof(*pages), GFP_KERNEL_ACCOUNT);
1660 	if (!pages) {
1661 		rc = -ENOMEM;
1662 		goto out_put;
1663 	}
1664 
1665 	/*
1666 	 * Drivers will need to think very carefully about this locking. The
1667 	 * core code can do multiple unmaps instantaneously after
1668 	 * iommufd_access_pin_pages() and *all* the unmaps must not return until
1669 	 * the range is unpinned. This simple implementation puts a global lock
1670 	 * around the pin, which may not suit drivers that want this to be a
1671 	 * performance path. drivers that get this wrong will trigger WARN_ON
1672 	 * races and cause EDEADLOCK failures to userspace.
1673 	 */
1674 	mutex_lock(&staccess->lock);
1675 	rc = iommufd_access_pin_pages(staccess->access, iova, length, pages,
1676 				      flags & MOCK_FLAGS_ACCESS_WRITE);
1677 	if (rc)
1678 		goto out_unlock;
1679 
1680 	/* For syzkaller allow uptr to be NULL to skip this check */
1681 	if (uptr) {
1682 		rc = iommufd_test_check_pages(
1683 			uptr - (iova - ALIGN_DOWN(iova, PAGE_SIZE)), pages,
1684 			npages);
1685 		if (rc)
1686 			goto out_unaccess;
1687 	}
1688 
1689 	item = kzalloc(sizeof(*item), GFP_KERNEL_ACCOUNT);
1690 	if (!item) {
1691 		rc = -ENOMEM;
1692 		goto out_unaccess;
1693 	}
1694 
1695 	item->iova = iova;
1696 	item->length = length;
1697 	item->id = staccess->next_id++;
1698 	list_add_tail(&item->items_elm, &staccess->items);
1699 
1700 	cmd->access_pages.out_access_pages_id = item->id;
1701 	rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
1702 	if (rc)
1703 		goto out_free_item;
1704 	goto out_unlock;
1705 
1706 out_free_item:
1707 	list_del(&item->items_elm);
1708 	kfree(item);
1709 out_unaccess:
1710 	iommufd_access_unpin_pages(staccess->access, iova, length);
1711 out_unlock:
1712 	mutex_unlock(&staccess->lock);
1713 	kvfree(pages);
1714 out_put:
1715 	fput(staccess->file);
1716 	return rc;
1717 }
1718 
iommufd_test_access_rw(struct iommufd_ucmd * ucmd,unsigned int access_id,unsigned long iova,size_t length,void __user * ubuf,unsigned int flags)1719 static int iommufd_test_access_rw(struct iommufd_ucmd *ucmd,
1720 				  unsigned int access_id, unsigned long iova,
1721 				  size_t length, void __user *ubuf,
1722 				  unsigned int flags)
1723 {
1724 	struct iommu_test_cmd *cmd = ucmd->cmd;
1725 	struct selftest_access *staccess;
1726 	void *tmp;
1727 	int rc;
1728 
1729 	/* Prevent syzkaller from triggering a WARN_ON in kvzalloc() */
1730 	if (length > 16 * 1024 * 1024)
1731 		return -ENOMEM;
1732 
1733 	if (flags & ~(MOCK_ACCESS_RW_WRITE | MOCK_ACCESS_RW_SLOW_PATH |
1734 		      MOCK_FLAGS_ACCESS_SYZ))
1735 		return -EOPNOTSUPP;
1736 
1737 	staccess = iommufd_access_get(access_id);
1738 	if (IS_ERR(staccess))
1739 		return PTR_ERR(staccess);
1740 
1741 	tmp = kvzalloc(length, GFP_KERNEL_ACCOUNT);
1742 	if (!tmp) {
1743 		rc = -ENOMEM;
1744 		goto out_put;
1745 	}
1746 
1747 	if (flags & MOCK_ACCESS_RW_WRITE) {
1748 		if (copy_from_user(tmp, ubuf, length)) {
1749 			rc = -EFAULT;
1750 			goto out_free;
1751 		}
1752 	}
1753 
1754 	if (flags & MOCK_FLAGS_ACCESS_SYZ)
1755 		iova = iommufd_test_syz_conv_iova(staccess->access,
1756 						  &cmd->access_rw.iova);
1757 
1758 	rc = iommufd_access_rw(staccess->access, iova, tmp, length, flags);
1759 	if (rc)
1760 		goto out_free;
1761 	if (!(flags & MOCK_ACCESS_RW_WRITE)) {
1762 		if (copy_to_user(ubuf, tmp, length)) {
1763 			rc = -EFAULT;
1764 			goto out_free;
1765 		}
1766 	}
1767 
1768 out_free:
1769 	kvfree(tmp);
1770 out_put:
1771 	fput(staccess->file);
1772 	return rc;
1773 }
1774 static_assert((unsigned int)MOCK_ACCESS_RW_WRITE == IOMMUFD_ACCESS_RW_WRITE);
1775 static_assert((unsigned int)MOCK_ACCESS_RW_SLOW_PATH ==
1776 	      __IOMMUFD_ACCESS_RW_SLOW_PATH);
1777 
iommufd_test_dirty(struct iommufd_ucmd * ucmd,unsigned int mockpt_id,unsigned long iova,size_t length,unsigned long page_size,void __user * uptr,u32 flags)1778 static int iommufd_test_dirty(struct iommufd_ucmd *ucmd, unsigned int mockpt_id,
1779 			      unsigned long iova, size_t length,
1780 			      unsigned long page_size, void __user *uptr,
1781 			      u32 flags)
1782 {
1783 	unsigned long i, max;
1784 	struct iommu_test_cmd *cmd = ucmd->cmd;
1785 	struct iommufd_hw_pagetable *hwpt;
1786 	struct mock_iommu_domain *mock;
1787 	int rc, count = 0;
1788 	void *tmp;
1789 
1790 	if (!page_size || !length || iova % page_size || length % page_size ||
1791 	    !uptr)
1792 		return -EINVAL;
1793 
1794 	hwpt = get_md_pagetable(ucmd, mockpt_id, &mock);
1795 	if (IS_ERR(hwpt))
1796 		return PTR_ERR(hwpt);
1797 
1798 	if (!(mock->flags & MOCK_DIRTY_TRACK)) {
1799 		rc = -EINVAL;
1800 		goto out_put;
1801 	}
1802 
1803 	max = length / page_size;
1804 	tmp = kvzalloc(DIV_ROUND_UP(max, BITS_PER_LONG) * sizeof(unsigned long),
1805 		       GFP_KERNEL_ACCOUNT);
1806 	if (!tmp) {
1807 		rc = -ENOMEM;
1808 		goto out_put;
1809 	}
1810 
1811 	if (copy_from_user(tmp, uptr, DIV_ROUND_UP(max, BITS_PER_BYTE))) {
1812 		rc = -EFAULT;
1813 		goto out_free;
1814 	}
1815 
1816 	for (i = 0; i < max; i++) {
1817 		unsigned long cur = iova + i * page_size;
1818 		void *ent, *old;
1819 
1820 		if (!test_bit(i, (unsigned long *)tmp))
1821 			continue;
1822 
1823 		ent = xa_load(&mock->pfns, cur / page_size);
1824 		if (ent) {
1825 			unsigned long val;
1826 
1827 			val = xa_to_value(ent) | MOCK_PFN_DIRTY_IOVA;
1828 			old = xa_store(&mock->pfns, cur / page_size,
1829 				       xa_mk_value(val), GFP_KERNEL);
1830 			WARN_ON_ONCE(ent != old);
1831 			count++;
1832 		}
1833 	}
1834 
1835 	cmd->dirty.out_nr_dirty = count;
1836 	rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
1837 out_free:
1838 	kvfree(tmp);
1839 out_put:
1840 	iommufd_put_object(ucmd->ictx, &hwpt->obj);
1841 	return rc;
1842 }
1843 
iommufd_test_trigger_iopf(struct iommufd_ucmd * ucmd,struct iommu_test_cmd * cmd)1844 static int iommufd_test_trigger_iopf(struct iommufd_ucmd *ucmd,
1845 				     struct iommu_test_cmd *cmd)
1846 {
1847 	struct iopf_fault event = {};
1848 	struct iommufd_device *idev;
1849 
1850 	idev = iommufd_get_device(ucmd, cmd->trigger_iopf.dev_id);
1851 	if (IS_ERR(idev))
1852 		return PTR_ERR(idev);
1853 
1854 	event.fault.prm.flags = IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE;
1855 	if (cmd->trigger_iopf.pasid != IOMMU_NO_PASID)
1856 		event.fault.prm.flags |= IOMMU_FAULT_PAGE_REQUEST_PASID_VALID;
1857 	event.fault.type = IOMMU_FAULT_PAGE_REQ;
1858 	event.fault.prm.addr = cmd->trigger_iopf.addr;
1859 	event.fault.prm.pasid = cmd->trigger_iopf.pasid;
1860 	event.fault.prm.grpid = cmd->trigger_iopf.grpid;
1861 	event.fault.prm.perm = cmd->trigger_iopf.perm;
1862 
1863 	iommu_report_device_fault(idev->dev, &event);
1864 	iommufd_put_object(ucmd->ictx, &idev->obj);
1865 
1866 	return 0;
1867 }
1868 
iommufd_test_trigger_vevent(struct iommufd_ucmd * ucmd,struct iommu_test_cmd * cmd)1869 static int iommufd_test_trigger_vevent(struct iommufd_ucmd *ucmd,
1870 				       struct iommu_test_cmd *cmd)
1871 {
1872 	struct iommu_viommu_event_selftest test = {};
1873 	struct iommufd_device *idev;
1874 	struct mock_dev *mdev;
1875 	int rc = -ENOENT;
1876 
1877 	idev = iommufd_get_device(ucmd, cmd->trigger_vevent.dev_id);
1878 	if (IS_ERR(idev))
1879 		return PTR_ERR(idev);
1880 	mdev = to_mock_dev(idev->dev);
1881 
1882 	down_read(&mdev->viommu_rwsem);
1883 	if (!mdev->viommu || !mdev->vdev_id)
1884 		goto out_unlock;
1885 
1886 	test.virt_id = mdev->vdev_id;
1887 	rc = iommufd_viommu_report_event(&mdev->viommu->core,
1888 					 IOMMU_VEVENTQ_TYPE_SELFTEST, &test,
1889 					 sizeof(test));
1890 out_unlock:
1891 	up_read(&mdev->viommu_rwsem);
1892 	iommufd_put_object(ucmd->ictx, &idev->obj);
1893 
1894 	return rc;
1895 }
1896 
1897 static inline struct iommufd_hw_pagetable *
iommufd_get_hwpt(struct iommufd_ucmd * ucmd,u32 id)1898 iommufd_get_hwpt(struct iommufd_ucmd *ucmd, u32 id)
1899 {
1900 	struct iommufd_object *pt_obj;
1901 
1902 	pt_obj = iommufd_get_object(ucmd->ictx, id, IOMMUFD_OBJ_ANY);
1903 	if (IS_ERR(pt_obj))
1904 		return ERR_CAST(pt_obj);
1905 
1906 	if (pt_obj->type != IOMMUFD_OBJ_HWPT_NESTED &&
1907 	    pt_obj->type != IOMMUFD_OBJ_HWPT_PAGING) {
1908 		iommufd_put_object(ucmd->ictx, pt_obj);
1909 		return ERR_PTR(-EINVAL);
1910 	}
1911 
1912 	return container_of(pt_obj, struct iommufd_hw_pagetable, obj);
1913 }
1914 
iommufd_test_pasid_check_hwpt(struct iommufd_ucmd * ucmd,struct iommu_test_cmd * cmd)1915 static int iommufd_test_pasid_check_hwpt(struct iommufd_ucmd *ucmd,
1916 					 struct iommu_test_cmd *cmd)
1917 {
1918 	u32 hwpt_id = cmd->pasid_check.hwpt_id;
1919 	struct iommu_domain *attached_domain;
1920 	struct iommu_attach_handle *handle;
1921 	struct iommufd_hw_pagetable *hwpt;
1922 	struct selftest_obj *sobj;
1923 	struct mock_dev *mdev;
1924 	int rc = 0;
1925 
1926 	sobj = iommufd_test_get_selftest_obj(ucmd->ictx, cmd->id);
1927 	if (IS_ERR(sobj))
1928 		return PTR_ERR(sobj);
1929 
1930 	mdev = sobj->idev.mock_dev;
1931 
1932 	handle = iommu_attach_handle_get(mdev->dev.iommu_group,
1933 					 cmd->pasid_check.pasid, 0);
1934 	if (IS_ERR(handle))
1935 		attached_domain = NULL;
1936 	else
1937 		attached_domain = handle->domain;
1938 
1939 	/* hwpt_id == 0 means to check if pasid is detached */
1940 	if (!hwpt_id) {
1941 		if (attached_domain)
1942 			rc = -EINVAL;
1943 		goto out_sobj;
1944 	}
1945 
1946 	hwpt = iommufd_get_hwpt(ucmd, hwpt_id);
1947 	if (IS_ERR(hwpt)) {
1948 		rc = PTR_ERR(hwpt);
1949 		goto out_sobj;
1950 	}
1951 
1952 	if (attached_domain != hwpt->domain)
1953 		rc = -EINVAL;
1954 
1955 	iommufd_put_object(ucmd->ictx, &hwpt->obj);
1956 out_sobj:
1957 	iommufd_put_object(ucmd->ictx, &sobj->obj);
1958 	return rc;
1959 }
1960 
iommufd_test_pasid_attach(struct iommufd_ucmd * ucmd,struct iommu_test_cmd * cmd)1961 static int iommufd_test_pasid_attach(struct iommufd_ucmd *ucmd,
1962 				     struct iommu_test_cmd *cmd)
1963 {
1964 	struct selftest_obj *sobj;
1965 	int rc;
1966 
1967 	sobj = iommufd_test_get_selftest_obj(ucmd->ictx, cmd->id);
1968 	if (IS_ERR(sobj))
1969 		return PTR_ERR(sobj);
1970 
1971 	rc = iommufd_device_attach(sobj->idev.idev, cmd->pasid_attach.pasid,
1972 				   &cmd->pasid_attach.pt_id);
1973 	if (rc)
1974 		goto out_sobj;
1975 
1976 	rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
1977 	if (rc)
1978 		iommufd_device_detach(sobj->idev.idev, cmd->pasid_attach.pasid);
1979 
1980 out_sobj:
1981 	iommufd_put_object(ucmd->ictx, &sobj->obj);
1982 	return rc;
1983 }
1984 
iommufd_test_pasid_replace(struct iommufd_ucmd * ucmd,struct iommu_test_cmd * cmd)1985 static int iommufd_test_pasid_replace(struct iommufd_ucmd *ucmd,
1986 				      struct iommu_test_cmd *cmd)
1987 {
1988 	struct selftest_obj *sobj;
1989 	int rc;
1990 
1991 	sobj = iommufd_test_get_selftest_obj(ucmd->ictx, cmd->id);
1992 	if (IS_ERR(sobj))
1993 		return PTR_ERR(sobj);
1994 
1995 	rc = iommufd_device_replace(sobj->idev.idev, cmd->pasid_attach.pasid,
1996 				    &cmd->pasid_attach.pt_id);
1997 	if (rc)
1998 		goto out_sobj;
1999 
2000 	rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
2001 
2002 out_sobj:
2003 	iommufd_put_object(ucmd->ictx, &sobj->obj);
2004 	return rc;
2005 }
2006 
iommufd_test_pasid_detach(struct iommufd_ucmd * ucmd,struct iommu_test_cmd * cmd)2007 static int iommufd_test_pasid_detach(struct iommufd_ucmd *ucmd,
2008 				     struct iommu_test_cmd *cmd)
2009 {
2010 	struct selftest_obj *sobj;
2011 
2012 	sobj = iommufd_test_get_selftest_obj(ucmd->ictx, cmd->id);
2013 	if (IS_ERR(sobj))
2014 		return PTR_ERR(sobj);
2015 
2016 	iommufd_device_detach(sobj->idev.idev, cmd->pasid_detach.pasid);
2017 	iommufd_put_object(ucmd->ictx, &sobj->obj);
2018 	return 0;
2019 }
2020 
iommufd_selftest_destroy(struct iommufd_object * obj)2021 void iommufd_selftest_destroy(struct iommufd_object *obj)
2022 {
2023 	struct selftest_obj *sobj = to_selftest_obj(obj);
2024 
2025 	switch (sobj->type) {
2026 	case TYPE_IDEV:
2027 		iommufd_device_detach(sobj->idev.idev, IOMMU_NO_PASID);
2028 		iommufd_device_unbind(sobj->idev.idev);
2029 		mock_dev_destroy(sobj->idev.mock_dev);
2030 		break;
2031 	}
2032 }
2033 
iommufd_test(struct iommufd_ucmd * ucmd)2034 int iommufd_test(struct iommufd_ucmd *ucmd)
2035 {
2036 	struct iommu_test_cmd *cmd = ucmd->cmd;
2037 
2038 	switch (cmd->op) {
2039 	case IOMMU_TEST_OP_ADD_RESERVED:
2040 		return iommufd_test_add_reserved(ucmd, cmd->id,
2041 						 cmd->add_reserved.start,
2042 						 cmd->add_reserved.length);
2043 	case IOMMU_TEST_OP_MOCK_DOMAIN:
2044 	case IOMMU_TEST_OP_MOCK_DOMAIN_FLAGS:
2045 		return iommufd_test_mock_domain(ucmd, cmd);
2046 	case IOMMU_TEST_OP_MOCK_DOMAIN_REPLACE:
2047 		return iommufd_test_mock_domain_replace(
2048 			ucmd, cmd->id, cmd->mock_domain_replace.pt_id, cmd);
2049 	case IOMMU_TEST_OP_MD_CHECK_MAP:
2050 		return iommufd_test_md_check_pa(
2051 			ucmd, cmd->id, cmd->check_map.iova,
2052 			cmd->check_map.length,
2053 			u64_to_user_ptr(cmd->check_map.uptr));
2054 	case IOMMU_TEST_OP_MD_CHECK_REFS:
2055 		return iommufd_test_md_check_refs(
2056 			ucmd, u64_to_user_ptr(cmd->check_refs.uptr),
2057 			cmd->check_refs.length, cmd->check_refs.refs);
2058 	case IOMMU_TEST_OP_MD_CHECK_IOTLB:
2059 		return iommufd_test_md_check_iotlb(ucmd, cmd->id,
2060 						   cmd->check_iotlb.id,
2061 						   cmd->check_iotlb.iotlb);
2062 	case IOMMU_TEST_OP_DEV_CHECK_CACHE:
2063 		return iommufd_test_dev_check_cache(ucmd, cmd->id,
2064 						    cmd->check_dev_cache.id,
2065 						    cmd->check_dev_cache.cache);
2066 	case IOMMU_TEST_OP_CREATE_ACCESS:
2067 		return iommufd_test_create_access(ucmd, cmd->id,
2068 						  cmd->create_access.flags);
2069 	case IOMMU_TEST_OP_ACCESS_REPLACE_IOAS:
2070 		return iommufd_test_access_replace_ioas(
2071 			ucmd, cmd->id, cmd->access_replace_ioas.ioas_id);
2072 	case IOMMU_TEST_OP_ACCESS_PAGES:
2073 		return iommufd_test_access_pages(
2074 			ucmd, cmd->id, cmd->access_pages.iova,
2075 			cmd->access_pages.length,
2076 			u64_to_user_ptr(cmd->access_pages.uptr),
2077 			cmd->access_pages.flags);
2078 	case IOMMU_TEST_OP_ACCESS_RW:
2079 		return iommufd_test_access_rw(
2080 			ucmd, cmd->id, cmd->access_rw.iova,
2081 			cmd->access_rw.length,
2082 			u64_to_user_ptr(cmd->access_rw.uptr),
2083 			cmd->access_rw.flags);
2084 	case IOMMU_TEST_OP_DESTROY_ACCESS_PAGES:
2085 		return iommufd_test_access_item_destroy(
2086 			ucmd, cmd->id, cmd->destroy_access_pages.access_pages_id);
2087 	case IOMMU_TEST_OP_SET_TEMP_MEMORY_LIMIT:
2088 		/* Protect _batch_init(), can not be less than elmsz */
2089 		if (cmd->memory_limit.limit <
2090 		    sizeof(unsigned long) + sizeof(u32))
2091 			return -EINVAL;
2092 		iommufd_test_memory_limit = cmd->memory_limit.limit;
2093 		return 0;
2094 	case IOMMU_TEST_OP_DIRTY:
2095 		return iommufd_test_dirty(ucmd, cmd->id, cmd->dirty.iova,
2096 					  cmd->dirty.length,
2097 					  cmd->dirty.page_size,
2098 					  u64_to_user_ptr(cmd->dirty.uptr),
2099 					  cmd->dirty.flags);
2100 	case IOMMU_TEST_OP_TRIGGER_IOPF:
2101 		return iommufd_test_trigger_iopf(ucmd, cmd);
2102 	case IOMMU_TEST_OP_TRIGGER_VEVENT:
2103 		return iommufd_test_trigger_vevent(ucmd, cmd);
2104 	case IOMMU_TEST_OP_PASID_ATTACH:
2105 		return iommufd_test_pasid_attach(ucmd, cmd);
2106 	case IOMMU_TEST_OP_PASID_REPLACE:
2107 		return iommufd_test_pasid_replace(ucmd, cmd);
2108 	case IOMMU_TEST_OP_PASID_DETACH:
2109 		return iommufd_test_pasid_detach(ucmd, cmd);
2110 	case IOMMU_TEST_OP_PASID_CHECK_HWPT:
2111 		return iommufd_test_pasid_check_hwpt(ucmd, cmd);
2112 	default:
2113 		return -EOPNOTSUPP;
2114 	}
2115 }
2116 
iommufd_should_fail(void)2117 bool iommufd_should_fail(void)
2118 {
2119 	return should_fail(&fail_iommufd, 1);
2120 }
2121 
iommufd_test_init(void)2122 int __init iommufd_test_init(void)
2123 {
2124 	struct platform_device_info pdevinfo = {
2125 		.name = "iommufd_selftest_iommu",
2126 	};
2127 	int rc;
2128 
2129 	dbgfs_root =
2130 		fault_create_debugfs_attr("fail_iommufd", NULL, &fail_iommufd);
2131 
2132 	selftest_iommu_dev = platform_device_register_full(&pdevinfo);
2133 	if (IS_ERR(selftest_iommu_dev)) {
2134 		rc = PTR_ERR(selftest_iommu_dev);
2135 		goto err_dbgfs;
2136 	}
2137 
2138 	rc = bus_register(&iommufd_mock_bus_type.bus);
2139 	if (rc)
2140 		goto err_platform;
2141 
2142 	rc = iommu_device_sysfs_add(&mock_iommu.iommu_dev,
2143 				    &selftest_iommu_dev->dev, NULL, "%s",
2144 				    dev_name(&selftest_iommu_dev->dev));
2145 	if (rc)
2146 		goto err_bus;
2147 
2148 	rc = iommu_device_register_bus(&mock_iommu.iommu_dev, &mock_ops,
2149 				       &iommufd_mock_bus_type.bus,
2150 				       &iommufd_mock_bus_type.nb);
2151 	if (rc)
2152 		goto err_sysfs;
2153 
2154 	refcount_set(&mock_iommu.users, 1);
2155 	init_completion(&mock_iommu.complete);
2156 
2157 	mock_iommu_iopf_queue = iopf_queue_alloc("mock-iopfq");
2158 	mock_iommu.iommu_dev.max_pasids = (1 << MOCK_PASID_WIDTH);
2159 
2160 	return 0;
2161 
2162 err_sysfs:
2163 	iommu_device_sysfs_remove(&mock_iommu.iommu_dev);
2164 err_bus:
2165 	bus_unregister(&iommufd_mock_bus_type.bus);
2166 err_platform:
2167 	platform_device_unregister(selftest_iommu_dev);
2168 err_dbgfs:
2169 	debugfs_remove_recursive(dbgfs_root);
2170 	return rc;
2171 }
2172 
iommufd_test_wait_for_users(void)2173 static void iommufd_test_wait_for_users(void)
2174 {
2175 	if (refcount_dec_and_test(&mock_iommu.users))
2176 		return;
2177 	/*
2178 	 * Time out waiting for iommu device user count to become 0.
2179 	 *
2180 	 * Note that this is just making an example here, since the selftest is
2181 	 * built into the iommufd module, i.e. it only unplugs the iommu device
2182 	 * when unloading the module. So, it is expected that this WARN_ON will
2183 	 * not trigger, as long as any iommufd FDs are open.
2184 	 */
2185 	WARN_ON(!wait_for_completion_timeout(&mock_iommu.complete,
2186 					     msecs_to_jiffies(10000)));
2187 }
2188 
iommufd_test_exit(void)2189 void iommufd_test_exit(void)
2190 {
2191 	if (mock_iommu_iopf_queue) {
2192 		iopf_queue_free(mock_iommu_iopf_queue);
2193 		mock_iommu_iopf_queue = NULL;
2194 	}
2195 
2196 	iommufd_test_wait_for_users();
2197 	iommu_device_sysfs_remove(&mock_iommu.iommu_dev);
2198 	iommu_device_unregister_bus(&mock_iommu.iommu_dev,
2199 				    &iommufd_mock_bus_type.bus,
2200 				    &iommufd_mock_bus_type.nb);
2201 	bus_unregister(&iommufd_mock_bus_type.bus);
2202 	platform_device_unregister(selftest_iommu_dev);
2203 	debugfs_remove_recursive(dbgfs_root);
2204 }
2205