iommu: Don't use sme_active() in generic code
[linux-2.6-microblaze.git] / drivers / iommu / iommu.c
index 0c674d8..d658c7c 100644 (file)
 
 static struct kset *iommu_group_kset;
 static DEFINE_IDA(iommu_group_ida);
-#ifdef CONFIG_IOMMU_DEFAULT_PASSTHROUGH
-static unsigned int iommu_def_domain_type = IOMMU_DOMAIN_IDENTITY;
-#else
-static unsigned int iommu_def_domain_type = IOMMU_DOMAIN_DMA;
-#endif
+
+static unsigned int iommu_def_domain_type __read_mostly;
 static bool iommu_dma_strict __read_mostly = true;
+static u32 iommu_cmd_line __read_mostly;
 
 struct iommu_group {
        struct kobject kobj;
@@ -68,6 +66,18 @@ static const char * const iommu_group_resv_type_string[] = {
        [IOMMU_RESV_SW_MSI]                     = "msi",
 };
 
+#define IOMMU_CMD_LINE_DMA_API         BIT(0)
+
+static void iommu_set_cmd_line_dma_api(void)
+{
+       iommu_cmd_line |= IOMMU_CMD_LINE_DMA_API;
+}
+
+static bool iommu_cmd_line_dma_api(void)
+{
+       return !!(iommu_cmd_line & IOMMU_CMD_LINE_DMA_API);
+}
+
 #define IOMMU_GROUP_ATTR(_name, _mode, _show, _store)          \
 struct iommu_group_attribute iommu_group_attr_##_name =                \
        __ATTR(_name, _mode, _show, _store)
@@ -80,12 +90,55 @@ struct iommu_group_attribute iommu_group_attr_##_name =             \
 static LIST_HEAD(iommu_device_list);
 static DEFINE_SPINLOCK(iommu_device_lock);
 
+/*
+ * Use a function instead of an array here because the domain-type is a
+ * bit-field, so an array would waste memory.
+ */
+static const char *iommu_domain_type_str(unsigned int t)
+{
+       switch (t) {
+       case IOMMU_DOMAIN_BLOCKED:
+               return "Blocked";
+       case IOMMU_DOMAIN_IDENTITY:
+               return "Passthrough";
+       case IOMMU_DOMAIN_UNMANAGED:
+               return "Unmanaged";
+       case IOMMU_DOMAIN_DMA:
+               return "Translated";
+       default:
+               return "Unknown";
+       }
+}
+
+static int __init iommu_subsys_init(void)
+{
+       bool cmd_line = iommu_cmd_line_dma_api();
+
+       if (!cmd_line) {
+               if (IS_ENABLED(CONFIG_IOMMU_DEFAULT_PASSTHROUGH))
+                       iommu_set_default_passthrough(false);
+               else
+                       iommu_set_default_translated(false);
+
+               if (iommu_default_passthrough() && mem_encrypt_active()) {
+                       pr_info("Memory encryption detected - Disabling default IOMMU Passthrough\n");
+                       iommu_set_default_translated(false);
+               }
+       }
+
+       pr_info("Default domain type: %s %s\n",
+               iommu_domain_type_str(iommu_def_domain_type),
+               cmd_line ? "(set via kernel command line)" : "");
+
+       return 0;
+}
+subsys_initcall(iommu_subsys_init);
+
 int iommu_device_register(struct iommu_device *iommu)
 {
        spin_lock(&iommu_device_lock);
        list_add_tail(&iommu->list, &iommu_device_list);
        spin_unlock(&iommu_device_lock);
-
        return 0;
 }
 
@@ -165,7 +218,11 @@ static int __init iommu_set_def_domain_type(char *str)
        if (ret)
                return ret;
 
-       iommu_def_domain_type = pt ? IOMMU_DOMAIN_IDENTITY : IOMMU_DOMAIN_DMA;
+       if (pt)
+               iommu_set_default_passthrough(true);
+       else
+               iommu_set_default_translated(true);
+
        return 0;
 }
 early_param("iommu.passthrough", iommu_set_def_domain_type);
