Merge branches 'arm/rockchip', 'arm/exynos', 'arm/smmu', 'x86/vt-d', 'x86/amd', ...
[cascardo/linux.git] / drivers / iommu / iommu.c
index ef73923..49e7542 100644 (file)
@@ -52,6 +52,7 @@ struct iommu_group {
        char *name;
        int id;
        struct iommu_domain *default_domain;
+       struct iommu_domain *domain;
 };
 
 struct iommu_device {
@@ -78,6 +79,12 @@ struct iommu_group_attribute iommu_group_attr_##_name =              \
 
 static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus,
                                                 unsigned type);
+static int __iommu_attach_device(struct iommu_domain *domain,
+                                struct device *dev);
+static int __iommu_attach_group(struct iommu_domain *domain,
+                               struct iommu_group *group);
+static void __iommu_detach_group(struct iommu_domain *domain,
+                                struct iommu_group *group);
 
 static ssize_t iommu_group_attr_show(struct kobject *kobj,
                                     struct attribute *__attr, char *buf)
@@ -318,6 +325,52 @@ int iommu_group_set_name(struct iommu_group *group, const char *name)
 }
 EXPORT_SYMBOL_GPL(iommu_group_set_name);
 
+static int iommu_group_create_direct_mappings(struct iommu_group *group,
+                                             struct device *dev)
+{
+       struct iommu_domain *domain = group->default_domain;
+       struct iommu_dm_region *entry;
+       struct list_head mappings;
+       unsigned long pg_size;
+       int ret = 0;
+
+       if (!domain || domain->type != IOMMU_DOMAIN_DMA)
+               return 0;
+
+       BUG_ON(!domain->ops->pgsize_bitmap);
+
+       pg_size = 1UL << __ffs(domain->ops->pgsize_bitmap);
+       INIT_LIST_HEAD(&mappings);
+
+       iommu_get_dm_regions(dev, &mappings);
+
+       /* We need to consider overlapping regions for different devices */
+       list_for_each_entry(entry, &mappings, list) {
+               dma_addr_t start, end, addr;
+
+               start = ALIGN(entry->start, pg_size);
+               end   = ALIGN(entry->start + entry->length, pg_size);
+
+               for (addr = start; addr < end; addr += pg_size) {
+                       phys_addr_t phys_addr;
+
+                       phys_addr = iommu_iova_to_phys(domain, addr);
+                       if (phys_addr)
+                               continue;
+
+                       ret = iommu_map(domain, addr, addr, pg_size, entry->prot);
+                       if (ret)
+                               goto out;
+               }
+
+       }
+
+out:
+       iommu_put_dm_regions(dev, &mappings);
+
+       return ret;
+}
+
 /**
  * iommu_group_add_device - add a device to an iommu group
  * @group: the group into which to add the device (reference should be held)
@@ -374,8 +427,12 @@ rename:
 
        dev->iommu_group = group;
 
+       iommu_group_create_direct_mappings(group, dev);
+
        mutex_lock(&group->mutex);
        list_add_tail(&device->list, &group->devices);
+       if (group->domain)
+               __iommu_attach_device(group->domain, dev);
        mutex_unlock(&group->mutex);
 
        /* Notify any listeners about change to group. */
@@ -455,19 +512,30 @@ static int iommu_group_device_count(struct iommu_group *group)
  * The group->mutex is held across callbacks, which will block calls to
  * iommu_group_add/remove_device.
  */
-int iommu_group_for_each_dev(struct iommu_group *group, void *data,
-                            int (*fn)(struct device *, void *))
+static int __iommu_group_for_each_dev(struct iommu_group *group, void *data,
+                                     int (*fn)(struct device *, void *))
 {
        struct iommu_device *device;
        int ret = 0;
 
-       mutex_lock(&group->mutex);
        list_for_each_entry(device, &group->devices, list) {
                ret = fn(device->dev, data);
                if (ret)
                        break;
        }
+       return ret;
+}
+
+
+int iommu_group_for_each_dev(struct iommu_group *group, void *data,
+                            int (*fn)(struct device *, void *))
+{
+       int ret;
+
+       mutex_lock(&group->mutex);
+       ret = __iommu_group_for_each_dev(group, data, fn);
        mutex_unlock(&group->mutex);
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(iommu_group_for_each_dev);
@@ -720,14 +788,16 @@ static struct iommu_group *iommu_group_get_for_pci_dev(struct pci_dev *pdev)
 
        /* No shared group found, allocate new */
        group = iommu_group_alloc();
-       if (group) {
-               /*
-                * Try to allocate a default domain - needs support from the
-                * IOMMU driver.
-                */
-               group->default_domain = __iommu_domain_alloc(pdev->dev.bus,
-                                                            IOMMU_DOMAIN_DMA);
-       }
+       if (IS_ERR(group))
+               return NULL;
+
+       /*
+        * Try to allocate a default domain - needs support from the
+        * IOMMU driver.
+        */
+       group->default_domain = __iommu_domain_alloc(pdev->dev.bus,
+                                                    IOMMU_DOMAIN_DMA);
+       group->domain = group->default_domain;
 
        return group;
 }
@@ -768,6 +838,11 @@ struct iommu_group *iommu_group_get_for_dev(struct device *dev)
        return group;
 }
 
+struct iommu_domain *iommu_group_default_domain(struct iommu_group *group)
+{
+       return group->default_domain;
+}
+
 static int add_iommu_group(struct device *dev, void *data)
 {
        struct iommu_callback_data *cb = data;
@@ -1012,7 +1087,7 @@ int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
        if (iommu_group_device_count(group) != 1)
                goto out_unlock;
 
-       ret = __iommu_attach_device(domain, dev);
+       ret = __iommu_attach_group(domain, group);
 
 out_unlock:
        mutex_unlock(&group->mutex);
@@ -1047,7 +1122,7 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
                goto out_unlock;
        }
 
