Merge branches 'x86/vt-d', 'x86/amd', 'arm/smmu', 'arm/omap', 'generic-dma-ops' and...
[linux-block.git] / drivers / iommu / iommu.c
index 67ee6623f9b2a4d07dcae56dfb99fcf6e4edbb0d..0c674d80c37fd5768fa62296f6be72f97c5dd245 100644 (file)
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * Copyright (C) 2007-2008 Advanced Micro Devices, Inc.
  * Author: Joerg Roedel <jroedel@suse.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
 #define pr_fmt(fmt)    "iommu: " fmt
@@ -73,10 +61,11 @@ struct iommu_group_attribute {
 };
 
 static const char * const iommu_group_resv_type_string[] = {
-       [IOMMU_RESV_DIRECT]     = "direct",
-       [IOMMU_RESV_RESERVED]   = "reserved",
-       [IOMMU_RESV_MSI]        = "msi",
-       [IOMMU_RESV_SW_MSI]     = "msi",
+       [IOMMU_RESV_DIRECT]                     = "direct",
+       [IOMMU_RESV_DIRECT_RELAXABLE]           = "direct-relaxable",
+       [IOMMU_RESV_RESERVED]                   = "reserved",
+       [IOMMU_RESV_MSI]                        = "msi",
+       [IOMMU_RESV_SW_MSI]                     = "msi",
 };
 
 #define IOMMU_GROUP_ATTR(_name, _mode, _show, _store)          \
@@ -107,15 +96,43 @@ void iommu_device_unregister(struct iommu_device *iommu)
        spin_unlock(&iommu_device_lock);
 }
 
+static struct iommu_param *iommu_get_dev_param(struct device *dev)
+{
+       struct iommu_param *param = dev->iommu_param;
+
+       if (param)
+               return param;
+
+       param = kzalloc(sizeof(*param), GFP_KERNEL);
+       if (!param)
+               return NULL;
+
+       mutex_init(&param->lock);
+       dev->iommu_param = param;
+       return param;
+}
+
+static void iommu_free_dev_param(struct device *dev)
+{
+       kfree(dev->iommu_param);
+       dev->iommu_param = NULL;
+}
+
 int iommu_probe_device(struct device *dev)
 {
        const struct iommu_ops *ops = dev->bus->iommu_ops;
-       int ret = -EINVAL;
+       int ret;
 
        WARN_ON(dev->iommu_group);
+       if (!ops)
+               return -EINVAL;
+
+       if (!iommu_get_dev_param(dev))
+               return -ENOMEM;
 
-       if (ops)
-               ret = ops->add_device(dev);
+       ret = ops->add_device(dev);
+       if (ret)
+               iommu_free_dev_param(dev);
 
        return ret;
 }
@@ -126,6 +143,8 @@ void iommu_release_device(struct device *dev)
 
        if (dev->iommu_group)
                ops->remove_device(dev);
+
+       iommu_free_dev_param(dev);
 }
 
 static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus,
@@ -237,18 +256,21 @@ static int iommu_insert_resv_region(struct iommu_resv_region *new,
                        pos = pos->next;
                } else if ((start >= a) && (end <= b)) {
                        if (new->type == type)
-                               goto done;
+                               return 0;
                        else
                                pos = pos->next;
                } else {
                        if (new->type == type) {
                                phys_addr_t new_start = min(a, start);
                                phys_addr_t new_end = max(b, end);
+                               int ret;
 
                                list_del(&entry->list);
                                entry->start = new_start;
                                entry->length = new_end - new_start + 1;
-                               iommu_insert_resv_region(entry, regions);
+                               ret = iommu_insert_resv_region(entry, regions);
+                               kfree(entry);
+                               return ret;
                        } else {
                                pos = pos->next;
                        }
@@ -261,7 +283,6 @@ insert:
                return -ENOMEM;
 
        list_add_tail(&region->list, pos);
-done:
        return 0;
 }
 
@@ -341,7 +362,7 @@ static ssize_t iommu_group_show_type(struct iommu_group *group,
                        type = "unmanaged\n";
                        break;
                case IOMMU_DOMAIN_DMA:
-                       type = "DMA";
+                       type = "DMA\n";
                        break;
                }
        }
