(Depending on DT information and module parameters) each device is put
into its own protection domain (if possible).  For configuration with
one or just a few masters per SMMU that is easy to achieve.

In case of many devices per SMMU (e.g. MMU-500 with it's distributed
translation support) isolation of each device might not be possible --
depending on number of available SMR groups and/or context banks.

Default is that device isolation is contolled per SMMU with SMMU node
property "linux,arm-smmu-isolate-devices" in a DT. If the property is
set for an SMMU node device isolation is performed.

Also introduce a module parameter:

 arm-smmu=off|force_isolation|disable_isolation

  arm-smmu=     arm-smmu driver option

        off     Disable arm-smmu driver (ie. ignore available SMMUs)

        force_isolation
                Try to attach each master device on every SMMU to a
                separate iommu_domain.
                (Even for SMMUs that don't have the property
                "linux,arm-smmu-isolate-devices" set in device
                tree.)

        disable_isolation
                Disable device_isolation globally.
                (ie. no isolation even for SMMUs for which
                "linux,arm-smmu-isolate-devices" is set.)

W/o device isolation the driver detects SMMUs but no translation is
configured (transactions just bypass translation process).

Note that for device isolation dma_base and size are derived from
(coherent_)dma_mask of the master device.

Signed-off-by: Andreas Herrmann <[email protected]>
---
 drivers/iommu/arm-smmu.c |  101 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 101 insertions(+)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 5fa34f9..48f3bfb 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -46,6 +46,7 @@
 #include <linux/amba/bus.h>
 
 #include <asm/pgalloc.h>
+#include <asm/dma-iommu.h>
 
 /* Maximum number of stream IDs assigned to a single device */
 #define MAX_MASTER_STREAMIDS           8
