[PATCH 7/9] iommu/arm-smmu: Add function that conditionally isolates all masters of all SMMUs

Andreas Herrmann andreas.herrmann at calxeda.com
Thu Sep 26 18:36:19 EDT 2013


(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 <andreas.herrmann at calxeda.com>
---
 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




More information about the linux-arm-kernel mailing list