-       __iommu_detach_device(domain, dev);
+       __iommu_detach_group(domain, group);
 
 out_unlock:
        mutex_unlock(&group->mutex);
@@ -1055,6 +1130,24 @@ out_unlock:
 }
 EXPORT_SYMBOL_GPL(iommu_detach_device);
 
+struct iommu_domain *iommu_get_domain_for_dev(struct device *dev)
+{
+       struct iommu_domain *domain;
+       struct iommu_group *group;
+
+       group = iommu_group_get(dev);
+       /* FIXME: Remove this when groups a mandatory for iommu drivers */
+       if (group == NULL)
+               return NULL;
+
+       domain = group->domain;
+
+       iommu_group_put(group);
+
+       return domain;
+}
+EXPORT_SYMBOL_GPL(iommu_get_domain_for_dev);
+
 /*
  * IOMMU groups are really the natrual working unit of the IOMMU, but
  * the IOMMU API works on domains and devices.  Bridge that gap by
@@ -1072,10 +1165,31 @@ static int iommu_group_do_attach_device(struct device *dev, void *data)
        return __iommu_attach_device(domain, dev);
 }
 
+static int __iommu_attach_group(struct iommu_domain *domain,
+                               struct iommu_group *group)
+{
+       int ret;
+
+       if (group->default_domain && group->domain != group->default_domain)
+               return -EBUSY;
+
+       ret = __iommu_group_for_each_dev(group, domain,
+                                        iommu_group_do_attach_device);
+       if (ret == 0)
+               group->domain = domain;
+
+       return ret;
+}
+
 int iommu_attach_group(struct iommu_domain *domain, struct iommu_group *group)
 {
-       return iommu_group_for_each_dev(group, domain,
-                                       iommu_group_do_attach_device);
+       int ret;
+
+       mutex_lock(&group->mutex);
+       ret = __iommu_attach_group(domain, group);
+       mutex_unlock(&group->mutex);
+
+       return ret;
 }
 EXPORT_SYMBOL_GPL(iommu_attach_group);
 
@@ -1088,9 +1202,35 @@ static int iommu_group_do_detach_device(struct device *dev, void *data)
        return 0;
 }
 
+static void __iommu_detach_group(struct iommu_domain *domain,
+                                struct iommu_group *group)
+{
+       int ret;
+
+       if (!group->default_domain) {
+               __iommu_group_for_each_dev(group, domain,
+                                          iommu_group_do_detach_device);
+               group->domain = NULL;
+               return;
+       }
+
+       if (group->domain == group->default_domain)
+               return;
+
+       /* Detach by re-attaching to the default domain */
+       ret = __iommu_group_for_each_dev(group, group->default_domain,
+                                        iommu_group_do_attach_device);
+       if (ret != 0)
+               WARN_ON(1);
+       else
+               group->domain = group->default_domain;
+}
+
 void iommu_detach_group(struct iommu_domain *domain, struct iommu_group *group)
 {
-       iommu_group_for_each_dev(group, domain, iommu_group_do_detach_device);
+       mutex_lock(&group->mutex);
+       __iommu_detach_group(domain, group);
+       mutex_unlock(&group->mutex);
 }
 EXPORT_SYMBOL_GPL(iommu_detach_group);
 
@@ -1317,7 +1457,7 @@ static int __init iommu_init(void)
 
        return 0;
 }
-arch_initcall(iommu_init);
+core_initcall(iommu_init);
 
 int iommu_domain_get_attr(struct iommu_domain *domain,
                          enum iommu_attr attr, void *data)
@@ -1383,3 +1523,72 @@ int iommu_domain_set_attr(struct iommu_domain *domain,
        return ret;
 }
 EXPORT_SYMBOL_GPL(iommu_domain_set_attr);
+
+void iommu_get_dm_regions(struct device *dev, struct list_head *list)
+{
+       const struct iommu_ops *ops = dev->bus->iommu_ops;
+
+       if (ops && ops->get_dm_regions)
+               ops->get_dm_regions(dev, list);
+}
+
+void iommu_put_dm_regions(struct device *dev, struct list_head *list)
+{
+       const struct iommu_ops *ops = dev->bus->iommu_ops;
+
+       if (ops && ops->put_dm_regions)
+               ops->put_dm_regions(dev, list);
+}
+
+/* Request that a device is direct mapped by the IOMMU */
+int iommu_request_dm_for_dev(struct device *dev)
+{
+       struct iommu_domain *dm_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);
+
+       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)
+               goto out;
+
+       /* Don't change mappings of existing devices */
+       ret = -EBUSY;
+       if (iommu_group_device_count(group) != 1)
+               goto out;
+
+       /* Allocate a direct mapped domain */
+       ret = -ENOMEM;
+       dm_domain = __iommu_domain_alloc(dev->bus, IOMMU_DOMAIN_IDENTITY);
+       if (!dm_domain)
+               goto out;
+
+       /* Attach the device to the domain */
+       ret = __iommu_attach_group(dm_domain, group);
+       if (ret) {
+               iommu_domain_free(dm_domain);
+               goto out;
+       }
+
+       /* 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;
+
+       pr_info("Using direct mapping for device %s\n", dev_name(dev));
+
+       ret = 0;
+out:
+       mutex_unlock(&group->mutex);
+       iommu_group_put(group);
+
+       return ret;
+}