@@ -229,60 +286,58 @@ static ssize_t iommu_group_show_name(struct iommu_group *group, char *buf)
  * @new: new region to insert
  * @regions: list of regions
  *
- * The new element is sorted by address with respect to the other
- * regions of the same type. In case it overlaps with another
- * region of the same type, regions are merged. In case it
- * overlaps with another region of different type, regions are
- * not merged.
+ * Elements are sorted by start address and overlapping segments
+ * of the same type are merged.
  */
-static int iommu_insert_resv_region(struct iommu_resv_region *new,
-                                   struct list_head *regions)
+int iommu_insert_resv_region(struct iommu_resv_region *new,
+                            struct list_head *regions)
 {
-       struct iommu_resv_region *region;
-       phys_addr_t start = new->start;
-       phys_addr_t end = new->start + new->length - 1;
-       struct list_head *pos = regions->next;
-
-       while (pos != regions) {
-               struct iommu_resv_region *entry =
-                       list_entry(pos, struct iommu_resv_region, list);
-               phys_addr_t a = entry->start;
-               phys_addr_t b = entry->start + entry->length - 1;
-               int type = entry->type;
-
-               if (end < a) {
-                       goto insert;
-               } else if (start > b) {
-                       pos = pos->next;
-               } else if ((start >= a) && (end <= b)) {
-                       if (new->type == type)
-                               return 0;
-                       else
-                               pos = pos->next;
+       struct iommu_resv_region *iter, *tmp, *nr, *top;
+       LIST_HEAD(stack);
+
+       nr = iommu_alloc_resv_region(new->start, new->length,
+                                    new->prot, new->type);
+       if (!nr)
+               return -ENOMEM;
+
+       /* First add the new element based on start address sorting */
+       list_for_each_entry(iter, regions, list) {
+               if (nr->start < iter->start ||
+                   (nr->start == iter->start && nr->type <= iter->type))
+                       break;
+       }
+       list_add_tail(&nr->list, &iter->list);
+
+       /* Merge overlapping segments of type nr->type in @regions, if any */
+       list_for_each_entry_safe(iter, tmp, regions, list) {
+               phys_addr_t top_end, iter_end = iter->start + iter->length - 1;
+
+               /* no merge needed on elements of different types than @nr */
+               if (iter->type != nr->type) {
+                       list_move_tail(&iter->list, &stack);
+                       continue;
+               }
+
+               /* look for the last stack element of same type as @iter */
+               list_for_each_entry_reverse(top, &stack, list)
+                       if (top->type == iter->type)
+                               goto check_overlap;
+
+               list_move_tail(&iter->list, &stack);
+               continue;
+
+check_overlap:
+               top_end = top->start + top->length - 1;
+
+               if (iter->start > top_end + 1) {
+                       list_move_tail(&iter->list, &stack);
                } 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;
-                               ret = iommu_insert_resv_region(entry, regions);
-                               kfree(entry);
-                               return ret;
-                       } else {
-                               pos = pos->next;
-                       }
+                       top->length = max(top_end, iter_end) - top->start + 1;
+                       list_del(&iter->list);
+                       kfree(iter);
                }
        }
-insert:
-       region = iommu_alloc_resv_region(new->start, new->length,
-                                        new->prot, new->type);
-       if (!region)
-               return -ENOMEM;
-
-       list_add_tail(&region->list, pos);
+       list_splice(&stack, regions);
        return 0;
 }
 