@@ -573,7 +594,8 @@ static int iommu_group_create_direct_mappings(struct iommu_group *group,
                start = ALIGN(entry->start, pg_size);
                end   = ALIGN(entry->start + entry->length, pg_size);
 
-               if (entry->type != IOMMU_RESV_DIRECT)
+               if (entry->type != IOMMU_RESV_DIRECT &&
+                   entry->type != IOMMU_RESV_DIRECT_RELAXABLE)
                        continue;
 
                for (addr = start; addr < end; addr += pg_size) {
@@ -854,6 +876,206 @@ int iommu_group_unregister_notifier(struct iommu_group *group,
 }
 EXPORT_SYMBOL_GPL(iommu_group_unregister_notifier);
 
+/**
+ * iommu_register_device_fault_handler() - Register a device fault handler
+ * @dev: the device
+ * @handler: the fault handler
+ * @data: private data passed as argument to the handler
+ *
+ * When an IOMMU fault event is received, this handler gets called with the
+ * fault event and data as argument. The handler should return 0 on success. If
+ * the fault is recoverable (IOMMU_FAULT_PAGE_REQ), the consumer should also
+ * complete the fault by calling iommu_page_response() with one of the following
+ * response code:
+ * - IOMMU_PAGE_RESP_SUCCESS: retry the translation
+ * - IOMMU_PAGE_RESP_INVALID: terminate the fault
+ * - IOMMU_PAGE_RESP_FAILURE: terminate the fault and stop reporting
+ *   page faults if possible.
+ *
+ * Return 0 if the fault handler was installed successfully, or an error.
+ */
+int iommu_register_device_fault_handler(struct device *dev,
+                                       iommu_dev_fault_handler_t handler,
+                                       void *data)
+{
+       struct iommu_param *param = dev->iommu_param;
+       int ret = 0;
+
+       if (!param)
+               return -EINVAL;
+
+       mutex_lock(&param->lock);
+       /* Only allow one fault handler registered for each device */
+       if (param->fault_param) {
+               ret = -EBUSY;
+               goto done_unlock;
+       }
+
+       get_device(dev);
+       param->fault_param = kzalloc(sizeof(*param->fault_param), GFP_KERNEL);
+       if (!param->fault_param) {
+               put_device(dev);
+               ret = -ENOMEM;
+               goto done_unlock;
+       }
+       param->fault_param->handler = handler;
+       param->fault_param->data = data;
+       mutex_init(&param->fault_param->lock);
+       INIT_LIST_HEAD(&param->fault_param->faults);
+
+done_unlock:
+       mutex_unlock(&param->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(iommu_register_device_fault_handler);
+
+/**
+ * iommu_unregister_device_fault_handler() - Unregister the device fault handler
+ * @dev: the device
+ *
+ * Remove the device fault handler installed with
+ * iommu_register_device_fault_handler().
+ *
+ * Return 0 on success, or an error.
+ */
+int iommu_unregister_device_fault_handler(struct device *dev)
+{
+       struct iommu_param *param = dev->iommu_param;
+       int ret = 0;
+
+       if (!param)
+               return -EINVAL;
+
+       mutex_lock(&param->lock);
+
+       if (!param->fault_param)
+               goto unlock;
+
+       /* we cannot unregister handler if there are pending faults */
+       if (!list_empty(&param->fault_param->faults)) {
+               ret = -EBUSY;
+               goto unlock;
+       }
+
+       kfree(param->fault_param);
+       param->fault_param = NULL;
+       put_device(dev);
+unlock:
+       mutex_unlock(&param->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(iommu_unregister_device_fault_handler);
+
+/**
+ * iommu_report_device_fault() - Report fault event to device driver
+ * @dev: the device
+ * @evt: fault event data
+ *
+ * Called by IOMMU drivers when a fault is detected, typically in a threaded IRQ
+ * handler. When this function fails and the fault is recoverable, it is the
+ * caller's responsibility to complete the fault.
+ *
+ * Return 0 on success, or an error.
+ */
+int iommu_report_device_fault(struct device *dev, struct iommu_fault_event *evt)
+{
+       struct iommu_param *param = dev->iommu_param;
+       struct iommu_fault_event *evt_pending = NULL;
+       struct iommu_fault_param *fparam;
+       int ret = 0;
+
+       if (!param || !evt)
+               return -EINVAL;
+
+       /* we only report device fault if there is a handler registered */
+       mutex_lock(&param->lock);
+       fparam = param->fault_param;
+       if (!fparam || !fparam->handler) {
+               ret = -EINVAL;
+               goto done_unlock;
+       }
+
+       if (evt->fault.type == IOMMU_FAULT_PAGE_REQ &&
+           (evt->fault.prm.flags & IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE)) {
+               evt_pending = kmemdup(evt, sizeof(struct iommu_fault_event),
+                                     GFP_KERNEL);
+               if (!evt_pending) {
+                       ret = -ENOMEM;
+                       goto done_unlock;
+               }
+               mutex_lock(&fparam->lock);
+               list_add_tail(&evt_pending->list, &fparam->faults);
+               mutex_unlock(&fparam->lock);
+       }
+
+       ret = fparam->handler(&evt->fault, fparam->data);
+       if (ret && evt_pending) {
+               mutex_lock(&fparam->lock);
+               list_del(&evt_pending->list);
+               mutex_unlock(&fparam->lock);
+               kfree(evt_pending);
+       }
+done_unlock:
+       mutex_unlock(&param->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(iommu_report_device_fault);
+
+int iommu_page_response(struct device *dev,
+                       struct iommu_page_response *msg)
+{
+       bool pasid_valid;
+       int ret = -EINVAL;
+       struct iommu_fault_event *evt;
+       struct iommu_fault_page_request *prm;
+       struct iommu_param *param = dev->iommu_param;
+       struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
+
+       if (!domain || !domain->ops->page_response)
+               return -ENODEV;
+
+       if (!param || !param->fault_param)
+               return -EINVAL;
+
+       if (msg->version != IOMMU_PAGE_RESP_VERSION_1 ||
+           msg->flags & ~IOMMU_PAGE_RESP_PASID_VALID)
+               return -EINVAL;
+
+       /* Only send response if there is a fault report pending */
+       mutex_lock(&param->fault_param->lock);
+       if (list_empty(&param->fault_param->faults)) {
+               dev_warn_ratelimited(dev, "no pending PRQ, drop response\n");
+               goto done_unlock;
+       }
+       /*
+        * Check if we have a matching page request pending to respond,
+        * otherwise return -EINVAL
+        */
+       list_for_each_entry(evt, &param->fault_param->faults, list) {
+               prm = &evt->fault.prm;
+               pasid_valid = prm->flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID;
+
+               if ((pasid_valid && prm->pasid != msg->pasid) ||
+                   prm->grpid != msg->grpid)
+                       continue;
+
+               /* Sanitize the reply */
+               msg->flags = pasid_valid ? IOMMU_PAGE_RESP_PASID_VALID : 0;
+
+               ret = domain->ops->page_response(dev, evt, msg);
+               list_del(&evt->list);
+               kfree(evt);
+               break;
+       }
+
+done_unlock:
+       mutex_unlock(&param->fault_param->lock);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(iommu_page_response);
+
 /**
  * iommu_group_id - Return ID for a group
  * @group: the group to ID
@@ -1907,24 +2129,23 @@ struct iommu_resv_region *iommu_alloc_resv_region(phys_addr_t start,
        return region;
 }
 
-/* Request that a device is direct mapped by the IOMMU */
-int iommu_request_dm_for_dev(struct device *dev)
+static int
+request_default_domain_for_dev(struct device *dev, unsigned long type)
 {
-       struct iommu_domain *dm_domain;
+       struct iommu_domain *domain;
        struct iommu_group *group;
        int ret;
 
        /* Device must already be in a group before calling this function */
-       group = iommu_group_get_for_dev(dev);
-       if (IS_ERR(group))
-               return PTR_ERR(group);
+       group = iommu_group_get(dev);
+       if (!group)
+               return -EINVAL;
 
        mutex_lock(&group->mutex);
 
        /* Check if the default domain is already direct mapped */
        ret = 0;
-       if (group->default_domain &&
-           group->default_domain->type == IOMMU_DOMAIN_IDENTITY)
+       if (group->default_domain && group->default_domain->type == type)
                goto out;
 
        /* Don't change mappings of existing devices */
@@ -1934,23 +2155,26 @@ int iommu_request_dm_for_dev(struct device *dev)
 
        /* Allocate a direct mapped domain */
        ret = -ENOMEM;
-       dm_domain = __iommu_domain_alloc(dev->bus, IOMMU_DOMAIN_IDENTITY);
-       if (!dm_domain)
+       domain = __iommu_domain_alloc(dev->bus, type);
+       if (!domain)
                goto out;
 
        /* Attach the device to the domain */
-       ret = __iommu_attach_group(dm_domain, group);
+       ret = __iommu_attach_group(domain, group);
        if (ret) {
-               iommu_domain_free(dm_domain);
+               iommu_domain_free(domain);
                goto out;
        }
 
+       iommu_group_create_direct_mappings(group, dev);
+
        /* Make the direct mapped domain the default for this group */
        if (group->default_domain)
                iommu_domain_free(group->default_domain);
-       group->default_domain = dm_domain;
+       group->default_domain = domain;
 
-       dev_info(dev, "Using iommu direct mapping\n");
+       dev_info(dev, "Using iommu %s mapping\n",
+                type == IOMMU_DOMAIN_DMA ? "dma" : "direct");
 
        ret = 0;
 out:
@@ -1960,6 +2184,18 @@ out:
        return ret;
 }
 
+/* Request that a device is direct mapped by the IOMMU */
+int iommu_request_dm_for_dev(struct device *dev)
+{
+       return request_default_domain_for_dev(dev, IOMMU_DOMAIN_IDENTITY);
+}
+
+/* Request that a device can't be direct mapped by the IOMMU */
+int iommu_request_dma_domain_for_dev(struct device *dev)
+{
+       return request_default_domain_for_dev(dev, IOMMU_DOMAIN_DMA);
+}
+
 const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode)
 {
        const struct iommu_ops *ops = NULL;