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