@@ -1862,7 +1917,7 @@ EXPORT_SYMBOL_GPL(iommu_map);
 
 static size_t __iommu_unmap(struct iommu_domain *domain,
                            unsigned long iova, size_t size,
-                           bool sync)
+                           struct iommu_iotlb_gather *iotlb_gather)
 {
        const struct iommu_ops *ops = domain->ops;
        size_t unmapped_page, unmapped = 0;
@@ -1899,13 +1954,10 @@ static size_t __iommu_unmap(struct iommu_domain *domain,
        while (unmapped < size) {
                size_t pgsize = iommu_pgsize(domain, iova, size - unmapped);
 
-               unmapped_page = ops->unmap(domain, iova, pgsize);
+               unmapped_page = ops->unmap(domain, iova, pgsize, iotlb_gather);
                if (!unmapped_page)
                        break;
 
-               if (sync && ops->iotlb_range_add)
-                       ops->iotlb_range_add(domain, iova, pgsize);
-
                pr_debug("unmapped: iova 0x%lx size 0x%zx\n",
                         iova, unmapped_page);
 
@@ -1913,9 +1965,6 @@ static size_t __iommu_unmap(struct iommu_domain *domain,
                unmapped += unmapped_page;
        }
 
-       if (sync && ops->iotlb_sync)
-               ops->iotlb_sync(domain);
-
        trace_unmap(orig_iova, size, unmapped);
        return unmapped;
 }
@@ -1923,14 +1972,22 @@ static size_t __iommu_unmap(struct iommu_domain *domain,
 size_t iommu_unmap(struct iommu_domain *domain,
                   unsigned long iova, size_t size)
 {
-       return __iommu_unmap(domain, iova, size, true);
+       struct iommu_iotlb_gather iotlb_gather;
+       size_t ret;
+
+       iommu_iotlb_gather_init(&iotlb_gather);
+       ret = __iommu_unmap(domain, iova, size, &iotlb_gather);
+       iommu_tlb_sync(domain, &iotlb_gather);
+
+       return ret;
 }
 EXPORT_SYMBOL_GPL(iommu_unmap);
 
 size_t iommu_unmap_fast(struct iommu_domain *domain,
-                       unsigned long iova, size_t size)
+                       unsigned long iova, size_t size,
+                       struct iommu_iotlb_gather *iotlb_gather)
 {
-       return __iommu_unmap(domain, iova, size, false);
+       return __iommu_unmap(domain, iova, size, iotlb_gather);
 }
 EXPORT_SYMBOL_GPL(iommu_unmap_fast);
 
@@ -2143,7 +2200,6 @@ request_default_domain_for_dev(struct device *dev, unsigned long type)
 
        mutex_lock(&group->mutex);
 
-       /* Check if the default domain is already direct mapped */
        ret = 0;
        if (group->default_domain && group->default_domain->type == type)
                goto out;
@@ -2153,7 +2209,6 @@ request_default_domain_for_dev(struct device *dev, unsigned long type)
        if (iommu_group_device_count(group) != 1)
                goto out;
 
-       /* Allocate a direct mapped domain */
        ret = -ENOMEM;
        domain = __iommu_domain_alloc(dev->bus, type);
        if (!domain)
@@ -2168,7 +2223,7 @@ request_default_domain_for_dev(struct device *dev, unsigned long type)
 
        iommu_group_create_direct_mappings(group, dev);
 
-       /* Make the direct mapped domain the default for this group */
+       /* Make the domain the default for this group */
        if (group->default_domain)
                iommu_domain_free(group->default_domain);
        group->default_domain = domain;
@@ -2196,6 +2251,28 @@ int iommu_request_dma_domain_for_dev(struct device *dev)
        return request_default_domain_for_dev(dev, IOMMU_DOMAIN_DMA);
 }
 
+void iommu_set_default_passthrough(bool cmd_line)
+{
+       if (cmd_line)
+               iommu_set_cmd_line_dma_api();
+
+       iommu_def_domain_type = IOMMU_DOMAIN_IDENTITY;
+}
+
+void iommu_set_default_translated(bool cmd_line)
+{
+       if (cmd_line)
+               iommu_set_cmd_line_dma_api();
+
+       iommu_def_domain_type = IOMMU_DOMAIN_DMA;
+}
+
+bool iommu_default_passthrough(void)
+{
+       return iommu_def_domain_type == IOMMU_DOMAIN_IDENTITY;
+}
+EXPORT_SYMBOL_GPL(iommu_default_passthrough);
+
 const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode)
 {
        const struct iommu_ops *ops = NULL;