1e8d57210SJason Gunthorpe // SPDX-License-Identifier: GPL-2.0-only
2e8d57210SJason Gunthorpe /* Copyright (c) 2021-2022, NVIDIA CORPORATION & AFFILIATES
3e8d57210SJason Gunthorpe */
4e8d57210SJason Gunthorpe #include <linux/iommufd.h>
5e8d57210SJason Gunthorpe #include <linux/slab.h>
6e8d57210SJason Gunthorpe #include <linux/iommu.h>
7*55dd4023SYi Liu #include <uapi/linux/iommufd.h>
8e88d4ec1SJason Gunthorpe #include "../iommu-priv.h"
9e8d57210SJason Gunthorpe
108d40205fSJason Gunthorpe #include "io_pagetable.h"
11e8d57210SJason Gunthorpe #include "iommufd_private.h"
12e8d57210SJason Gunthorpe
13e8d57210SJason Gunthorpe static bool allow_unsafe_interrupts;
14e8d57210SJason Gunthorpe module_param(allow_unsafe_interrupts, bool, S_IRUGO | S_IWUSR);
15e8d57210SJason Gunthorpe MODULE_PARM_DESC(
16e8d57210SJason Gunthorpe allow_unsafe_interrupts,
17e8d57210SJason Gunthorpe "Allow IOMMUFD to bind to devices even if the platform cannot isolate "
18e8d57210SJason Gunthorpe "the MSI interrupt window. Enabling this is a security weakness.");
19e8d57210SJason Gunthorpe
iommufd_group_release(struct kref * kref)203a3329a7SJason Gunthorpe static void iommufd_group_release(struct kref *kref)
213a3329a7SJason Gunthorpe {
223a3329a7SJason Gunthorpe struct iommufd_group *igroup =
233a3329a7SJason Gunthorpe container_of(kref, struct iommufd_group, ref);
243a3329a7SJason Gunthorpe
2591a2e17eSJason Gunthorpe WARN_ON(igroup->hwpt || !list_empty(&igroup->device_list));
2691a2e17eSJason Gunthorpe
273a3329a7SJason Gunthorpe xa_cmpxchg(&igroup->ictx->groups, iommu_group_id(igroup->group), igroup,
283a3329a7SJason Gunthorpe NULL, GFP_KERNEL);
293a3329a7SJason Gunthorpe iommu_group_put(igroup->group);
3091a2e17eSJason Gunthorpe mutex_destroy(&igroup->lock);
313a3329a7SJason Gunthorpe kfree(igroup);
323a3329a7SJason Gunthorpe }
333a3329a7SJason Gunthorpe
iommufd_put_group(struct iommufd_group * group)343a3329a7SJason Gunthorpe static void iommufd_put_group(struct iommufd_group *group)
353a3329a7SJason Gunthorpe {
363a3329a7SJason Gunthorpe kref_put(&group->ref, iommufd_group_release);
373a3329a7SJason Gunthorpe }
383a3329a7SJason Gunthorpe
iommufd_group_try_get(struct iommufd_group * igroup,struct iommu_group * group)393a3329a7SJason Gunthorpe static bool iommufd_group_try_get(struct iommufd_group *igroup,
403a3329a7SJason Gunthorpe struct iommu_group *group)
413a3329a7SJason Gunthorpe {
423a3329a7SJason Gunthorpe if (!igroup)
433a3329a7SJason Gunthorpe return false;
443a3329a7SJason Gunthorpe /*
453a3329a7SJason Gunthorpe * group ID's cannot be re-used until the group is put back which does
463a3329a7SJason Gunthorpe * not happen if we could get an igroup pointer under the xa_lock.
473a3329a7SJason Gunthorpe */
483a3329a7SJason Gunthorpe if (WARN_ON(igroup->group != group))
493a3329a7SJason Gunthorpe return false;
503a3329a7SJason Gunthorpe return kref_get_unless_zero(&igroup->ref);
513a3329a7SJason Gunthorpe }
523a3329a7SJason Gunthorpe
533a3329a7SJason Gunthorpe /*
543a3329a7SJason Gunthorpe * iommufd needs to store some more data for each iommu_group, we keep a
553a3329a7SJason Gunthorpe * parallel xarray indexed by iommu_group id to hold this instead of putting it
563a3329a7SJason Gunthorpe * in the core structure. To keep things simple the iommufd_group memory is
573a3329a7SJason Gunthorpe * unique within the iommufd_ctx. This makes it easy to check there are no
583a3329a7SJason Gunthorpe * memory leaks.
593a3329a7SJason Gunthorpe */
iommufd_get_group(struct iommufd_ctx * ictx,struct device * dev)603a3329a7SJason Gunthorpe static struct iommufd_group *iommufd_get_group(struct iommufd_ctx *ictx,
613a3329a7SJason Gunthorpe struct device *dev)
623a3329a7SJason Gunthorpe {
633a3329a7SJason Gunthorpe struct iommufd_group *new_igroup;
643a3329a7SJason Gunthorpe struct iommufd_group *cur_igroup;
653a3329a7SJason Gunthorpe struct iommufd_group *igroup;
663a3329a7SJason Gunthorpe struct iommu_group *group;
673a3329a7SJason Gunthorpe unsigned int id;
683a3329a7SJason Gunthorpe
693a3329a7SJason Gunthorpe group = iommu_group_get(dev);
703a3329a7SJason Gunthorpe if (!group)
713a3329a7SJason Gunthorpe return ERR_PTR(-ENODEV);
723a3329a7SJason Gunthorpe
733a3329a7SJason Gunthorpe id = iommu_group_id(group);
743a3329a7SJason Gunthorpe
753a3329a7SJason Gunthorpe xa_lock(&ictx->groups);
763a3329a7SJason Gunthorpe igroup = xa_load(&ictx->groups, id);
773a3329a7SJason Gunthorpe if (iommufd_group_try_get(igroup, group)) {
783a3329a7SJason Gunthorpe xa_unlock(&ictx->groups);
793a3329a7SJason Gunthorpe iommu_group_put(group);
803a3329a7SJason Gunthorpe return igroup;
813a3329a7SJason Gunthorpe }
823a3329a7SJason Gunthorpe xa_unlock(&ictx->groups);
833a3329a7SJason Gunthorpe
843a3329a7SJason Gunthorpe new_igroup = kzalloc(sizeof(*new_igroup), GFP_KERNEL);
853a3329a7SJason Gunthorpe if (!new_igroup) {
863a3329a7SJason Gunthorpe iommu_group_put(group);
873a3329a7SJason Gunthorpe return ERR_PTR(-ENOMEM);
883a3329a7SJason Gunthorpe }
893a3329a7SJason Gunthorpe
903a3329a7SJason Gunthorpe kref_init(&new_igroup->ref);
9191a2e17eSJason Gunthorpe mutex_init(&new_igroup->lock);
9291a2e17eSJason Gunthorpe INIT_LIST_HEAD(&new_igroup->device_list);
931d149ab2SJason Gunthorpe new_igroup->sw_msi_start = PHYS_ADDR_MAX;
943a3329a7SJason Gunthorpe /* group reference moves into new_igroup */
953a3329a7SJason Gunthorpe new_igroup->group = group;
963a3329a7SJason Gunthorpe
973a3329a7SJason Gunthorpe /*
983a3329a7SJason Gunthorpe * The ictx is not additionally refcounted here becase all objects using
993a3329a7SJason Gunthorpe * an igroup must put it before their destroy completes.
1003a3329a7SJason Gunthorpe */
1013a3329a7SJason Gunthorpe new_igroup->ictx = ictx;
1023a3329a7SJason Gunthorpe
1033a3329a7SJason Gunthorpe /*
1043a3329a7SJason Gunthorpe * We dropped the lock so igroup is invalid. NULL is a safe and likely
1053a3329a7SJason Gunthorpe * value to assume for the xa_cmpxchg algorithm.
1063a3329a7SJason Gunthorpe */
1073a3329a7SJason Gunthorpe cur_igroup = NULL;
1083a3329a7SJason Gunthorpe xa_lock(&ictx->groups);
1093a3329a7SJason Gunthorpe while (true) {
1103a3329a7SJason Gunthorpe igroup = __xa_cmpxchg(&ictx->groups, id, cur_igroup, new_igroup,
1113a3329a7SJason Gunthorpe GFP_KERNEL);
1123a3329a7SJason Gunthorpe if (xa_is_err(igroup)) {
1133a3329a7SJason Gunthorpe xa_unlock(&ictx->groups);
1143a3329a7SJason Gunthorpe iommufd_put_group(new_igroup);
1153a3329a7SJason Gunthorpe return ERR_PTR(xa_err(igroup));
1163a3329a7SJason Gunthorpe }
1173a3329a7SJason Gunthorpe
1183a3329a7SJason Gunthorpe /* new_group was successfully installed */
1193a3329a7SJason Gunthorpe if (cur_igroup == igroup) {
1203a3329a7SJason Gunthorpe xa_unlock(&ictx->groups);
1213a3329a7SJason Gunthorpe return new_igroup;
1223a3329a7SJason Gunthorpe }
1233a3329a7SJason Gunthorpe
1243a3329a7SJason Gunthorpe /* Check again if the current group is any good */
1253a3329a7SJason Gunthorpe if (iommufd_group_try_get(igroup, group)) {
1263a3329a7SJason Gunthorpe xa_unlock(&ictx->groups);
1273a3329a7SJason Gunthorpe iommufd_put_group(new_igroup);
1283a3329a7SJason Gunthorpe return igroup;
1293a3329a7SJason Gunthorpe }
1303a3329a7SJason Gunthorpe cur_igroup = igroup;
1313a3329a7SJason Gunthorpe }
1323a3329a7SJason Gunthorpe }
1333a3329a7SJason Gunthorpe
iommufd_device_destroy(struct iommufd_object * obj)134e8d57210SJason Gunthorpe void iommufd_device_destroy(struct iommufd_object *obj)
135e8d57210SJason Gunthorpe {
136e8d57210SJason Gunthorpe struct iommufd_device *idev =
137e8d57210SJason Gunthorpe container_of(obj, struct iommufd_device, obj);
138e8d57210SJason Gunthorpe
139e8d57210SJason Gunthorpe iommu_device_release_dma_owner(idev->dev);
1403a3329a7SJason Gunthorpe iommufd_put_group(idev->igroup);
14165c619aeSJason Gunthorpe if (!iommufd_selftest_is_mock_dev(idev->dev))
142e8d57210SJason Gunthorpe iommufd_ctx_put(idev->ictx);
143e8d57210SJason Gunthorpe }
144e8d57210SJason Gunthorpe
145e8d57210SJason Gunthorpe /**
146e8d57210SJason Gunthorpe * iommufd_device_bind - Bind a physical device to an iommu fd
147e8d57210SJason Gunthorpe * @ictx: iommufd file descriptor
148e8d57210SJason Gunthorpe * @dev: Pointer to a physical device struct
149e8d57210SJason Gunthorpe * @id: Output ID number to return to userspace for this device
150e8d57210SJason Gunthorpe *
151e8d57210SJason Gunthorpe * A successful bind establishes an ownership over the device and returns
152e8d57210SJason Gunthorpe * struct iommufd_device pointer, otherwise returns error pointer.
153e8d57210SJason Gunthorpe *
154e8d57210SJason Gunthorpe * A driver using this API must set driver_managed_dma and must not touch
155e8d57210SJason Gunthorpe * the device until this routine succeeds and establishes ownership.
156e8d57210SJason Gunthorpe *
157e8d57210SJason Gunthorpe * Binding a PCI device places the entire RID under iommufd control.
158e8d57210SJason Gunthorpe *
159e8d57210SJason Gunthorpe * The caller must undo this with iommufd_device_unbind()
160e8d57210SJason Gunthorpe */
iommufd_device_bind(struct iommufd_ctx * ictx,struct device * dev,u32 * id)161e8d57210SJason Gunthorpe struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx,
162e8d57210SJason Gunthorpe struct device *dev, u32 *id)
163e8d57210SJason Gunthorpe {
164e8d57210SJason Gunthorpe struct iommufd_device *idev;
1653a3329a7SJason Gunthorpe struct iommufd_group *igroup;
166e8d57210SJason Gunthorpe int rc;
167e8d57210SJason Gunthorpe
168e8d57210SJason Gunthorpe /*
169e8d57210SJason Gunthorpe * iommufd always sets IOMMU_CACHE because we offer no way for userspace
170e8d57210SJason Gunthorpe * to restore cache coherency.
171e8d57210SJason Gunthorpe */
172e8d57210SJason Gunthorpe if (!device_iommu_capable(dev, IOMMU_CAP_CACHE_COHERENCY))
173e8d57210SJason Gunthorpe return ERR_PTR(-EINVAL);
174e8d57210SJason Gunthorpe
1753a3329a7SJason Gunthorpe igroup = iommufd_get_group(ictx, dev);
1763a3329a7SJason Gunthorpe if (IS_ERR(igroup))
1773a3329a7SJason Gunthorpe return ERR_CAST(igroup);
178e8d57210SJason Gunthorpe
179d525a5b8SJason Gunthorpe /*
180d525a5b8SJason Gunthorpe * For historical compat with VFIO the insecure interrupt path is
181d525a5b8SJason Gunthorpe * allowed if the module parameter is set. Secure/Isolated means that a
182d525a5b8SJason Gunthorpe * MemWr operation from the device (eg a simple DMA) cannot trigger an
183d525a5b8SJason Gunthorpe * interrupt outside this iommufd context.
184d525a5b8SJason Gunthorpe */
185d525a5b8SJason Gunthorpe if (!iommufd_selftest_is_mock_dev(dev) &&
1863a3329a7SJason Gunthorpe !iommu_group_has_isolated_msi(igroup->group)) {
187d525a5b8SJason Gunthorpe if (!allow_unsafe_interrupts) {
188d525a5b8SJason Gunthorpe rc = -EPERM;
189d525a5b8SJason Gunthorpe goto out_group_put;
190d525a5b8SJason Gunthorpe }
191d525a5b8SJason Gunthorpe
192d525a5b8SJason Gunthorpe dev_warn(
193d525a5b8SJason Gunthorpe dev,
194d525a5b8SJason Gunthorpe "MSI interrupts are not secure, they cannot be isolated by the platform. "
195d525a5b8SJason Gunthorpe "Check that platform features like interrupt remapping are enabled. "
196d525a5b8SJason Gunthorpe "Use the \"allow_unsafe_interrupts\" module parameter to override\n");
197d525a5b8SJason Gunthorpe }
198d525a5b8SJason Gunthorpe
199e8d57210SJason Gunthorpe rc = iommu_device_claim_dma_owner(dev, ictx);
200e8d57210SJason Gunthorpe if (rc)
201e8d57210SJason Gunthorpe goto out_group_put;
202e8d57210SJason Gunthorpe
203e8d57210SJason Gunthorpe idev = iommufd_object_alloc(ictx, idev, IOMMUFD_OBJ_DEVICE);
204e8d57210SJason Gunthorpe if (IS_ERR(idev)) {
205e8d57210SJason Gunthorpe rc = PTR_ERR(idev);
206e8d57210SJason Gunthorpe goto out_release_owner;
207e8d57210SJason Gunthorpe }
208e8d57210SJason Gunthorpe idev->ictx = ictx;
20965c619aeSJason Gunthorpe if (!iommufd_selftest_is_mock_dev(dev))
210e8d57210SJason Gunthorpe iommufd_ctx_get(ictx);
211e8d57210SJason Gunthorpe idev->dev = dev;
212e8d57210SJason Gunthorpe idev->enforce_cache_coherency =
213e8d57210SJason Gunthorpe device_iommu_capable(dev, IOMMU_CAP_ENFORCE_CACHE_COHERENCY);
214e8d57210SJason Gunthorpe /* The calling driver is a user until iommufd_device_unbind() */
215e8d57210SJason Gunthorpe refcount_inc(&idev->obj.users);
2163a3329a7SJason Gunthorpe /* igroup refcount moves into iommufd_device */
2173a3329a7SJason Gunthorpe idev->igroup = igroup;
218e8d57210SJason Gunthorpe
219e8d57210SJason Gunthorpe /*
220e8d57210SJason Gunthorpe * If the caller fails after this success it must call
221e8d57210SJason Gunthorpe * iommufd_unbind_device() which is safe since we hold this refcount.
222e8d57210SJason Gunthorpe * This also means the device is a leaf in the graph and no other object
223e8d57210SJason Gunthorpe * can take a reference on it.
224e8d57210SJason Gunthorpe */
225e8d57210SJason Gunthorpe iommufd_object_finalize(ictx, &idev->obj);
226e8d57210SJason Gunthorpe *id = idev->obj.id;
227e8d57210SJason Gunthorpe return idev;
228e8d57210SJason Gunthorpe
229e8d57210SJason Gunthorpe out_release_owner:
230e8d57210SJason Gunthorpe iommu_device_release_dma_owner(dev);
231e8d57210SJason Gunthorpe out_group_put:
2323a3329a7SJason Gunthorpe iommufd_put_group(igroup);
233e8d57210SJason Gunthorpe return ERR_PTR(rc);
234e8d57210SJason Gunthorpe }
235e8d57210SJason Gunthorpe EXPORT_SYMBOL_NS_GPL(iommufd_device_bind, IOMMUFD);
236e8d57210SJason Gunthorpe
237e8d57210SJason Gunthorpe /**
23886b0a96cSYi Liu * iommufd_ctx_has_group - True if any device within the group is bound
23986b0a96cSYi Liu * to the ictx
24086b0a96cSYi Liu * @ictx: iommufd file descriptor
24186b0a96cSYi Liu * @group: Pointer to a physical iommu_group struct
24286b0a96cSYi Liu *
24386b0a96cSYi Liu * True if any device within the group has been bound to this ictx, ex. via
24486b0a96cSYi Liu * iommufd_device_bind(), therefore implying ictx ownership of the group.
24586b0a96cSYi Liu */
iommufd_ctx_has_group(struct iommufd_ctx * ictx,struct iommu_group * group)24686b0a96cSYi Liu bool iommufd_ctx_has_group(struct iommufd_ctx *ictx, struct iommu_group *group)
24786b0a96cSYi Liu {
24886b0a96cSYi Liu struct iommufd_object *obj;
24986b0a96cSYi Liu unsigned long index;
25086b0a96cSYi Liu
25186b0a96cSYi Liu if (!ictx || !group)
25286b0a96cSYi Liu return false;
25386b0a96cSYi Liu
25486b0a96cSYi Liu xa_lock(&ictx->objects);
25586b0a96cSYi Liu xa_for_each(&ictx->objects, index, obj) {
25686b0a96cSYi Liu if (obj->type == IOMMUFD_OBJ_DEVICE &&
2573a3329a7SJason Gunthorpe container_of(obj, struct iommufd_device, obj)
2583a3329a7SJason Gunthorpe ->igroup->group == group) {
25986b0a96cSYi Liu xa_unlock(&ictx->objects);
26086b0a96cSYi Liu return true;
26186b0a96cSYi Liu }
26286b0a96cSYi Liu }
26386b0a96cSYi Liu xa_unlock(&ictx->objects);
26486b0a96cSYi Liu return false;
26586b0a96cSYi Liu }
26686b0a96cSYi Liu EXPORT_SYMBOL_NS_GPL(iommufd_ctx_has_group, IOMMUFD);
26786b0a96cSYi Liu
26886b0a96cSYi Liu /**
269e8d57210SJason Gunthorpe * iommufd_device_unbind - Undo iommufd_device_bind()
270e8d57210SJason Gunthorpe * @idev: Device returned by iommufd_device_bind()
271e8d57210SJason Gunthorpe *
272e8d57210SJason Gunthorpe * Release the device from iommufd control. The DMA ownership will return back
273e8d57210SJason Gunthorpe * to unowned with DMA controlled by the DMA API. This invalidates the
274e8d57210SJason Gunthorpe * iommufd_device pointer, other APIs that consume it must not be called
275e8d57210SJason Gunthorpe * concurrently.
276e8d57210SJason Gunthorpe */
iommufd_device_unbind(struct iommufd_device * idev)277e8d57210SJason Gunthorpe void iommufd_device_unbind(struct iommufd_device *idev)
278e8d57210SJason Gunthorpe {
27999f98a7cSJason Gunthorpe iommufd_object_destroy_user(idev->ictx, &idev->obj);
280e8d57210SJason Gunthorpe }
281e8d57210SJason Gunthorpe EXPORT_SYMBOL_NS_GPL(iommufd_device_unbind, IOMMUFD);
282e8d57210SJason Gunthorpe
iommufd_device_to_ictx(struct iommufd_device * idev)28378d3df45SYi Liu struct iommufd_ctx *iommufd_device_to_ictx(struct iommufd_device *idev)
28478d3df45SYi Liu {
28578d3df45SYi Liu return idev->ictx;
28678d3df45SYi Liu }
28778d3df45SYi Liu EXPORT_SYMBOL_NS_GPL(iommufd_device_to_ictx, IOMMUFD);
28878d3df45SYi Liu
iommufd_device_to_id(struct iommufd_device * idev)28978d3df45SYi Liu u32 iommufd_device_to_id(struct iommufd_device *idev)
29078d3df45SYi Liu {
29178d3df45SYi Liu return idev->obj.id;
29278d3df45SYi Liu }
29378d3df45SYi Liu EXPORT_SYMBOL_NS_GPL(iommufd_device_to_id, IOMMUFD);
29478d3df45SYi Liu
iommufd_group_setup_msi(struct iommufd_group * igroup,struct iommufd_hw_pagetable * hwpt)2951d149ab2SJason Gunthorpe static int iommufd_group_setup_msi(struct iommufd_group *igroup,
2961d149ab2SJason Gunthorpe struct iommufd_hw_pagetable *hwpt)
297e8d57210SJason Gunthorpe {
2981d149ab2SJason Gunthorpe phys_addr_t sw_msi_start = igroup->sw_msi_start;
299e8d57210SJason Gunthorpe int rc;
300e8d57210SJason Gunthorpe
301e8d57210SJason Gunthorpe /*
302d6c55c0aSJason Gunthorpe * If the IOMMU driver gives a IOMMU_RESV_SW_MSI then it is asking us to
303d6c55c0aSJason Gunthorpe * call iommu_get_msi_cookie() on its behalf. This is necessary to setup
304d6c55c0aSJason Gunthorpe * the MSI window so iommu_dma_prepare_msi() can install pages into our
305d6c55c0aSJason Gunthorpe * domain after request_irq(). If it is not done interrupts will not
306d6c55c0aSJason Gunthorpe * work on this domain.
307e8d57210SJason Gunthorpe *
308e8d57210SJason Gunthorpe * FIXME: This is conceptually broken for iommufd since we want to allow
309e8d57210SJason Gunthorpe * userspace to change the domains, eg switch from an identity IOAS to a
310e8d57210SJason Gunthorpe * DMA IOAS. There is currently no way to create a MSI window that
311e8d57210SJason Gunthorpe * matches what the IRQ layer actually expects in a newly created
312e8d57210SJason Gunthorpe * domain.
313e8d57210SJason Gunthorpe */
314d6c55c0aSJason Gunthorpe if (sw_msi_start != PHYS_ADDR_MAX && !hwpt->msi_cookie) {
315d6c55c0aSJason Gunthorpe rc = iommu_get_msi_cookie(hwpt->domain, sw_msi_start);
316d6c55c0aSJason Gunthorpe if (rc)
317d6c55c0aSJason Gunthorpe return rc;
318d6c55c0aSJason Gunthorpe
319e8d57210SJason Gunthorpe /*
320e8d57210SJason Gunthorpe * iommu_get_msi_cookie() can only be called once per domain,
321e8d57210SJason Gunthorpe * it returns -EBUSY on later calls.
322e8d57210SJason Gunthorpe */
323e8d57210SJason Gunthorpe hwpt->msi_cookie = true;
324e8d57210SJason Gunthorpe }
325e8d57210SJason Gunthorpe return 0;
326e8d57210SJason Gunthorpe }
327e8d57210SJason Gunthorpe
iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable * hwpt,struct iommufd_device * idev)328339fbf3aSJason Gunthorpe int iommufd_hw_pagetable_attach(struct iommufd_hw_pagetable *hwpt,
329339fbf3aSJason Gunthorpe struct iommufd_device *idev)
330e8d57210SJason Gunthorpe {
331e8d57210SJason Gunthorpe int rc;
332e8d57210SJason Gunthorpe
33391a2e17eSJason Gunthorpe mutex_lock(&idev->igroup->lock);
334339fbf3aSJason Gunthorpe
33591a2e17eSJason Gunthorpe if (idev->igroup->hwpt != NULL && idev->igroup->hwpt != hwpt) {
33691a2e17eSJason Gunthorpe rc = -EINVAL;
33791a2e17eSJason Gunthorpe goto err_unlock;
33891a2e17eSJason Gunthorpe }
339e8d57210SJason Gunthorpe
34017bad527SJason Gunthorpe /* Try to upgrade the domain we have */
34117bad527SJason Gunthorpe if (idev->enforce_cache_coherency) {
34217bad527SJason Gunthorpe rc = iommufd_hw_pagetable_enforce_cc(hwpt);
34317bad527SJason Gunthorpe if (rc)
34491a2e17eSJason Gunthorpe goto err_unlock;
345e8d57210SJason Gunthorpe }
346e8d57210SJason Gunthorpe
3471d149ab2SJason Gunthorpe rc = iopt_table_enforce_dev_resv_regions(&hwpt->ioas->iopt, idev->dev,
3481d149ab2SJason Gunthorpe &idev->igroup->sw_msi_start);
349e8d57210SJason Gunthorpe if (rc)
35091a2e17eSJason Gunthorpe goto err_unlock;
351e8d57210SJason Gunthorpe
352e8d57210SJason Gunthorpe /*
35391a2e17eSJason Gunthorpe * Only attach to the group once for the first device that is in the
35491a2e17eSJason Gunthorpe * group. All the other devices will follow this attachment. The user
35591a2e17eSJason Gunthorpe * should attach every device individually to the hwpt as the per-device
35691a2e17eSJason Gunthorpe * reserved regions are only updated during individual device
35791a2e17eSJason Gunthorpe * attachment.
358e8d57210SJason Gunthorpe */
35991a2e17eSJason Gunthorpe if (list_empty(&idev->igroup->device_list)) {
3601d149ab2SJason Gunthorpe rc = iommufd_group_setup_msi(idev->igroup, hwpt);
361269c5238SJason Gunthorpe if (rc)
362269c5238SJason Gunthorpe goto err_unresv;
363269c5238SJason Gunthorpe
3643a3329a7SJason Gunthorpe rc = iommu_attach_group(hwpt->domain, idev->igroup->group);
365e8d57210SJason Gunthorpe if (rc)
366339fbf3aSJason Gunthorpe goto err_unresv;
36791a2e17eSJason Gunthorpe idev->igroup->hwpt = hwpt;
368339fbf3aSJason Gunthorpe }
36991a2e17eSJason Gunthorpe refcount_inc(&hwpt->obj.users);
37091a2e17eSJason Gunthorpe list_add_tail(&idev->group_item, &idev->igroup->device_list);
37191a2e17eSJason Gunthorpe mutex_unlock(&idev->igroup->lock);
372339fbf3aSJason Gunthorpe return 0;
373339fbf3aSJason Gunthorpe err_unresv:
374339fbf3aSJason Gunthorpe iopt_remove_reserved_iova(&hwpt->ioas->iopt, idev->dev);
37591a2e17eSJason Gunthorpe err_unlock:
37691a2e17eSJason Gunthorpe mutex_unlock(&idev->igroup->lock);
377339fbf3aSJason Gunthorpe return rc;
378339fbf3aSJason Gunthorpe }
379e8d57210SJason Gunthorpe
38091a2e17eSJason Gunthorpe struct iommufd_hw_pagetable *
iommufd_hw_pagetable_detach(struct iommufd_device * idev)38191a2e17eSJason Gunthorpe iommufd_hw_pagetable_detach(struct iommufd_device *idev)
382339fbf3aSJason Gunthorpe {
38391a2e17eSJason Gunthorpe struct iommufd_hw_pagetable *hwpt = idev->igroup->hwpt;
38491a2e17eSJason Gunthorpe
38591a2e17eSJason Gunthorpe mutex_lock(&idev->igroup->lock);
38691a2e17eSJason Gunthorpe list_del(&idev->group_item);
38791a2e17eSJason Gunthorpe if (list_empty(&idev->igroup->device_list)) {
3883a3329a7SJason Gunthorpe iommu_detach_group(hwpt->domain, idev->igroup->group);
38991a2e17eSJason Gunthorpe idev->igroup->hwpt = NULL;
390339fbf3aSJason Gunthorpe }
39191a2e17eSJason Gunthorpe iopt_remove_reserved_iova(&hwpt->ioas->iopt, idev->dev);
39291a2e17eSJason Gunthorpe mutex_unlock(&idev->igroup->lock);
393339fbf3aSJason Gunthorpe
39491a2e17eSJason Gunthorpe /* Caller must destroy hwpt */
39591a2e17eSJason Gunthorpe return hwpt;
396e8d57210SJason Gunthorpe }
397e8d57210SJason Gunthorpe
398ea2d6124SJason Gunthorpe static struct iommufd_hw_pagetable *
iommufd_device_do_attach(struct iommufd_device * idev,struct iommufd_hw_pagetable * hwpt)399ea2d6124SJason Gunthorpe iommufd_device_do_attach(struct iommufd_device *idev,
400ea2d6124SJason Gunthorpe struct iommufd_hw_pagetable *hwpt)
401ea2d6124SJason Gunthorpe {
402ea2d6124SJason Gunthorpe int rc;
403ea2d6124SJason Gunthorpe
404ea2d6124SJason Gunthorpe rc = iommufd_hw_pagetable_attach(hwpt, idev);
405ea2d6124SJason Gunthorpe if (rc)
406ea2d6124SJason Gunthorpe return ERR_PTR(rc);
407ea2d6124SJason Gunthorpe return NULL;
408ea2d6124SJason Gunthorpe }
409ea2d6124SJason Gunthorpe
410e88d4ec1SJason Gunthorpe static struct iommufd_hw_pagetable *
iommufd_device_do_replace(struct iommufd_device * idev,struct iommufd_hw_pagetable * hwpt)411e88d4ec1SJason Gunthorpe iommufd_device_do_replace(struct iommufd_device *idev,
412e88d4ec1SJason Gunthorpe struct iommufd_hw_pagetable *hwpt)
413e88d4ec1SJason Gunthorpe {
414e88d4ec1SJason Gunthorpe struct iommufd_group *igroup = idev->igroup;
415e88d4ec1SJason Gunthorpe struct iommufd_hw_pagetable *old_hwpt;
416e88d4ec1SJason Gunthorpe unsigned int num_devices = 0;
417e88d4ec1SJason Gunthorpe struct iommufd_device *cur;
418e88d4ec1SJason Gunthorpe int rc;
419e88d4ec1SJason Gunthorpe
420e88d4ec1SJason Gunthorpe mutex_lock(&idev->igroup->lock);
421e88d4ec1SJason Gunthorpe
422e88d4ec1SJason Gunthorpe if (igroup->hwpt == NULL) {
423e88d4ec1SJason Gunthorpe rc = -EINVAL;
424e88d4ec1SJason Gunthorpe goto err_unlock;
425e88d4ec1SJason Gunthorpe }
426e88d4ec1SJason Gunthorpe
427e88d4ec1SJason Gunthorpe if (hwpt == igroup->hwpt) {
428e88d4ec1SJason Gunthorpe mutex_unlock(&idev->igroup->lock);
429e88d4ec1SJason Gunthorpe return NULL;
430e88d4ec1SJason Gunthorpe }
431e88d4ec1SJason Gunthorpe
432e88d4ec1SJason Gunthorpe /* Try to upgrade the domain we have */
433e88d4ec1SJason Gunthorpe list_for_each_entry(cur, &igroup->device_list, group_item) {
434e88d4ec1SJason Gunthorpe num_devices++;
435e88d4ec1SJason Gunthorpe if (cur->enforce_cache_coherency) {
436e88d4ec1SJason Gunthorpe rc = iommufd_hw_pagetable_enforce_cc(hwpt);
437e88d4ec1SJason Gunthorpe if (rc)
438e88d4ec1SJason Gunthorpe goto err_unlock;
439e88d4ec1SJason Gunthorpe }
440e88d4ec1SJason Gunthorpe }
441e88d4ec1SJason Gunthorpe
442e88d4ec1SJason Gunthorpe old_hwpt = igroup->hwpt;
443e88d4ec1SJason Gunthorpe if (hwpt->ioas != old_hwpt->ioas) {
444e88d4ec1SJason Gunthorpe list_for_each_entry(cur, &igroup->device_list, group_item) {
445e88d4ec1SJason Gunthorpe rc = iopt_table_enforce_dev_resv_regions(
446e88d4ec1SJason Gunthorpe &hwpt->ioas->iopt, cur->dev, NULL);
447e88d4ec1SJason Gunthorpe if (rc)
448e88d4ec1SJason Gunthorpe goto err_unresv;
449e88d4ec1SJason Gunthorpe }
450e88d4ec1SJason Gunthorpe }
451e88d4ec1SJason Gunthorpe
452e88d4ec1SJason Gunthorpe rc = iommufd_group_setup_msi(idev->igroup, hwpt);
453e88d4ec1SJason Gunthorpe if (rc)
454e88d4ec1SJason Gunthorpe goto err_unresv;
455e88d4ec1SJason Gunthorpe
456e88d4ec1SJason Gunthorpe rc = iommu_group_replace_domain(igroup->group, hwpt->domain);
457e88d4ec1SJason Gunthorpe if (rc)
458e88d4ec1SJason Gunthorpe goto err_unresv;
459e88d4ec1SJason Gunthorpe
460e88d4ec1SJason Gunthorpe if (hwpt->ioas != old_hwpt->ioas) {
461e88d4ec1SJason Gunthorpe list_for_each_entry(cur, &igroup->device_list, group_item)
462e88d4ec1SJason Gunthorpe iopt_remove_reserved_iova(&old_hwpt->ioas->iopt,
463e88d4ec1SJason Gunthorpe cur->dev);
464e88d4ec1SJason Gunthorpe }
465e88d4ec1SJason Gunthorpe
466e88d4ec1SJason Gunthorpe igroup->hwpt = hwpt;
467e88d4ec1SJason Gunthorpe
468e88d4ec1SJason Gunthorpe /*
469e88d4ec1SJason Gunthorpe * Move the refcounts held by the device_list to the new hwpt. Retain a
470e88d4ec1SJason Gunthorpe * refcount for this thread as the caller will free it.
471e88d4ec1SJason Gunthorpe */
472e88d4ec1SJason Gunthorpe refcount_add(num_devices, &hwpt->obj.users);
473e88d4ec1SJason Gunthorpe if (num_devices > 1)
474e88d4ec1SJason Gunthorpe WARN_ON(refcount_sub_and_test(num_devices - 1,
475e88d4ec1SJason Gunthorpe &old_hwpt->obj.users));
476e88d4ec1SJason Gunthorpe mutex_unlock(&idev->igroup->lock);
477e88d4ec1SJason Gunthorpe
478e88d4ec1SJason Gunthorpe /* Caller must destroy old_hwpt */
479e88d4ec1SJason Gunthorpe return old_hwpt;
480e88d4ec1SJason Gunthorpe err_unresv:
481e88d4ec1SJason Gunthorpe list_for_each_entry(cur, &igroup->device_list, group_item)
482e88d4ec1SJason Gunthorpe iopt_remove_reserved_iova(&hwpt->ioas->iopt, cur->dev);
483e88d4ec1SJason Gunthorpe err_unlock:
484e88d4ec1SJason Gunthorpe mutex_unlock(&idev->igroup->lock);
485e88d4ec1SJason Gunthorpe return ERR_PTR(rc);
486e88d4ec1SJason Gunthorpe }
487e88d4ec1SJason Gunthorpe
488ea2d6124SJason Gunthorpe typedef struct iommufd_hw_pagetable *(*attach_fn)(
489ea2d6124SJason Gunthorpe struct iommufd_device *idev, struct iommufd_hw_pagetable *hwpt);
490ea2d6124SJason Gunthorpe
491e8d57210SJason Gunthorpe /*
492e8d57210SJason Gunthorpe * When automatically managing the domains we search for a compatible domain in
493e8d57210SJason Gunthorpe * the iopt and if one is found use it, otherwise create a new domain.
494e8d57210SJason Gunthorpe * Automatic domain selection will never pick a manually created domain.
495e8d57210SJason Gunthorpe */
496ea2d6124SJason Gunthorpe static struct iommufd_hw_pagetable *
497ea2d6124SJason Gunthorpe iommufd_device_auto_get_domain(struct iommufd_device *idev,
498ea2d6124SJason Gunthorpe struct iommufd_ioas *ioas, u32 *pt_id,
499ea2d6124SJason Gunthorpe attach_fn do_attach)
500e8d57210SJason Gunthorpe {
501ea2d6124SJason Gunthorpe /*
502ea2d6124SJason Gunthorpe * iommufd_hw_pagetable_attach() is called by
503ea2d6124SJason Gunthorpe * iommufd_hw_pagetable_alloc() in immediate attachment mode, same as
504ea2d6124SJason Gunthorpe * iommufd_device_do_attach(). So if we are in this mode then we prefer
505ea2d6124SJason Gunthorpe * to use the immediate_attach path as it supports drivers that can't
506ea2d6124SJason Gunthorpe * directly allocate a domain.
507ea2d6124SJason Gunthorpe */
508ea2d6124SJason Gunthorpe bool immediate_attach = do_attach == iommufd_device_do_attach;
509ea2d6124SJason Gunthorpe struct iommufd_hw_pagetable *destroy_hwpt;
510e8d57210SJason Gunthorpe struct iommufd_hw_pagetable *hwpt;
511e8d57210SJason Gunthorpe
512e8d57210SJason Gunthorpe /*
513e8d57210SJason Gunthorpe * There is no differentiation when domains are allocated, so any domain
514e8d57210SJason Gunthorpe * that is willing to attach to the device is interchangeable with any
515e8d57210SJason Gunthorpe * other.
516e8d57210SJason Gunthorpe */
517e8d57210SJason Gunthorpe mutex_lock(&ioas->mutex);
518e8d57210SJason Gunthorpe list_for_each_entry(hwpt, &ioas->hwpt_list, hwpt_item) {
519e8d57210SJason Gunthorpe if (!hwpt->auto_domain)
520e8d57210SJason Gunthorpe continue;
521e8d57210SJason Gunthorpe
5227214c1c8SJason Gunthorpe if (!iommufd_lock_obj(&hwpt->obj))
5237214c1c8SJason Gunthorpe continue;
524ea2d6124SJason Gunthorpe destroy_hwpt = (*do_attach)(idev, hwpt);
525ea2d6124SJason Gunthorpe if (IS_ERR(destroy_hwpt)) {
5267214c1c8SJason Gunthorpe iommufd_put_object(&hwpt->obj);
527e8d57210SJason Gunthorpe /*
528ea2d6124SJason Gunthorpe * -EINVAL means the domain is incompatible with the
529ea2d6124SJason Gunthorpe * device. Other error codes should propagate to
530ea2d6124SJason Gunthorpe * userspace as failure. Success means the domain is
531ea2d6124SJason Gunthorpe * attached.
532e8d57210SJason Gunthorpe */
533ea2d6124SJason Gunthorpe if (PTR_ERR(destroy_hwpt) == -EINVAL)
534e8d57210SJason Gunthorpe continue;
535ea2d6124SJason Gunthorpe goto out_unlock;
536ea2d6124SJason Gunthorpe }
53791a2e17eSJason Gunthorpe *pt_id = hwpt->obj.id;
538ea2d6124SJason Gunthorpe iommufd_put_object(&hwpt->obj);
539e8d57210SJason Gunthorpe goto out_unlock;
540e8d57210SJason Gunthorpe }
541e8d57210SJason Gunthorpe
542ea2d6124SJason Gunthorpe hwpt = iommufd_hw_pagetable_alloc(idev->ictx, ioas, idev,
543ea2d6124SJason Gunthorpe immediate_attach);
544e8d57210SJason Gunthorpe if (IS_ERR(hwpt)) {
545ea2d6124SJason Gunthorpe destroy_hwpt = ERR_CAST(hwpt);
546e8d57210SJason Gunthorpe goto out_unlock;
547e8d57210SJason Gunthorpe }
548ea2d6124SJason Gunthorpe
549ea2d6124SJason Gunthorpe if (!immediate_attach) {
550ea2d6124SJason Gunthorpe destroy_hwpt = (*do_attach)(idev, hwpt);
551ea2d6124SJason Gunthorpe if (IS_ERR(destroy_hwpt))
552ea2d6124SJason Gunthorpe goto out_abort;
553ea2d6124SJason Gunthorpe } else {
554ea2d6124SJason Gunthorpe destroy_hwpt = NULL;
555ea2d6124SJason Gunthorpe }
556ea2d6124SJason Gunthorpe
557e8d57210SJason Gunthorpe hwpt->auto_domain = true;
55891a2e17eSJason Gunthorpe *pt_id = hwpt->obj.id;
559e8d57210SJason Gunthorpe
560e8d57210SJason Gunthorpe iommufd_object_finalize(idev->ictx, &hwpt->obj);
56131422dffSJason Gunthorpe mutex_unlock(&ioas->mutex);
562ea2d6124SJason Gunthorpe return destroy_hwpt;
563ea2d6124SJason Gunthorpe
564ea2d6124SJason Gunthorpe out_abort:
565ea2d6124SJason Gunthorpe iommufd_object_abort_and_destroy(idev->ictx, &hwpt->obj);
566e8d57210SJason Gunthorpe out_unlock:
567e8d57210SJason Gunthorpe mutex_unlock(&ioas->mutex);
568ea2d6124SJason Gunthorpe return destroy_hwpt;
569ea2d6124SJason Gunthorpe }
570ea2d6124SJason Gunthorpe
571ea2d6124SJason Gunthorpe static int iommufd_device_change_pt(struct iommufd_device *idev, u32 *pt_id,
572ea2d6124SJason Gunthorpe attach_fn do_attach)
573ea2d6124SJason Gunthorpe {
574ea2d6124SJason Gunthorpe struct iommufd_hw_pagetable *destroy_hwpt;
575ea2d6124SJason Gunthorpe struct iommufd_object *pt_obj;
576ea2d6124SJason Gunthorpe
577ea2d6124SJason Gunthorpe pt_obj = iommufd_get_object(idev->ictx, *pt_id, IOMMUFD_OBJ_ANY);
578ea2d6124SJason Gunthorpe if (IS_ERR(pt_obj))
579ea2d6124SJason Gunthorpe return PTR_ERR(pt_obj);
580ea2d6124SJason Gunthorpe
581ea2d6124SJason Gunthorpe switch (pt_obj->type) {
582ea2d6124SJason Gunthorpe case IOMMUFD_OBJ_HW_PAGETABLE: {
583ea2d6124SJason Gunthorpe struct iommufd_hw_pagetable *hwpt =
584ea2d6124SJason Gunthorpe container_of(pt_obj, struct iommufd_hw_pagetable, obj);
585ea2d6124SJason Gunthorpe
586ea2d6124SJason Gunthorpe destroy_hwpt = (*do_attach)(idev, hwpt);
587ea2d6124SJason Gunthorpe if (IS_ERR(destroy_hwpt))
588ea2d6124SJason Gunthorpe goto out_put_pt_obj;
589ea2d6124SJason Gunthorpe break;
590ea2d6124SJason Gunthorpe }
591ea2d6124SJason Gunthorpe case IOMMUFD_OBJ_IOAS: {
592ea2d6124SJason Gunthorpe struct iommufd_ioas *ioas =
593ea2d6124SJason Gunthorpe container_of(pt_obj, struct iommufd_ioas, obj);
594ea2d6124SJason Gunthorpe
595ea2d6124SJason Gunthorpe destroy_hwpt = iommufd_device_auto_get_domain(idev, ioas, pt_id,
596ea2d6124SJason Gunthorpe do_attach);
597ea2d6124SJason Gunthorpe if (IS_ERR(destroy_hwpt))
598ea2d6124SJason Gunthorpe goto out_put_pt_obj;
599ea2d6124SJason Gunthorpe break;
600ea2d6124SJason Gunthorpe }
601ea2d6124SJason Gunthorpe default:
602ea2d6124SJason Gunthorpe destroy_hwpt = ERR_PTR(-EINVAL);
603ea2d6124SJason Gunthorpe goto out_put_pt_obj;
604ea2d6124SJason Gunthorpe }
605ea2d6124SJason Gunthorpe iommufd_put_object(pt_obj);
606ea2d6124SJason Gunthorpe
607ea2d6124SJason Gunthorpe /* This destruction has to be after we unlock everything */
608ea2d6124SJason Gunthorpe if (destroy_hwpt)
609ea2d6124SJason Gunthorpe iommufd_hw_pagetable_put(idev->ictx, destroy_hwpt);
610ea2d6124SJason Gunthorpe return 0;
611ea2d6124SJason Gunthorpe
612ea2d6124SJason Gunthorpe out_put_pt_obj:
613ea2d6124SJason Gunthorpe iommufd_put_object(pt_obj);
614ea2d6124SJason Gunthorpe return PTR_ERR(destroy_hwpt);
615e8d57210SJason Gunthorpe }
616e8d57210SJason Gunthorpe
617e8d57210SJason Gunthorpe /**
618ea2d6124SJason Gunthorpe * iommufd_device_attach - Connect a device to an iommu_domain
619e8d57210SJason Gunthorpe * @idev: device to attach
620e8d57210SJason Gunthorpe * @pt_id: Input a IOMMUFD_OBJ_IOAS, or IOMMUFD_OBJ_HW_PAGETABLE
621e8d57210SJason Gunthorpe * Output the IOMMUFD_OBJ_HW_PAGETABLE ID
622e8d57210SJason Gunthorpe *
623e8d57210SJason Gunthorpe * This connects the device to an iommu_domain, either automatically or manually
624e8d57210SJason Gunthorpe * selected. Once this completes the device could do DMA.
625e8d57210SJason Gunthorpe *
626e8d57210SJason Gunthorpe * The caller should return the resulting pt_id back to userspace.
627e8d57210SJason Gunthorpe * This function is undone by calling iommufd_device_detach().
628e8d57210SJason Gunthorpe */
629e8d57210SJason Gunthorpe int iommufd_device_attach(struct iommufd_device *idev, u32 *pt_id)
630e8d57210SJason Gunthorpe {
631e8d57210SJason Gunthorpe int rc;
632e8d57210SJason Gunthorpe
633ea2d6124SJason Gunthorpe rc = iommufd_device_change_pt(idev, pt_id, &iommufd_device_do_attach);
634e8d57210SJason Gunthorpe if (rc)
635e8d57210SJason Gunthorpe return rc;
636ea2d6124SJason Gunthorpe
637ea2d6124SJason Gunthorpe /*
638ea2d6124SJason Gunthorpe * Pairs with iommufd_device_detach() - catches caller bugs attempting
639ea2d6124SJason Gunthorpe * to destroy a device with an attachment.
640ea2d6124SJason Gunthorpe */
641ea2d6124SJason Gunthorpe refcount_inc(&idev->obj.users);
642ea2d6124SJason Gunthorpe return 0;
643e8d57210SJason Gunthorpe }
644e8d57210SJason Gunthorpe EXPORT_SYMBOL_NS_GPL(iommufd_device_attach, IOMMUFD);
645e8d57210SJason Gunthorpe
646e8d57210SJason Gunthorpe /**
647e88d4ec1SJason Gunthorpe * iommufd_device_replace - Change the device's iommu_domain
648e88d4ec1SJason Gunthorpe * @idev: device to change
649e88d4ec1SJason Gunthorpe * @pt_id: Input a IOMMUFD_OBJ_IOAS, or IOMMUFD_OBJ_HW_PAGETABLE
650e88d4ec1SJason Gunthorpe * Output the IOMMUFD_OBJ_HW_PAGETABLE ID
651e88d4ec1SJason Gunthorpe *
652e88d4ec1SJason Gunthorpe * This is the same as::
653e88d4ec1SJason Gunthorpe *
654e88d4ec1SJason Gunthorpe * iommufd_device_detach();
655e88d4ec1SJason Gunthorpe * iommufd_device_attach();
656e88d4ec1SJason Gunthorpe *
657e88d4ec1SJason Gunthorpe * If it fails then no change is made to the attachment. The iommu driver may
658e88d4ec1SJason Gunthorpe * implement this so there is no disruption in translation. This can only be
659e88d4ec1SJason Gunthorpe * called if iommufd_device_attach() has already succeeded.
660e88d4ec1SJason Gunthorpe */
iommufd_device_replace(struct iommufd_device * idev,u32 * pt_id)661e88d4ec1SJason Gunthorpe int iommufd_device_replace(struct iommufd_device *idev, u32 *pt_id)
662e88d4ec1SJason Gunthorpe {
663e88d4ec1SJason Gunthorpe return iommufd_device_change_pt(idev, pt_id,
664e88d4ec1SJason Gunthorpe &iommufd_device_do_replace);
665e88d4ec1SJason Gunthorpe }
666e88d4ec1SJason Gunthorpe EXPORT_SYMBOL_NS_GPL(iommufd_device_replace, IOMMUFD);
667e88d4ec1SJason Gunthorpe
668e88d4ec1SJason Gunthorpe /**
669e8d57210SJason Gunthorpe * iommufd_device_detach - Disconnect a device to an iommu_domain
670e8d57210SJason Gunthorpe * @idev: device to detach
671e8d57210SJason Gunthorpe *
672e8d57210SJason Gunthorpe * Undo iommufd_device_attach(). This disconnects the idev from the previously
673e8d57210SJason Gunthorpe * attached pt_id. The device returns back to a blocked DMA translation.
674e8d57210SJason Gunthorpe */
iommufd_device_detach(struct iommufd_device * idev)675e8d57210SJason Gunthorpe void iommufd_device_detach(struct iommufd_device *idev)
676e8d57210SJason Gunthorpe {
67791a2e17eSJason Gunthorpe struct iommufd_hw_pagetable *hwpt;
678e8d57210SJason Gunthorpe
67991a2e17eSJason Gunthorpe hwpt = iommufd_hw_pagetable_detach(idev);
680d03f1336SJason Gunthorpe iommufd_hw_pagetable_put(idev->ictx, hwpt);
681e8d57210SJason Gunthorpe refcount_dec(&idev->obj.users);
682e8d57210SJason Gunthorpe }
683e8d57210SJason Gunthorpe EXPORT_SYMBOL_NS_GPL(iommufd_device_detach, IOMMUFD);
6848d40205fSJason Gunthorpe
6859227da78SNicolin Chen /*
6869227da78SNicolin Chen * On success, it will refcount_inc() at a valid new_ioas and refcount_dec() at
6879227da78SNicolin Chen * a valid cur_ioas (access->ioas). A caller passing in a valid new_ioas should
6889227da78SNicolin Chen * call iommufd_put_object() if it does an iommufd_get_object() for a new_ioas.
6899227da78SNicolin Chen */
iommufd_access_change_ioas(struct iommufd_access * access,struct iommufd_ioas * new_ioas)6909227da78SNicolin Chen static int iommufd_access_change_ioas(struct iommufd_access *access,
6919227da78SNicolin Chen struct iommufd_ioas *new_ioas)
6929227da78SNicolin Chen {
6939227da78SNicolin Chen u32 iopt_access_list_id = access->iopt_access_list_id;
6949227da78SNicolin Chen struct iommufd_ioas *cur_ioas = access->ioas;
6959227da78SNicolin Chen int rc;
6969227da78SNicolin Chen
6979227da78SNicolin Chen lockdep_assert_held(&access->ioas_lock);
6989227da78SNicolin Chen
6999227da78SNicolin Chen /* We are racing with a concurrent detach, bail */
7009227da78SNicolin Chen if (cur_ioas != access->ioas_unpin)
7019227da78SNicolin Chen return -EBUSY;
7029227da78SNicolin Chen
7039227da78SNicolin Chen if (cur_ioas == new_ioas)
7049227da78SNicolin Chen return 0;
7059227da78SNicolin Chen
7069227da78SNicolin Chen /*
7079227da78SNicolin Chen * Set ioas to NULL to block any further iommufd_access_pin_pages().
7089227da78SNicolin Chen * iommufd_access_unpin_pages() can continue using access->ioas_unpin.
7099227da78SNicolin Chen */
7109227da78SNicolin Chen access->ioas = NULL;
7119227da78SNicolin Chen
7129227da78SNicolin Chen if (new_ioas) {
7139227da78SNicolin Chen rc = iopt_add_access(&new_ioas->iopt, access);
7149227da78SNicolin Chen if (rc) {
7159227da78SNicolin Chen access->ioas = cur_ioas;
7169227da78SNicolin Chen return rc;
7179227da78SNicolin Chen }
7189227da78SNicolin Chen refcount_inc(&new_ioas->obj.users);
7199227da78SNicolin Chen }
7209227da78SNicolin Chen
7219227da78SNicolin Chen if (cur_ioas) {
7229227da78SNicolin Chen if (access->ops->unmap) {
7239227da78SNicolin Chen mutex_unlock(&access->ioas_lock);
7249227da78SNicolin Chen access->ops->unmap(access->data, 0, ULONG_MAX);
7259227da78SNicolin Chen mutex_lock(&access->ioas_lock);
7269227da78SNicolin Chen }
7279227da78SNicolin Chen iopt_remove_access(&cur_ioas->iopt, access, iopt_access_list_id);
7289227da78SNicolin Chen refcount_dec(&cur_ioas->obj.users);
7299227da78SNicolin Chen }
7309227da78SNicolin Chen
7319227da78SNicolin Chen access->ioas = new_ioas;
7329227da78SNicolin Chen access->ioas_unpin = new_ioas;
7339227da78SNicolin Chen
7349227da78SNicolin Chen return 0;
7359227da78SNicolin Chen }
7369227da78SNicolin Chen
iommufd_access_change_ioas_id(struct iommufd_access * access,u32 id)7379227da78SNicolin Chen static int iommufd_access_change_ioas_id(struct iommufd_access *access, u32 id)
7389227da78SNicolin Chen {
7399227da78SNicolin Chen struct iommufd_ioas *ioas = iommufd_get_ioas(access->ictx, id);
7409227da78SNicolin Chen int rc;
7419227da78SNicolin Chen
7429227da78SNicolin Chen if (IS_ERR(ioas))
7439227da78SNicolin Chen return PTR_ERR(ioas);
7449227da78SNicolin Chen rc = iommufd_access_change_ioas(access, ioas);
7459227da78SNicolin Chen iommufd_put_object(&ioas->obj);
7469227da78SNicolin Chen return rc;
7479227da78SNicolin Chen }
7489227da78SNicolin Chen
iommufd_access_destroy_object(struct iommufd_object * obj)7498d40205fSJason Gunthorpe void iommufd_access_destroy_object(struct iommufd_object *obj)
7508d40205fSJason Gunthorpe {
7518d40205fSJason Gunthorpe struct iommufd_access *access =
7528d40205fSJason Gunthorpe container_of(obj, struct iommufd_access, obj);
7538d40205fSJason Gunthorpe
7546129b59fSNicolin Chen mutex_lock(&access->ioas_lock);
7556129b59fSNicolin Chen if (access->ioas)
7566129b59fSNicolin Chen WARN_ON(iommufd_access_change_ioas(access, NULL));
7576129b59fSNicolin Chen mutex_unlock(&access->ioas_lock);
75854b47585SNicolin Chen iommufd_ctx_put(access->ictx);
7598d40205fSJason Gunthorpe }
7608d40205fSJason Gunthorpe
7618d40205fSJason Gunthorpe /**
7628d40205fSJason Gunthorpe * iommufd_access_create - Create an iommufd_access
7638d40205fSJason Gunthorpe * @ictx: iommufd file descriptor
7648d40205fSJason Gunthorpe * @ops: Driver's ops to associate with the access
7658d40205fSJason Gunthorpe * @data: Opaque data to pass into ops functions
766632fda7fSYi Liu * @id: Output ID number to return to userspace for this access
7678d40205fSJason Gunthorpe *
7688d40205fSJason Gunthorpe * An iommufd_access allows a driver to read/write to the IOAS without using
7698d40205fSJason Gunthorpe * DMA. The underlying CPU memory can be accessed using the
7708d40205fSJason Gunthorpe * iommufd_access_pin_pages() or iommufd_access_rw() functions.
7718d40205fSJason Gunthorpe *
7728d40205fSJason Gunthorpe * The provided ops are required to use iommufd_access_pin_pages().
7738d40205fSJason Gunthorpe */
7748d40205fSJason Gunthorpe struct iommufd_access *
iommufd_access_create(struct iommufd_ctx * ictx,const struct iommufd_access_ops * ops,void * data,u32 * id)77554b47585SNicolin Chen iommufd_access_create(struct iommufd_ctx *ictx,
776632fda7fSYi Liu const struct iommufd_access_ops *ops, void *data, u32 *id)
7778d40205fSJason Gunthorpe {
7788d40205fSJason Gunthorpe struct iommufd_access *access;
7798d40205fSJason Gunthorpe
7808d40205fSJason Gunthorpe /*
7818d40205fSJason Gunthorpe * There is no uAPI for the access object, but to keep things symmetric
7828d40205fSJason Gunthorpe * use the object infrastructure anyhow.
7838d40205fSJason Gunthorpe */
7848d40205fSJason Gunthorpe access = iommufd_object_alloc(ictx, access, IOMMUFD_OBJ_ACCESS);
7858d40205fSJason Gunthorpe if (IS_ERR(access))
7868d40205fSJason Gunthorpe return access;
7878d40205fSJason Gunthorpe
7888d40205fSJason Gunthorpe access->data = data;
7898d40205fSJason Gunthorpe access->ops = ops;
7908d40205fSJason Gunthorpe
7918d40205fSJason Gunthorpe if (ops->needs_pin_pages)
7928d40205fSJason Gunthorpe access->iova_alignment = PAGE_SIZE;
7938d40205fSJason Gunthorpe else
7948d40205fSJason Gunthorpe access->iova_alignment = 1;
7958d40205fSJason Gunthorpe
7968d40205fSJason Gunthorpe /* The calling driver is a user until iommufd_access_destroy() */
7978d40205fSJason Gunthorpe refcount_inc(&access->obj.users);
7988d40205fSJason Gunthorpe access->ictx = ictx;
7998d40205fSJason Gunthorpe iommufd_ctx_get(ictx);
8008d40205fSJason Gunthorpe iommufd_object_finalize(ictx, &access->obj);
801632fda7fSYi Liu *id = access->obj.id;
802e23a6217SNicolin Chen mutex_init(&access->ioas_lock);
8038d40205fSJason Gunthorpe return access;
8048d40205fSJason Gunthorpe }
8058d40205fSJason Gunthorpe EXPORT_SYMBOL_NS_GPL(iommufd_access_create, IOMMUFD);
8068d40205fSJason Gunthorpe
8078d40205fSJason Gunthorpe /**
8088d40205fSJason Gunthorpe * iommufd_access_destroy - Destroy an iommufd_access
8098d40205fSJason Gunthorpe * @access: The access to destroy
8108d40205fSJason Gunthorpe *
8118d40205fSJason Gunthorpe * The caller must stop using the access before destroying it.
8128d40205fSJason Gunthorpe */
iommufd_access_destroy(struct iommufd_access * access)8138d40205fSJason Gunthorpe void iommufd_access_destroy(struct iommufd_access *access)
8148d40205fSJason Gunthorpe {
81599f98a7cSJason Gunthorpe iommufd_object_destroy_user(access->ictx, &access->obj);
8168d40205fSJason Gunthorpe }
8178d40205fSJason Gunthorpe EXPORT_SYMBOL_NS_GPL(iommufd_access_destroy, IOMMUFD);
8188d40205fSJason Gunthorpe
iommufd_access_detach(struct iommufd_access * access)819e23a6217SNicolin Chen void iommufd_access_detach(struct iommufd_access *access)
820e23a6217SNicolin Chen {
821e23a6217SNicolin Chen mutex_lock(&access->ioas_lock);
8229227da78SNicolin Chen if (WARN_ON(!access->ioas)) {
823e23a6217SNicolin Chen mutex_unlock(&access->ioas_lock);
8249227da78SNicolin Chen return;
825e23a6217SNicolin Chen }
8269227da78SNicolin Chen WARN_ON(iommufd_access_change_ioas(access, NULL));
827e23a6217SNicolin Chen mutex_unlock(&access->ioas_lock);
828e23a6217SNicolin Chen }
829e23a6217SNicolin Chen EXPORT_SYMBOL_NS_GPL(iommufd_access_detach, IOMMUFD);
830e23a6217SNicolin Chen
iommufd_access_attach(struct iommufd_access * access,u32 ioas_id)83154b47585SNicolin Chen int iommufd_access_attach(struct iommufd_access *access, u32 ioas_id)
83254b47585SNicolin Chen {
8339227da78SNicolin Chen int rc;
83454b47585SNicolin Chen
835e23a6217SNicolin Chen mutex_lock(&access->ioas_lock);
8369227da78SNicolin Chen if (WARN_ON(access->ioas)) {
837e23a6217SNicolin Chen mutex_unlock(&access->ioas_lock);
83854b47585SNicolin Chen return -EINVAL;
839e23a6217SNicolin Chen }
84054b47585SNicolin Chen
8419227da78SNicolin Chen rc = iommufd_access_change_ioas_id(access, ioas_id);
842e23a6217SNicolin Chen mutex_unlock(&access->ioas_lock);
84354b47585SNicolin Chen return rc;
84454b47585SNicolin Chen }
84554b47585SNicolin Chen EXPORT_SYMBOL_NS_GPL(iommufd_access_attach, IOMMUFD);
84654b47585SNicolin Chen
iommufd_access_replace(struct iommufd_access * access,u32 ioas_id)84770c16123SNicolin Chen int iommufd_access_replace(struct iommufd_access *access, u32 ioas_id)
84870c16123SNicolin Chen {
84970c16123SNicolin Chen int rc;
85070c16123SNicolin Chen
85170c16123SNicolin Chen mutex_lock(&access->ioas_lock);
85270c16123SNicolin Chen if (!access->ioas) {
85370c16123SNicolin Chen mutex_unlock(&access->ioas_lock);
85470c16123SNicolin Chen return -ENOENT;
85570c16123SNicolin Chen }
85670c16123SNicolin Chen rc = iommufd_access_change_ioas_id(access, ioas_id);
85770c16123SNicolin Chen mutex_unlock(&access->ioas_lock);
85870c16123SNicolin Chen return rc;
85970c16123SNicolin Chen }
86070c16123SNicolin Chen EXPORT_SYMBOL_NS_GPL(iommufd_access_replace, IOMMUFD);
86170c16123SNicolin Chen
8628d40205fSJason Gunthorpe /**
8638d40205fSJason Gunthorpe * iommufd_access_notify_unmap - Notify users of an iopt to stop using it
8648d40205fSJason Gunthorpe * @iopt: iopt to work on
8658d40205fSJason Gunthorpe * @iova: Starting iova in the iopt
8668d40205fSJason Gunthorpe * @length: Number of bytes
8678d40205fSJason Gunthorpe *
8688d40205fSJason Gunthorpe * After this function returns there should be no users attached to the pages
8698d40205fSJason Gunthorpe * linked to this iopt that intersect with iova,length. Anyone that has attached
8708d40205fSJason Gunthorpe * a user through iopt_access_pages() needs to detach it through
8718d40205fSJason Gunthorpe * iommufd_access_unpin_pages() before this function returns.
8728d40205fSJason Gunthorpe *
8738d40205fSJason Gunthorpe * iommufd_access_destroy() will wait for any outstanding unmap callback to
8748d40205fSJason Gunthorpe * complete. Once iommufd_access_destroy() no unmap ops are running or will
8758d40205fSJason Gunthorpe * run in the future. Due to this a driver must not create locking that prevents
8768d40205fSJason Gunthorpe * unmap to complete while iommufd_access_destroy() is running.
8778d40205fSJason Gunthorpe */
iommufd_access_notify_unmap(struct io_pagetable * iopt,unsigned long iova,unsigned long length)8788d40205fSJason Gunthorpe void iommufd_access_notify_unmap(struct io_pagetable *iopt, unsigned long iova,
8798d40205fSJason Gunthorpe unsigned long length)
8808d40205fSJason Gunthorpe {
8818d40205fSJason Gunthorpe struct iommufd_ioas *ioas =
8828d40205fSJason Gunthorpe container_of(iopt, struct iommufd_ioas, iopt);
8838d40205fSJason Gunthorpe struct iommufd_access *access;
8848d40205fSJason Gunthorpe unsigned long index;
8858d40205fSJason Gunthorpe
8868d40205fSJason Gunthorpe xa_lock(&ioas->iopt.access_list);
8878d40205fSJason Gunthorpe xa_for_each(&ioas->iopt.access_list, index, access) {
8888d40205fSJason Gunthorpe if (!iommufd_lock_obj(&access->obj))
8898d40205fSJason Gunthorpe continue;
8908d40205fSJason Gunthorpe xa_unlock(&ioas->iopt.access_list);
8918d40205fSJason Gunthorpe
8928d40205fSJason Gunthorpe access->ops->unmap(access->data, iova, length);
8938d40205fSJason Gunthorpe
8948d40205fSJason Gunthorpe iommufd_put_object(&access->obj);
8958d40205fSJason Gunthorpe xa_lock(&ioas->iopt.access_list);
8968d40205fSJason Gunthorpe }
8978d40205fSJason Gunthorpe xa_unlock(&ioas->iopt.access_list);
8988d40205fSJason Gunthorpe }
8998d40205fSJason Gunthorpe
9008d40205fSJason Gunthorpe /**
9018d40205fSJason Gunthorpe * iommufd_access_unpin_pages() - Undo iommufd_access_pin_pages
9028d40205fSJason Gunthorpe * @access: IOAS access to act on
9038d40205fSJason Gunthorpe * @iova: Starting IOVA
9048d40205fSJason Gunthorpe * @length: Number of bytes to access
9058d40205fSJason Gunthorpe *
9068d40205fSJason Gunthorpe * Return the struct page's. The caller must stop accessing them before calling
9078d40205fSJason Gunthorpe * this. The iova/length must exactly match the one provided to access_pages.
9088d40205fSJason Gunthorpe */
iommufd_access_unpin_pages(struct iommufd_access * access,unsigned long iova,unsigned long length)9098d40205fSJason Gunthorpe void iommufd_access_unpin_pages(struct iommufd_access *access,
9108d40205fSJason Gunthorpe unsigned long iova, unsigned long length)
9118d40205fSJason Gunthorpe {
9128d40205fSJason Gunthorpe struct iopt_area_contig_iter iter;
913e23a6217SNicolin Chen struct io_pagetable *iopt;
9148d40205fSJason Gunthorpe unsigned long last_iova;
9158d40205fSJason Gunthorpe struct iopt_area *area;
9168d40205fSJason Gunthorpe
9178d40205fSJason Gunthorpe if (WARN_ON(!length) ||
9188d40205fSJason Gunthorpe WARN_ON(check_add_overflow(iova, length - 1, &last_iova)))
9198d40205fSJason Gunthorpe return;
9208d40205fSJason Gunthorpe
921e23a6217SNicolin Chen mutex_lock(&access->ioas_lock);
922e23a6217SNicolin Chen /*
923e23a6217SNicolin Chen * The driver must be doing something wrong if it calls this before an
924e23a6217SNicolin Chen * iommufd_access_attach() or after an iommufd_access_detach().
925e23a6217SNicolin Chen */
926e23a6217SNicolin Chen if (WARN_ON(!access->ioas_unpin)) {
927e23a6217SNicolin Chen mutex_unlock(&access->ioas_lock);
928e23a6217SNicolin Chen return;
929e23a6217SNicolin Chen }
930e23a6217SNicolin Chen iopt = &access->ioas_unpin->iopt;
931e23a6217SNicolin Chen
9328d40205fSJason Gunthorpe down_read(&iopt->iova_rwsem);
9338d40205fSJason Gunthorpe iopt_for_each_contig_area(&iter, area, iopt, iova, last_iova)
9348d40205fSJason Gunthorpe iopt_area_remove_access(
9358d40205fSJason Gunthorpe area, iopt_area_iova_to_index(area, iter.cur_iova),
9368d40205fSJason Gunthorpe iopt_area_iova_to_index(
9378d40205fSJason Gunthorpe area,
9388d40205fSJason Gunthorpe min(last_iova, iopt_area_last_iova(area))));
9398d40205fSJason Gunthorpe WARN_ON(!iopt_area_contig_done(&iter));
940dbe245cdSJason Gunthorpe up_read(&iopt->iova_rwsem);
941e23a6217SNicolin Chen mutex_unlock(&access->ioas_lock);
9428d40205fSJason Gunthorpe }
9438d40205fSJason Gunthorpe EXPORT_SYMBOL_NS_GPL(iommufd_access_unpin_pages, IOMMUFD);
9448d40205fSJason Gunthorpe
iopt_area_contig_is_aligned(struct iopt_area_contig_iter * iter)9458d40205fSJason Gunthorpe static bool iopt_area_contig_is_aligned(struct iopt_area_contig_iter *iter)
9468d40205fSJason Gunthorpe {
9478d40205fSJason Gunthorpe if (iopt_area_start_byte(iter->area, iter->cur_iova) % PAGE_SIZE)
9488d40205fSJason Gunthorpe return false;
9498d40205fSJason Gunthorpe
9508d40205fSJason Gunthorpe if (!iopt_area_contig_done(iter) &&
9518d40205fSJason Gunthorpe (iopt_area_start_byte(iter->area, iopt_area_last_iova(iter->area)) %
9528d40205fSJason Gunthorpe PAGE_SIZE) != (PAGE_SIZE - 1))
9538d40205fSJason Gunthorpe return false;
9548d40205fSJason Gunthorpe return true;
9558d40205fSJason Gunthorpe }
9568d40205fSJason Gunthorpe
check_area_prot(struct iopt_area * area,unsigned int flags)9578d40205fSJason Gunthorpe static bool check_area_prot(struct iopt_area *area, unsigned int flags)
9588d40205fSJason Gunthorpe {
9598d40205fSJason Gunthorpe if (flags & IOMMUFD_ACCESS_RW_WRITE)
9608d40205fSJason Gunthorpe return area->iommu_prot & IOMMU_WRITE;
9618d40205fSJason Gunthorpe return area->iommu_prot & IOMMU_READ;
9628d40205fSJason Gunthorpe }
9638d40205fSJason Gunthorpe
9648d40205fSJason Gunthorpe /**
9658d40205fSJason Gunthorpe * iommufd_access_pin_pages() - Return a list of pages under the iova
9668d40205fSJason Gunthorpe * @access: IOAS access to act on
9678d40205fSJason Gunthorpe * @iova: Starting IOVA
9688d40205fSJason Gunthorpe * @length: Number of bytes to access
9698d40205fSJason Gunthorpe * @out_pages: Output page list
9708d40205fSJason Gunthorpe * @flags: IOPMMUFD_ACCESS_RW_* flags
9718d40205fSJason Gunthorpe *
9728d40205fSJason Gunthorpe * Reads @length bytes starting at iova and returns the struct page * pointers.
9738d40205fSJason Gunthorpe * These can be kmap'd by the caller for CPU access.
9748d40205fSJason Gunthorpe *
9758d40205fSJason Gunthorpe * The caller must perform iommufd_access_unpin_pages() when done to balance
9768d40205fSJason Gunthorpe * this.
9778d40205fSJason Gunthorpe *
9788d40205fSJason Gunthorpe * This API always requires a page aligned iova. This happens naturally if the
9798d40205fSJason Gunthorpe * ioas alignment is >= PAGE_SIZE and the iova is PAGE_SIZE aligned. However
9808d40205fSJason Gunthorpe * smaller alignments have corner cases where this API can fail on otherwise
9818d40205fSJason Gunthorpe * aligned iova.
9828d40205fSJason Gunthorpe */
iommufd_access_pin_pages(struct iommufd_access * access,unsigned long iova,unsigned long length,struct page ** out_pages,unsigned int flags)9838d40205fSJason Gunthorpe int iommufd_access_pin_pages(struct iommufd_access *access, unsigned long iova,
9848d40205fSJason Gunthorpe unsigned long length, struct page **out_pages,
9858d40205fSJason Gunthorpe unsigned int flags)
9868d40205fSJason Gunthorpe {
9878d40205fSJason Gunthorpe struct iopt_area_contig_iter iter;
988e23a6217SNicolin Chen struct io_pagetable *iopt;
9898d40205fSJason Gunthorpe unsigned long last_iova;
9908d40205fSJason Gunthorpe struct iopt_area *area;
9918d40205fSJason Gunthorpe int rc;
9928d40205fSJason Gunthorpe
99352f52858SJason Gunthorpe /* Driver's ops don't support pin_pages */
99452f52858SJason Gunthorpe if (IS_ENABLED(CONFIG_IOMMUFD_TEST) &&
99552f52858SJason Gunthorpe WARN_ON(access->iova_alignment != PAGE_SIZE || !access->ops->unmap))
99652f52858SJason Gunthorpe return -EINVAL;
99752f52858SJason Gunthorpe
9988d40205fSJason Gunthorpe if (!length)
9998d40205fSJason Gunthorpe return -EINVAL;
10008d40205fSJason Gunthorpe if (check_add_overflow(iova, length - 1, &last_iova))
10018d40205fSJason Gunthorpe return -EOVERFLOW;
10028d40205fSJason Gunthorpe
1003e23a6217SNicolin Chen mutex_lock(&access->ioas_lock);
1004e23a6217SNicolin Chen if (!access->ioas) {
1005e23a6217SNicolin Chen mutex_unlock(&access->ioas_lock);
1006e23a6217SNicolin Chen return -ENOENT;
1007e23a6217SNicolin Chen }
1008e23a6217SNicolin Chen iopt = &access->ioas->iopt;
1009e23a6217SNicolin Chen
10108d40205fSJason Gunthorpe down_read(&iopt->iova_rwsem);
10118d40205fSJason Gunthorpe iopt_for_each_contig_area(&iter, area, iopt, iova, last_iova) {
10128d40205fSJason Gunthorpe unsigned long last = min(last_iova, iopt_area_last_iova(area));
10138d40205fSJason Gunthorpe unsigned long last_index = iopt_area_iova_to_index(area, last);
10148d40205fSJason Gunthorpe unsigned long index =
10158d40205fSJason Gunthorpe iopt_area_iova_to_index(area, iter.cur_iova);
10168d40205fSJason Gunthorpe
10178d40205fSJason Gunthorpe if (area->prevent_access ||
10188d40205fSJason Gunthorpe !iopt_area_contig_is_aligned(&iter)) {
10198d40205fSJason Gunthorpe rc = -EINVAL;
10208d40205fSJason Gunthorpe goto err_remove;
10218d40205fSJason Gunthorpe }
10228d40205fSJason Gunthorpe
10238d40205fSJason Gunthorpe if (!check_area_prot(area, flags)) {
10248d40205fSJason Gunthorpe rc = -EPERM;
10258d40205fSJason Gunthorpe goto err_remove;
10268d40205fSJason Gunthorpe }
10278d40205fSJason Gunthorpe
10288d40205fSJason Gunthorpe rc = iopt_area_add_access(area, index, last_index, out_pages,
10298d40205fSJason Gunthorpe flags);
10308d40205fSJason Gunthorpe if (rc)
10318d40205fSJason Gunthorpe goto err_remove;
10328d40205fSJason Gunthorpe out_pages += last_index - index + 1;
10338d40205fSJason Gunthorpe }
10348d40205fSJason Gunthorpe if (!iopt_area_contig_done(&iter)) {
10358d40205fSJason Gunthorpe rc = -ENOENT;
10368d40205fSJason Gunthorpe goto err_remove;
10378d40205fSJason Gunthorpe }
10388d40205fSJason Gunthorpe
10398d40205fSJason Gunthorpe up_read(&iopt->iova_rwsem);
1040e23a6217SNicolin Chen mutex_unlock(&access->ioas_lock);
10418d40205fSJason Gunthorpe return 0;
10428d40205fSJason Gunthorpe
10438d40205fSJason Gunthorpe err_remove:
10448d40205fSJason Gunthorpe if (iova < iter.cur_iova) {
10458d40205fSJason Gunthorpe last_iova = iter.cur_iova - 1;
10468d40205fSJason Gunthorpe iopt_for_each_contig_area(&iter, area, iopt, iova, last_iova)
10478d40205fSJason Gunthorpe iopt_area_remove_access(
10488d40205fSJason Gunthorpe area,
10498d40205fSJason Gunthorpe iopt_area_iova_to_index(area, iter.cur_iova),
10508d40205fSJason Gunthorpe iopt_area_iova_to_index(
10518d40205fSJason Gunthorpe area, min(last_iova,
10528d40205fSJason Gunthorpe iopt_area_last_iova(area))));
10538d40205fSJason Gunthorpe }
10548d40205fSJason Gunthorpe up_read(&iopt->iova_rwsem);
1055e23a6217SNicolin Chen mutex_unlock(&access->ioas_lock);
10568d40205fSJason Gunthorpe return rc;
10578d40205fSJason Gunthorpe }
10588d40205fSJason Gunthorpe EXPORT_SYMBOL_NS_GPL(iommufd_access_pin_pages, IOMMUFD);
10598d40205fSJason Gunthorpe
10608d40205fSJason Gunthorpe /**
10618d40205fSJason Gunthorpe * iommufd_access_rw - Read or write data under the iova
10628d40205fSJason Gunthorpe * @access: IOAS access to act on
10638d40205fSJason Gunthorpe * @iova: Starting IOVA
10648d40205fSJason Gunthorpe * @data: Kernel buffer to copy to/from
10658d40205fSJason Gunthorpe * @length: Number of bytes to access
10668d40205fSJason Gunthorpe * @flags: IOMMUFD_ACCESS_RW_* flags
10678d40205fSJason Gunthorpe *
10688d40205fSJason Gunthorpe * Copy kernel to/from data into the range given by IOVA/length. If flags
10698d40205fSJason Gunthorpe * indicates IOMMUFD_ACCESS_RW_KTHREAD then a large copy can be optimized
10708d40205fSJason Gunthorpe * by changing it into copy_to/from_user().
10718d40205fSJason Gunthorpe */
iommufd_access_rw(struct iommufd_access * access,unsigned long iova,void * data,size_t length,unsigned int flags)10728d40205fSJason Gunthorpe int iommufd_access_rw(struct iommufd_access *access, unsigned long iova,
10738d40205fSJason Gunthorpe void *data, size_t length, unsigned int flags)
10748d40205fSJason Gunthorpe {
10758d40205fSJason Gunthorpe struct iopt_area_contig_iter iter;
1076e23a6217SNicolin Chen struct io_pagetable *iopt;
10778d40205fSJason Gunthorpe struct iopt_area *area;
10788d40205fSJason Gunthorpe unsigned long last_iova;
10798d40205fSJason Gunthorpe int rc;
10808d40205fSJason Gunthorpe
10818d40205fSJason Gunthorpe if (!length)
10828d40205fSJason Gunthorpe return -EINVAL;
10838d40205fSJason Gunthorpe if (check_add_overflow(iova, length - 1, &last_iova))
10848d40205fSJason Gunthorpe return -EOVERFLOW;
10858d40205fSJason Gunthorpe
1086e23a6217SNicolin Chen mutex_lock(&access->ioas_lock);
1087e23a6217SNicolin Chen if (!access->ioas) {
1088e23a6217SNicolin Chen mutex_unlock(&access->ioas_lock);
1089e23a6217SNicolin Chen return -ENOENT;
1090e23a6217SNicolin Chen }
1091e23a6217SNicolin Chen iopt = &access->ioas->iopt;
1092e23a6217SNicolin Chen
10938d40205fSJason Gunthorpe down_read(&iopt->iova_rwsem);
10948d40205fSJason Gunthorpe iopt_for_each_contig_area(&iter, area, iopt, iova, last_iova) {
10958d40205fSJason Gunthorpe unsigned long last = min(last_iova, iopt_area_last_iova(area));
10968d40205fSJason Gunthorpe unsigned long bytes = (last - iter.cur_iova) + 1;
10978d40205fSJason Gunthorpe
10988d40205fSJason Gunthorpe if (area->prevent_access) {
10998d40205fSJason Gunthorpe rc = -EINVAL;
11008d40205fSJason Gunthorpe goto err_out;
11018d40205fSJason Gunthorpe }
11028d40205fSJason Gunthorpe
11038d40205fSJason Gunthorpe if (!check_area_prot(area, flags)) {
11048d40205fSJason Gunthorpe rc = -EPERM;
11058d40205fSJason Gunthorpe goto err_out;
11068d40205fSJason Gunthorpe }
11078d40205fSJason Gunthorpe
11088d40205fSJason Gunthorpe rc = iopt_pages_rw_access(
11098d40205fSJason Gunthorpe area->pages, iopt_area_start_byte(area, iter.cur_iova),
11108d40205fSJason Gunthorpe data, bytes, flags);
11118d40205fSJason Gunthorpe if (rc)
11128d40205fSJason Gunthorpe goto err_out;
11138d40205fSJason Gunthorpe data += bytes;
11148d40205fSJason Gunthorpe }
11158d40205fSJason Gunthorpe if (!iopt_area_contig_done(&iter))
11168d40205fSJason Gunthorpe rc = -ENOENT;
11178d40205fSJason Gunthorpe err_out:
11188d40205fSJason Gunthorpe up_read(&iopt->iova_rwsem);
1119e23a6217SNicolin Chen mutex_unlock(&access->ioas_lock);
11208d40205fSJason Gunthorpe return rc;
11218d40205fSJason Gunthorpe }
11228d40205fSJason Gunthorpe EXPORT_SYMBOL_NS_GPL(iommufd_access_rw, IOMMUFD);
1123*55dd4023SYi Liu
iommufd_get_hw_info(struct iommufd_ucmd * ucmd)1124*55dd4023SYi Liu int iommufd_get_hw_info(struct iommufd_ucmd *ucmd)
1125*55dd4023SYi Liu {
1126*55dd4023SYi Liu struct iommu_hw_info *cmd = ucmd->cmd;
1127*55dd4023SYi Liu void __user *user_ptr = u64_to_user_ptr(cmd->data_uptr);
1128*55dd4023SYi Liu const struct iommu_ops *ops;
1129*55dd4023SYi Liu struct iommufd_device *idev;
1130*55dd4023SYi Liu unsigned int data_len;
1131*55dd4023SYi Liu unsigned int copy_len;
1132*55dd4023SYi Liu void *data;
1133*55dd4023SYi Liu int rc;
1134*55dd4023SYi Liu
1135*55dd4023SYi Liu if (cmd->flags || cmd->__reserved)
1136*55dd4023SYi Liu return -EOPNOTSUPP;
1137*55dd4023SYi Liu
1138*55dd4023SYi Liu idev = iommufd_get_device(ucmd, cmd->dev_id);
1139*55dd4023SYi Liu if (IS_ERR(idev))
1140*55dd4023SYi Liu return PTR_ERR(idev);
1141*55dd4023SYi Liu
1142*55dd4023SYi Liu ops = dev_iommu_ops(idev->dev);
1143*55dd4023SYi Liu if (ops->hw_info) {
1144*55dd4023SYi Liu data = ops->hw_info(idev->dev, &data_len, &cmd->out_data_type);
1145*55dd4023SYi Liu if (IS_ERR(data)) {
1146*55dd4023SYi Liu rc = PTR_ERR(data);
1147*55dd4023SYi Liu goto out_put;
1148*55dd4023SYi Liu }
1149*55dd4023SYi Liu
1150*55dd4023SYi Liu /*
1151*55dd4023SYi Liu * drivers that have hw_info callback should have a unique
1152*55dd4023SYi Liu * iommu_hw_info_type.
1153*55dd4023SYi Liu */
1154*55dd4023SYi Liu if (WARN_ON_ONCE(cmd->out_data_type ==
1155*55dd4023SYi Liu IOMMU_HW_INFO_TYPE_NONE)) {
1156*55dd4023SYi Liu rc = -ENODEV;
1157*55dd4023SYi Liu goto out_free;
1158*55dd4023SYi Liu }
1159*55dd4023SYi Liu } else {
1160*55dd4023SYi Liu cmd->out_data_type = IOMMU_HW_INFO_TYPE_NONE;
1161*55dd4023SYi Liu data_len = 0;
1162*55dd4023SYi Liu data = NULL;
1163*55dd4023SYi Liu }
1164*55dd4023SYi Liu
1165*55dd4023SYi Liu copy_len = min(cmd->data_len, data_len);
1166*55dd4023SYi Liu if (copy_to_user(user_ptr, data, copy_len)) {
1167*55dd4023SYi Liu rc = -EFAULT;
1168*55dd4023SYi Liu goto out_free;
1169*55dd4023SYi Liu }
1170*55dd4023SYi Liu
1171*55dd4023SYi Liu /*
1172*55dd4023SYi Liu * Zero the trailing bytes if the user buffer is bigger than the
1173*55dd4023SYi Liu * data size kernel actually has.
1174*55dd4023SYi Liu */
1175*55dd4023SYi Liu if (copy_len < cmd->data_len) {
1176*55dd4023SYi Liu if (clear_user(user_ptr + copy_len, cmd->data_len - copy_len)) {
1177*55dd4023SYi Liu rc = -EFAULT;
1178*55dd4023SYi Liu goto out_free;
1179*55dd4023SYi Liu }
1180*55dd4023SYi Liu }
1181*55dd4023SYi Liu
1182*55dd4023SYi Liu /*
1183*55dd4023SYi Liu * We return the length the kernel supports so userspace may know what
1184*55dd4023SYi Liu * the kernel capability is. It could be larger than the input buffer.
1185*55dd4023SYi Liu */
1186*55dd4023SYi Liu cmd->data_len = data_len;
1187*55dd4023SYi Liu
1188*55dd4023SYi Liu rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
1189*55dd4023SYi Liu out_free:
1190*55dd4023SYi Liu kfree(data);
1191*55dd4023SYi Liu out_put:
1192*55dd4023SYi Liu iommufd_put_object(&idev->obj);
1193*55dd4023SYi Liu return rc;
1194*55dd4023SYi Liu }
1195