summaryrefslogtreecommitdiff
path: root/drivers/iommu
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iommu')
-rw-r--r--drivers/iommu/iommufd/device.c149
-rw-r--r--drivers/iommu/iommufd/hw_pagetable.c70
-rw-r--r--drivers/iommu/iommufd/iommufd_private.h35
-rw-r--r--drivers/iommu/iommufd/iommufd_test.h2
-rw-r--r--drivers/iommu/iommufd/selftest.c207
5 files changed, 313 insertions, 150 deletions
diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c
index 4b565a5b51da..4f9b2142274c 100644
--- a/drivers/iommu/iommufd/device.c
+++ b/drivers/iommu/iommufd/device.c
@@ -15,23 +15,6 @@ MODULE_PARM_DESC(
"Allow IOMMUFD to bind to devices even if the platform cannot isolate "
"the MSI interrupt window. Enabling this is a security weakness.");
-/*
- * A iommufd_device object represents the binding relationship between a
- * consuming driver and the iommufd. These objects are created/destroyed by
- * external drivers, not by userspace.
- */
-struct iommufd_device {
- struct iommufd_object obj;
- struct iommufd_ctx *ictx;
- struct iommufd_hw_pagetable *hwpt;
- /* Head at iommufd_hw_pagetable::devices */
- struct list_head devices_item;
- /* always the physical device */
- struct device *dev;
- struct iommu_group *group;
- bool enforce_cache_coherency;
-};
-
void iommufd_device_destroy(struct iommufd_object *obj)
{
struct iommufd_device *idev =
@@ -39,7 +22,8 @@ void iommufd_device_destroy(struct iommufd_object *obj)
iommu_device_release_dma_owner(idev->dev);
iommu_group_put(idev->group);
- iommufd_ctx_put(idev->ictx);
+ if (!iommufd_selftest_is_mock_dev(idev->dev))
+ iommufd_ctx_put(idev->ictx);
}
/**
@@ -86,7 +70,8 @@ struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx,
goto out_release_owner;
}
idev->ictx = ictx;
- iommufd_ctx_get(ictx);
+ if (!iommufd_selftest_is_mock_dev(dev))
+ iommufd_ctx_get(ictx);
idev->dev = dev;
idev->enforce_cache_coherency =
device_iommu_capable(dev, IOMMU_CAP_ENFORCE_CACHE_COHERENCY);
@@ -168,7 +153,8 @@ static int iommufd_device_setup_msi(struct iommufd_device *idev,
* operation from the device (eg a simple DMA) cannot trigger an
* interrupt outside this iommufd context.
*/
- if (!iommu_group_has_isolated_msi(idev->group)) {
+ if (!iommufd_selftest_is_mock_dev(idev->dev) &&
+ !iommu_group_has_isolated_msi(idev->group)) {
if (!allow_unsafe_interrupts)
return -EPERM;
@@ -186,19 +172,24 @@ static bool iommufd_hw_pagetable_has_group(struct iommufd_hw_pagetable *hwpt,
{
struct iommufd_device *cur_dev;
+ lockdep_assert_held(&hwpt->devices_lock);
+
list_for_each_entry(cur_dev, &hwpt->devices, devices_item)
if (cur_dev->group == group)
return true;
return false;
}
-static int iommufd_device_do_attach(struct iommufd_device *idev,
- struct iommufd_hw_pagetable *hwpt)
+int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
+ struct iommufd_device *idev)
{
phys_addr_t sw_msi_start = PHYS_ADDR_MAX;
int rc;
- mutex_lock(&hwpt->devices_lock);
+ lockdep_assert_held(&hwpt->devices_lock);
+
+ if (WARN_ON(idev->hwpt))
+ return -EINVAL;
/*
* Try to upgrade the domain we have, it is an iommu driver bug to
@@ -213,19 +204,18 @@ static int iommufd_device_do_attach(struct iommufd_device *idev,
hwpt->domain);
if (!hwpt->enforce_cache_coherency) {
WARN_ON(list_empty(&hwpt->devices));
- rc = -EINVAL;
- goto out_unlock;
+ return -EINVAL;
}
}
rc = iopt_table_enforce_group_resv_regions(&hwpt->ioas->iopt, idev->dev,
idev->group, &sw_msi_start);
if (rc)
- goto out_unlock;
+ return rc;
rc = iommufd_device_setup_msi(idev, hwpt, sw_msi_start);
if (rc)
- goto out_iova;
+ goto err_unresv;
/*
* FIXME: Hack around missing a device-centric iommu api, only attach to
@@ -234,26 +224,35 @@ static int iommufd_device_do_attach(struct iommufd_device *idev,
if (!iommufd_hw_pagetable_has_group(hwpt, idev->group)) {
rc = iommu_attach_group(hwpt->domain, idev->group);
if (rc)
- goto out_iova;
-
- if (list_empty(&hwpt->devices)) {
- rc = iopt_table_add_domain(&hwpt->ioas->iopt,
- hwpt->domain);
- if (rc)
- goto out_detach;
- }
+ goto err_unresv;
}
+ return 0;
+err_unresv:
+ iopt_remove_reserved_iova(&hwpt->ioas->iopt, idev->dev);
+ return rc;
+}
+
+void iommufd_hw_pagetable_detach(struct iommufd_hw_pagetable *hwpt,
+ struct iommufd_device *idev)
+{
+ if (!iommufd_hw_pagetable_has_group(hwpt, idev->group))
+ iommu_detach_group(hwpt->domain, idev->group);
+ iopt_remove_reserved_iova(&hwpt->ioas->iopt, idev->dev);
+}
+
+static int iommufd_device_do_attach(struct iommufd_device *idev,
+ struct iommufd_hw_pagetable *hwpt)
+{
+ int rc;
+
+ mutex_lock(&hwpt->devices_lock);
+ rc = iommufd_hw_pagetable_attach(hwpt, idev);
+ if (rc)
+ goto out_unlock;
idev->hwpt = hwpt;
refcount_inc(&hwpt->obj.users);
list_add(&idev->devices_item, &hwpt->devices);
- mutex_unlock(&hwpt->devices_lock);
- return 0;
-
-out_detach:
- iommu_detach_group(hwpt->domain, idev->group);
-out_iova:
- iopt_remove_reserved_iova(&hwpt->ioas->iopt, idev->dev);
out_unlock:
mutex_unlock(&hwpt->devices_lock);
return rc;
@@ -280,7 +279,10 @@ static int iommufd_device_auto_get_domain(struct iommufd_device *idev,
if (!hwpt->auto_domain)
continue;
+ if (!iommufd_lock_obj(&hwpt->obj))
+ continue;
rc = iommufd_device_do_attach(idev, hwpt);
+ iommufd_put_object(&hwpt->obj);
/*
* -EINVAL means the domain is incompatible with the device.
@@ -292,24 +294,16 @@ static int iommufd_device_auto_get_domain(struct iommufd_device *idev,
goto out_unlock;
}
- hwpt = iommufd_hw_pagetable_alloc(idev->ictx, ioas, idev->dev);
+ hwpt = iommufd_hw_pagetable_alloc(idev->ictx, ioas, idev, true);
if (IS_ERR(hwpt)) {
rc = PTR_ERR(hwpt);
goto out_unlock;
}
hwpt->auto_domain = true;
- rc = iommufd_device_do_attach(idev, hwpt);
- if (rc)
- goto out_abort;
- list_add_tail(&hwpt->hwpt_item, &ioas->hwpt_list);
-
mutex_unlock(&ioas->mutex);
iommufd_object_finalize(idev->ictx, &hwpt->obj);
return 0;
-
-out_abort:
- iommufd_object_abort_and_destroy(idev->ictx, &hwpt->obj);
out_unlock:
mutex_unlock(&ioas->mutex);
return rc;
@@ -381,28 +375,17 @@ void iommufd_device_detach(struct iommufd_device *idev)
{
struct iommufd_hw_pagetable *hwpt = idev->hwpt;
- mutex_lock(&hwpt->ioas->mutex);
mutex_lock(&hwpt->devices_lock);
list_del(&idev->devices_item);
- if (!iommufd_hw_pagetable_has_group(hwpt, idev->group)) {
- if (list_empty(&hwpt->devices)) {
- iopt_table_remove_domain(&hwpt->ioas->iopt,
- hwpt->domain);
- list_del(&hwpt->hwpt_item);
- }
- iommu_detach_group(hwpt->domain, idev->group);
- }
- iopt_remove_reserved_iova(&hwpt->ioas->iopt, idev->dev);
+ idev->hwpt = NULL;
+ iommufd_hw_pagetable_detach(hwpt, idev);
mutex_unlock(&hwpt->devices_lock);
- mutex_unlock(&hwpt->ioas->mutex);
if (hwpt->auto_domain)
iommufd_object_destroy_user(idev->ictx, &hwpt->obj);
else
refcount_dec(&hwpt->obj.users);
- idev->hwpt = NULL;
-
refcount_dec(&idev->obj.users);
}
EXPORT_SYMBOL_NS_GPL(iommufd_device_detach, IOMMUFD);
@@ -736,41 +719,3 @@ err_out:
return rc;
}
EXPORT_SYMBOL_NS_GPL(iommufd_access_rw, IOMMUFD);
-
-#ifdef CONFIG_IOMMUFD_TEST
-/*
- * Creating a real iommufd_device is too hard, bypass creating a iommufd_device
- * and go directly to attaching a domain.
- */
-struct iommufd_hw_pagetable *
-iommufd_device_selftest_attach(struct iommufd_ctx *ictx,
- struct iommufd_ioas *ioas,
- struct device *mock_dev)
-{
- struct iommufd_hw_pagetable *hwpt;
- int rc;
-
- hwpt = iommufd_hw_pagetable_alloc(ictx, ioas, mock_dev);
- if (IS_ERR(hwpt))
- return hwpt;
-
- rc = iopt_table_add_domain(&hwpt->ioas->iopt, hwpt->domain);
- if (rc)
- goto out_hwpt;
-
- refcount_inc(&hwpt->obj.users);
- iommufd_object_finalize(ictx, &hwpt->obj);
- return hwpt;
-
-out_hwpt:
- iommufd_object_abort_and_destroy(ictx, &hwpt->obj);
- return ERR_PTR(rc);
-}
-
-void iommufd_device_selftest_detach(struct iommufd_ctx *ictx,
- struct iommufd_hw_pagetable *hwpt)
-{
- iopt_table_remove_domain(&hwpt->ioas->iopt, hwpt->domain);
- refcount_dec(&hwpt->obj.users);
-}
-#endif
diff --git a/drivers/iommu/iommufd/hw_pagetable.c b/drivers/iommu/iommufd/hw_pagetable.c
index 43d473989a06..6cdb6749d359 100644
--- a/drivers/iommu/iommufd/hw_pagetable.c
+++ b/drivers/iommu/iommufd/hw_pagetable.c
@@ -13,7 +13,17 @@ void iommufd_hw_pagetable_destroy(struct iommufd_object *obj)
WARN_ON(!list_empty(&hwpt->devices));
- iommu_domain_free(hwpt->domain);
+ if (!list_empty(&hwpt->hwpt_item)) {
+ mutex_lock(&hwpt->ioas->mutex);
+ list_del(&hwpt->hwpt_item);
+ mutex_unlock(&hwpt->ioas->mutex);
+
+ iopt_table_remove_domain(&hwpt->ioas->iopt, hwpt->domain);
+ }
+
+ if (hwpt->domain)
+ iommu_domain_free(hwpt->domain);
+
refcount_dec(&hwpt->ioas->obj.users);
mutex_destroy(&hwpt->devices_lock);
}
@@ -22,36 +32,74 @@ void iommufd_hw_pagetable_destroy(struct iommufd_object *obj)
* iommufd_hw_pagetable_alloc() - Get an iommu_domain for a device
* @ictx: iommufd context
* @ioas: IOAS to associate the domain with
- * @dev: Device to get an iommu_domain for
+ * @idev: Device to get an iommu_domain for
+ * @immediate_attach: True if idev should be attached to the hwpt
*
- * Allocate a new iommu_domain and return it as a hw_pagetable.
+ * Allocate a new iommu_domain and return it as a hw_pagetable. The HWPT
+ * will be linked to the given ioas and upon return the underlying iommu_domain
+ * is fully popoulated.
*/
struct iommufd_hw_pagetable *
iommufd_hw_pagetable_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas,
- struct device *dev)
+ struct iommufd_device *idev, bool immediate_attach)
{
struct iommufd_hw_pagetable *hwpt;
int rc;
+ lockdep_assert_held(&ioas->mutex);
+
hwpt = iommufd_object_alloc(ictx, hwpt, IOMMUFD_OBJ_HW_PAGETABLE);
if (IS_ERR(hwpt))
return hwpt;
- hwpt->domain = iommu_domain_alloc(dev->bus);
- if (!hwpt->domain) {
- rc = -ENOMEM;
- goto out_abort;
- }
-
INIT_LIST_HEAD(&hwpt->devices);
INIT_LIST_HEAD(&hwpt->hwpt_item);
mutex_init(&hwpt->devices_lock);
/* Pairs with iommufd_hw_pagetable_destroy() */
refcount_inc(&ioas->obj.users);
hwpt->ioas = ioas;
+
+ hwpt->domain = iommu_domain_alloc(idev->dev->bus);
+ if (!hwpt->domain) {
+ rc = -ENOMEM;
+ goto out_abort;
+ }
+
+ mutex_lock(&hwpt->devices_lock);
+
+ /*
+ * immediate_attach exists only to accommodate iommu drivers that cannot
+ * directly allocate a domain. These drivers do not finish creating the
+ * domain until attach is completed. Thus we must have this call
+ * sequence. Once those drivers are fixed this should be removed.
+ */
+ if (immediate_attach) {
+ rc = iommufd_hw_pagetable_attach(hwpt, idev);
+ if (rc)
+ goto out_unlock;
+ }
+
+ rc = iopt_table_add_domain(&hwpt->ioas->iopt, hwpt->domain);
+ if (rc)
+ goto out_detach;
+ list_add_tail(&hwpt->hwpt_item, &hwpt->ioas->hwpt_list);
+
+ if (immediate_attach) {
+ /* See iommufd_device_do_attach() */
+ refcount_inc(&hwpt->obj.users);
+ idev->hwpt = hwpt;
+ list_add(&idev->devices_item, &hwpt->devices);
+ }
+
+ mutex_unlock(&hwpt->devices_lock);
return hwpt;
+out_detach:
+ if (immediate_attach)
+ iommufd_hw_pagetable_detach(hwpt, idev);
+out_unlock:
+ mutex_unlock(&hwpt->devices_lock);
out_abort:
- iommufd_object_abort(ictx, &hwpt->obj);
+ iommufd_object_abort_and_destroy(ictx, &hwpt->obj);
return ERR_PTR(rc);
}
diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
index 2e6e8e217cce..b38e67d1988b 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -12,6 +12,7 @@
struct iommu_domain;
struct iommu_group;
struct iommu_option;
+struct iommufd_device;
struct iommufd_ctx {
struct file *file;
@@ -254,9 +255,30 @@ struct iommufd_hw_pagetable {
struct iommufd_hw_pagetable *
iommufd_hw_pagetable_alloc(struct iommufd_ctx *ictx, struct iommufd_ioas *ioas,
- struct device *dev);
+ struct iommufd_device *idev, bool immediate_attach);
+int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
+ struct iommufd_device *idev);
+void iommufd_hw_pagetable_detach(struct iommufd_hw_pagetable *hwpt,
+ struct iommufd_device *idev);
void iommufd_hw_pagetable_destroy(struct iommufd_object *obj);
+/*
+ * A iommufd_device object represents the binding relationship between a
+ * consuming driver and the iommufd. These objects are created/destroyed by
+ * external drivers, not by userspace.
+ */
+struct iommufd_device {
+ struct iommufd_object obj;
+ struct iommufd_ctx *ictx;
+ struct iommufd_hw_pagetable *hwpt;
+ /* Head at iommufd_hw_pagetable::devices */
+ struct list_head devices_item;
+ /* always the physical device */
+ struct device *dev;
+ struct iommu_group *group;
+ bool enforce_cache_coherency;
+};
+
void iommufd_device_destroy(struct iommufd_object *obj);
struct iommufd_access {
@@ -275,12 +297,6 @@ void iopt_remove_access(struct io_pagetable *iopt,
void iommufd_access_destroy_object(struct iommufd_object *obj);
#ifdef CONFIG_IOMMUFD_TEST
-struct iommufd_hw_pagetable *
-iommufd_device_selftest_attach(struct iommufd_ctx *ictx,
- struct iommufd_ioas *ioas,
- struct device *mock_dev);
-void iommufd_device_selftest_detach(struct iommufd_ctx *ictx,
- struct iommufd_hw_pagetable *hwpt);
int iommufd_test(struct iommufd_ucmd *ucmd);
void iommufd_selftest_destroy(struct iommufd_object *obj);
extern size_t iommufd_test_memory_limit;
@@ -289,6 +305,7 @@ void iommufd_test_syz_conv_iova_id(struct iommufd_ucmd *ucmd,
bool iommufd_should_fail(void);
void __init iommufd_test_init(void);
void iommufd_test_exit(void);
+bool iommufd_selftest_is_mock_dev(struct device *dev);
#else
static inline void iommufd_test_syz_conv_iova_id(struct iommufd_ucmd *ucmd,
unsigned int ioas_id,
@@ -305,5 +322,9 @@ static inline void __init iommufd_test_init(void)
static inline void iommufd_test_exit(void)
{
}
+static inline bool iommufd_selftest_is_mock_dev(struct device *dev)
+{
+ return false;
+}
#endif
#endif
diff --git a/drivers/iommu/iommufd/iommufd_test.h b/drivers/iommu/iommufd/iommufd_test.h
index 1d96a8f466fd..b3d69cca7729 100644
--- a/drivers/iommu/iommufd/iommufd_test.h
+++ b/drivers/iommu/iommufd/iommufd_test.h
@@ -49,7 +49,7 @@ struct iommu_test_cmd {
__aligned_u64 length;
} add_reserved;
struct {
- __u32 out_device_id;
+ __u32 out_stdev_id;
__u32 out_hwpt_id;
} mock_domain;
struct {
diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c
index e6e04dceffe3..4463060ec84b 100644
--- a/drivers/iommu/iommufd/selftest.c
+++ b/drivers/iommu/iommufd/selftest.c
@@ -91,23 +91,50 @@ enum selftest_obj_type {
TYPE_IDEV,
};
+struct mock_dev {
+ struct device dev;
+};
+
struct selftest_obj {
struct iommufd_object obj;
enum selftest_obj_type type;
union {
struct {
- struct iommufd_hw_pagetable *hwpt;
+ struct iommufd_device *idev;
struct iommufd_ctx *ictx;
- struct device mock_dev;
+ struct mock_dev *mock_dev;
} idev;
};
};
+static void mock_domain_blocking_free(struct iommu_domain *domain)
+{
+}
+
+static int mock_domain_nop_attach(struct iommu_domain *domain,
+ struct device *dev)
+{
+ return 0;
+}
+
+static const struct iommu_domain_ops mock_blocking_ops = {
+ .free = mock_domain_blocking_free,
+ .attach_dev = mock_domain_nop_attach,
+};
+
+static struct iommu_domain mock_blocking_domain = {
+ .type = IOMMU_DOMAIN_BLOCKED,
+ .ops = &mock_blocking_ops,
+};
+
static struct iommu_domain *mock_domain_alloc(unsigned int iommu_domain_type)
{
struct mock_iommu_domain *mock;
+ if (iommu_domain_type == IOMMU_DOMAIN_BLOCKED)
+ return &mock_blocking_domain;
+
if (WARN_ON(iommu_domain_type != IOMMU_DOMAIN_UNMANAGED))
return NULL;
@@ -236,19 +263,39 @@ static phys_addr_t mock_domain_iova_to_phys(struct iommu_domain *domain,
return (xa_to_value(ent) & MOCK_PFN_MASK) * MOCK_IO_PAGE_SIZE;
}
+static bool mock_domain_capable(struct device *dev, enum iommu_cap cap)
+{
+ return cap == IOMMU_CAP_CACHE_COHERENCY;
+}
+
+static void mock_domain_set_plaform_dma_ops(struct device *dev)
+{
+ /*
+ * mock doesn't setup default domains because we can't hook into the
+ * normal probe path
+ */
+}
+
static const struct iommu_ops mock_ops = {
.owner = THIS_MODULE,
.pgsize_bitmap = MOCK_IO_PAGE_SIZE,
.domain_alloc = mock_domain_alloc,
+ .capable = mock_domain_capable,
+ .set_platform_dma_ops = mock_domain_set_plaform_dma_ops,
.default_domain_ops =
&(struct iommu_domain_ops){
.free = mock_domain_free,
+ .attach_dev = mock_domain_nop_attach,
.map_pages = mock_domain_map_pages,
.unmap_pages = mock_domain_unmap_pages,
.iova_to_phys = mock_domain_iova_to_phys,
},
};
+struct iommu_device mock_iommu_device = {
+ .ops = &mock_ops,
+};
+
static inline struct iommufd_hw_pagetable *
get_md_pagetable(struct iommufd_ucmd *ucmd, u32 mockpt_id,
struct mock_iommu_domain **mock)
@@ -269,48 +316,142 @@ get_md_pagetable(struct iommufd_ucmd *ucmd, u32 mockpt_id,
return hwpt;
}
+static struct bus_type iommufd_mock_bus_type = {
+ .name = "iommufd_mock",
+ .iommu_ops = &mock_ops,
+};
+
+static void mock_dev_release(struct device *dev)
+{
+ struct mock_dev *mdev = container_of(dev, struct mock_dev, dev);
+
+ kfree(mdev);
+}
+
+static struct mock_dev *mock_dev_create(void)
+{
+ struct iommu_group *iommu_group;
+ struct dev_iommu *dev_iommu;
+ struct mock_dev *mdev;
+ int rc;
+
+ mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
+ if (!mdev)
+ return ERR_PTR(-ENOMEM);
+
+ device_initialize(&mdev->dev);
+ mdev->dev.release = mock_dev_release;
+ mdev->dev.bus = &iommufd_mock_bus_type;
+
+ iommu_group = iommu_group_alloc();
+ if (IS_ERR(iommu_group)) {
+ rc = PTR_ERR(iommu_group);
+ goto err_put;
+ }
+
+ rc = dev_set_name(&mdev->dev, "iommufd_mock%u",
+ iommu_group_id(iommu_group));
+ if (rc)
+ goto err_group;
+
+ /*
+ * The iommu core has no way to associate a single device with an iommu
+ * driver (heck currently it can't even support two iommu_drivers
+ * registering). Hack it together with an open coded dev_iommu_get().
+ * Notice that the normal notifier triggered iommu release process also
+ * does not work here because this bus is not in iommu_buses.
+ */
+ mdev->dev.iommu = kzalloc(sizeof(*dev_iommu), GFP_KERNEL);
+ if (!mdev->dev.iommu) {
+ rc = -ENOMEM;
+ goto err_group;
+ }
+ mutex_init(&mdev->dev.iommu->lock);
+ mdev->dev.iommu->iommu_dev = &mock_iommu_device;
+
+ rc = device_add(&mdev->dev);
+ if (rc)
+ goto err_dev_iommu;
+
+ rc = iommu_group_add_device(iommu_group, &mdev->dev);
+ if (rc)
+ goto err_del;
+ iommu_group_put(iommu_group);
+ return mdev;
+
+err_del:
+ device_del(&mdev->dev);
+err_dev_iommu:
+ kfree(mdev->dev.iommu);
+ mdev->dev.iommu = NULL;
+err_group:
+ iommu_group_put(iommu_group);
+err_put:
+ put_device(&mdev->dev);
+ return ERR_PTR(rc);
+}
+
+static void mock_dev_destroy(struct mock_dev *mdev)
+{
+ iommu_group_remove_device(&mdev->dev);
+ device_del(&mdev->dev);
+ kfree(mdev->dev.iommu);
+ mdev->dev.iommu = NULL;
+ put_device(&mdev->dev);
+}
+
+bool iommufd_selftest_is_mock_dev(struct device *dev)
+{
+ return dev->release == mock_dev_release;
+}
+
/* Create an hw_pagetable with the mock domain so we can test the domain ops */
static int iommufd_test_mock_domain(struct iommufd_ucmd *ucmd,
struct iommu_test_cmd *cmd)
{
- static struct bus_type mock_bus = { .iommu_ops = &mock_ops };
- struct iommufd_hw_pagetable *hwpt;
+ struct iommufd_device *idev;
struct selftest_obj *sobj;
- struct iommufd_ioas *ioas;
+ u32 pt_id = cmd->id;
+ u32 idev_id;
int rc;
- ioas = iommufd_get_ioas(ucmd->ictx, cmd->id);
- if (IS_ERR(ioas))
- return PTR_ERR(ioas);
-
sobj = iommufd_object_alloc(ucmd->ictx, sobj, IOMMUFD_OBJ_SELFTEST);
- if (IS_ERR(sobj)) {
- rc = PTR_ERR(sobj);
- goto out_ioas;
- }
+ if (IS_ERR(sobj))
+ return PTR_ERR(sobj);
+
sobj->idev.ictx = ucmd->ictx;
sobj->type = TYPE_IDEV;
- sobj->idev.mock_dev.bus = &mock_bus;
- hwpt = iommufd_device_selftest_attach(ucmd->ictx, ioas,
- &sobj->idev.mock_dev);
- if (IS_ERR(hwpt)) {
- rc = PTR_ERR(hwpt);
+ sobj->idev.mock_dev = mock_dev_create();
+ if (IS_ERR(sobj->idev.mock_dev)) {
+ rc = PTR_ERR(sobj->idev.mock_dev);
goto out_sobj;
}
- sobj->idev.hwpt = hwpt;
- /* Userspace must destroy both of these IDs to destroy the object */
- cmd->mock_domain.out_hwpt_id = hwpt->obj.id;
- cmd->mock_domain.out_device_id = sobj->obj.id;
+ idev = iommufd_device_bind(ucmd->ictx, &sobj->idev.mock_dev->dev,
+ &idev_id);
+ if (IS_ERR(idev)) {
+ rc = PTR_ERR(idev);
+ goto out_mdev;
+ }
+ sobj->idev.idev = idev;
+
+ rc = iommufd_device_attach(idev, &pt_id);
+ if (rc)
+ goto out_unbind;
+
+ /* Userspace must destroy the device_id to destroy the object */
+ cmd->mock_domain.out_hwpt_id = pt_id;
+ cmd->mock_domain.out_stdev_id = sobj->obj.id;
iommufd_object_finalize(ucmd->ictx, &sobj->obj);
- iommufd_put_object(&ioas->obj);
return iommufd_ucmd_respond(ucmd, sizeof(*cmd));
+out_unbind:
+ iommufd_device_unbind(idev);
+out_mdev:
+ mock_dev_destroy(sobj->idev.mock_dev);
out_sobj:
iommufd_object_abort(ucmd->ictx, &sobj->obj);
-out_ioas:
- iommufd_put_object(&ioas->obj);
return rc;
}
@@ -339,10 +480,12 @@ static int iommufd_test_md_check_pa(struct iommufd_ucmd *ucmd,
{
struct iommufd_hw_pagetable *hwpt;
struct mock_iommu_domain *mock;
+ uintptr_t end;
int rc;
if (iova % MOCK_IO_PAGE_SIZE || length % MOCK_IO_PAGE_SIZE ||
- (uintptr_t)uptr % MOCK_IO_PAGE_SIZE)
+ (uintptr_t)uptr % MOCK_IO_PAGE_SIZE ||
+ check_add_overflow((uintptr_t)uptr, (uintptr_t)length, &end))
return -EINVAL;
hwpt = get_md_pagetable(ucmd, mockpt_id, &mock);
@@ -390,7 +533,10 @@ static int iommufd_test_md_check_refs(struct iommufd_ucmd *ucmd,
void __user *uptr, size_t length,
unsigned int refs)
{
- if (length % PAGE_SIZE || (uintptr_t)uptr % PAGE_SIZE)
+ uintptr_t end;
+
+ if (length % PAGE_SIZE || (uintptr_t)uptr % PAGE_SIZE ||
+ check_add_overflow((uintptr_t)uptr, (uintptr_t)length, &end))
return -EINVAL;
for (; length; length -= PAGE_SIZE) {
@@ -784,8 +930,9 @@ void iommufd_selftest_destroy(struct iommufd_object *obj)
switch (sobj->type) {
case TYPE_IDEV:
- iommufd_device_selftest_detach(sobj->idev.ictx,
- sobj->idev.hwpt);
+ iommufd_device_detach(sobj->idev.idev);
+ iommufd_device_unbind(sobj->idev.idev);
+ mock_dev_destroy(sobj->idev.mock_dev);
break;
}
}
@@ -849,9 +996,11 @@ void __init iommufd_test_init(void)
{
dbgfs_root =
fault_create_debugfs_attr("fail_iommufd", NULL, &fail_iommufd);
+ WARN_ON(bus_register(&iommufd_mock_bus_type));
}
void iommufd_test_exit(void)
{
debugfs_remove_recursive(dbgfs_root);
+ bus_unregister(&iommufd_mock_bus_type);
}