@@ -354,6 +355,7 @@ struct arm_smmu_device {
 #define ARM_SMMU_FEAT_TRANS_S1         (1 << 2)
 #define ARM_SMMU_FEAT_TRANS_S2         (1 << 3)
 #define ARM_SMMU_FEAT_TRANS_NESTED     (1 << 4)
+#define ARM_SMMU_FEAT_ISOLATE_DEVICES  (1 << 5)
        u32                             features;
 #define ARM_SMMU_BUG_SECURE_CFG_ACCESS (1 << 0)
        u32                             bugs;
@@ -407,6 +409,10 @@ struct arm_smmu_domain {
 static DEFINE_SPINLOCK(arm_smmu_devices_lock);
 static LIST_HEAD(arm_smmu_devices);
 
+static bool arm_smmu_disabled;
+static bool arm_smmu_force_isolation;
+static bool arm_smmu_disable_isolation;
+
 static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu,
                                                struct device_node *dev_node)
 {
@@ -1810,6 +1816,74 @@ static int arm_smmu_device_cfg_probe(struct 
arm_smmu_device *smmu)
        return 0;
 }
 
+extern struct platform_device *of_find_device_by_node(struct device_node *np);
+
+static int arm_smmu_isolate_devices(void)
+{
+       struct dma_iommu_mapping *mapping;
+       struct arm_smmu_device *smmu;
+       struct rb_node *rbn;
+       struct arm_smmu_master *master;
+       struct platform_device *pdev;
+       struct device *dev;
+       void __iomem *gr0_base;
+       u32 cr0;
+       int ret = 0;
+       size_t size;
+
+       list_for_each_entry(smmu, &arm_smmu_devices, list) {
+               if (arm_smmu_disable_isolation ||
+                       (!(smmu->features & ARM_SMMU_FEAT_ISOLATE_DEVICES)
+                               && !arm_smmu_force_isolation))
+                       continue;
+               rbn = rb_first(&smmu->masters);
+               while (rbn) {
+                       master = container_of(rbn, struct arm_smmu_master, 
node);
+                       pdev = of_find_device_by_node(master->of_node);
+                       if (!pdev)
+                               break;
+                       dev = &pdev->dev;
+
+                       size = (size_t) dev->coherent_dma_mask;
+                       size = size ? : (unsigned long) dev->dma_mask;
+                       if (!size) {
+                               dev_warn(dev, "(coherent_)dma_mask not set\n");
+                               continue;
+                       }
+
+                       mapping = arm_iommu_create_mapping(&platform_bus_type,
+                                                       0, size, 0);
+                       if (IS_ERR(mapping)) {
+                               ret = PTR_ERR(mapping);
+                               dev_info(dev, "arm_iommu_create_mapping 
failed\n");
+                               goto out;
+                       }
+
+                       ret = arm_iommu_attach_device(dev, mapping);
+                       if (ret < 0) {
+                               dev_info(dev, "arm_iommu_attach_device 
failed\n");
+                               arm_iommu_release_mapping(mapping);
+                       }
+                       rbn = rb_next(rbn);
+               }
+
+               gr0_base = ARM_SMMU_GR0(smmu);
+               if (smmu->bugs & ARM_SMMU_BUG_SECURE_CFG_ACCESS) {
+                       cr0 = readl_relaxed(gr0_base + ARM_SMMU_GR0_nsCR0);
+                       cr0 |= sCR0_USFCFG;
+                       writel(cr0, gr0_base + ARM_SMMU_GR0_nsCR0);
+               } else {
+                       cr0 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sCR0);
+                       cr0 |= sCR0_USFCFG;
+                       writel(cr0, gr0_base + ARM_SMMU_GR0_sCR0);
+               }
+       }
+
+out:
+       return ret;
+}
+
+
 static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 {
        struct resource *res;
@@ -1820,6 +1894,9 @@ static int arm_smmu_device_dt_probe(struct 
platform_device *pdev)
        struct of_phandle_args masterspec;
        int num_irqs, i, err;
 
+       if (arm_smmu_disabled)
+               return -ENODEV;
+
        smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
        if (!smmu) {
                dev_err(dev, "failed to allocate arm_smmu_device\n");
@@ -1925,6 +2002,9 @@ static int arm_smmu_device_dt_probe(struct 
platform_device *pdev)
        if (of_property_read_bool(dev->of_node, 
"calxeda,smmu-secure-cfg-access"))
                smmu->bugs |= ARM_SMMU_BUG_SECURE_CFG_ACCESS;
 
+       if (of_property_read_bool(dev->of_node, 
"linux,arm-smmu-isolate-devices"))
+               smmu->features |= ARM_SMMU_FEAT_ISOLATE_DEVICES;
+
        INIT_LIST_HEAD(&smmu->list);
        spin_lock(&arm_smmu_devices_lock);
        list_add(&smmu->list, &arm_smmu_devices);
@@ -2015,6 +2095,25 @@ static struct platform_driver arm_smmu_driver = {
        .remove = arm_smmu_device_remove,
 };
 
+static int __init arm_smmu_parse_options(char *str)
+{
+       if (*str) {
+               str++;
+               if (!strncmp(str, "off", 3))
+                       arm_smmu_disabled = true;
+               else if(!strncmp(str, "force_isolation", 15))
+                       arm_smmu_force_isolation = true;
+               else if(!strncmp(str, "disable_isolation", 17))
+                       arm_smmu_disable_isolation = true;
+               else {
+                       pr_warn("arm_smmu: invalid parameter (\"%s\")\n", str);
+                       return 0;
+               }
+       }
+       return 1;
+}
+__setup("arm-smmu", arm_smmu_parse_options);
+
 static int __init arm_smmu_init(void)
 {
        int ret;
@@ -2030,6 +2129,8 @@ static int __init arm_smmu_init(void)
        if (!iommu_present(&amba_bustype))
                bus_set_iommu(&amba_bustype, &arm_smmu_ops);
 
+       arm_smmu_isolate_devices();
+
        return 0;
 }
 
-- 
1.7.9.5

_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to