Skip to contentSkip to conversation

Generic DT bindings for PCI IOMMUs and ARM SMMUv3

PATCH Superseded Robin Murphy 5 participants 30 messages Jun 28, 2016 – Jul 7, 2016 1 message not in the archive

Subsystems (11)

v3 of 7 Superseded Latest is v7 →

lore
Share & tools
lore

Robin Murphy

author maintainer
#0 lore raw

v3 cover Generic DT bindings for PCI IOMMUs and ARM SMMUv3

I didn't want the ongoing SMMUv2 rework delaying getting this out any longer, so here's the SMMUv3-flavoured parts of the respin to address the last round of comments[1] plus some new bits which fell out of the woodwork along the way. This time I've actually fully tested the platform device/multiple ID support as well (turns out I was being a bit thick and our model does have some suitable peripherals after all).

As ever, branch at git://linux-arm.org/linux-rm iommu/generic-v3

Robin.

[1]:http://thread.gmane.org/gmane.linux.kernel.iommu/13936

Lorenzo Pieralisi (1):
arm64: mm: change IOMMU notifier action to attach DMA ops

Mark Rutland (1):
Docs: dt: add PCI IOMMU map bindings

Robin Murphy (7):
iommu/of: Consolidate device creation workarounds of/irq: Break out msi-map lookup (again) iommu/of: Handle iommu-map property for PCI iommu/of: Introduce iommu_fwspec iommu/arm-smmu: Implement of_xlate() for SMMUv3 iommu/arm-smmu: Support non-PCI devices with SMMUv3 iommu/arm-smmu: Set PRIVCFG in stage 1 STEs

 .../devicetree/bindings/pci/pci-iommu.txt          | 171 ++++++++++++
 arch/arm64/mm/dma-mapping.c                        |  22 +-
 drivers/iommu/Kconfig                              |   2 +-
 drivers/iommu/arm-smmu-v3.c                        | 307 ++++++++++-----------
 drivers/iommu/exynos-iommu.c                       |  20 +-
 drivers/iommu/mtk_iommu.c                          |   8 +-
 drivers/iommu/of_iommu.c                           |  96 ++++++-
 drivers/of/irq.c                                   |  78 +-----
 drivers/of/of_pci.c                                | 102 +++++++
 include/linux/of_iommu.h                           |  18 ++
 include/linux/of_pci.h                             |  10 +
 11 files changed, 554 insertions(+), 280 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/pci/pci-iommu.txt
Signature · 1 line
2.8.1.dirty
author maintainer
#1 lore raw

PATCH v3 1/9 arm64: mm: change IOMMU notifier action to attach DMA ops

From: Lorenzo Pieralisi <[email protected]>

Current bus notifier in ARM64 (__iommu_attach_notifier) attempts to attach dma_ops to a device on BUS_NOTIFY_ADD_DEVICE action notification.

This will cause issues on ACPI based systems, where PCI devices can be added before the IOMMUs the devices are attached to had a chance to be probed, causing failures on attempts to attach dma_ops in that the domain for the respective IOMMU may not be set-up yet by the time the bus notifier is run.

Devices dma_ops do not require to be set-up till the matching device drivers are probed. This means that instead of running the notifier attaching dma_ops to devices (__iommu_attach_notifier) on BUS_NOTIFY_ADD_DEVICE action, it can be run just before the device driver is bound to the device in question (on action BUS_NOTIFY_BIND_DRIVER) so that it is certain that its IOMMU group and domain are set-up accordingly at the time the notifier is triggered.

This patch changes the notifier action upon which dma_ops are attached to devices and defer it to driver binding time, so that IOMMU devices have a chance to be probed and to register their bus notifiers before the dma_ops attach sequence for a device is actually carried out.

As a result we also no longer need worry about racing with iommu_bus_notifier(), or about retrying the queue in case devices were added too early on DT-based systems, so clean up the notifier itself plus the additional workaround from 722ec35f7fae ("arm64:
dma-mapping: fix handling of devices registered before arch_initcall")

Cc Will Deacon <[email protected]>
Cc Catalin Marinas <[email protected]>
Cc Marek Szyprowski <[email protected]>
[rm: get rid of other now-redundant bits]

---

v3: New, co-opted from http://article.gmane.org/gmane.linux.kernel.iommu/13991

 arch/arm64/mm/dma-mapping.c | 22 +++++-----------------
 1 file changed, 5 insertions(+), 17 deletions(-)
arch/arm64/mm/dma-mapping.c +5 -17
index c566ec83719f..e384b76203cc 100644
@@ -848,15 +848,16 @@ static int __iommu_attach_notifier(struct notifier_block *nb,
 {
 	struct iommu_dma_notifier_data *master, *tmp;
 
-	if (action != BUS_NOTIFY_ADD_DEVICE)
+	if (action != BUS_NOTIFY_BIND_DRIVER)
 		return 0;
 
 	mutex_lock(&iommu_dma_notifier_lock);
 	list_for_each_entry_safe(master, tmp, &iommu_dma_masters, list) {
-		if (do_iommu_attach(master->dev, master->ops,
-				master->dma_base, master->size)) {
+		if (data == master->dev && do_iommu_attach(master->dev,
+				master->ops, master->dma_base, master->size)) {
 			list_del(&master->list);
 			kfree(master);
+			break;
 		}
 	}
 	mutex_unlock(&iommu_dma_notifier_lock);
@@ -870,17 +871,8 @@ static int __init register_iommu_dma_ops_notifier(struct bus_type *bus)
 
 	if (!nb)
 		return -ENOMEM;
-	/*
-	 * The device must be attached to a domain before the driver probe
-	 * routine gets a chance to start allocating DMA buffers. However,
-	 * the IOMMU driver also needs a chance to configure the iommu_group
-	 * via its add_device callback first, so we need to make the attach
-	 * happen between those two points. Since the IOMMU core uses a bus
-	 * notifier with default priority for add_device, do the same but
-	 * with a lower priority to ensure the appropriate ordering.
-	 */
+
 	nb->notifier_call = __iommu_attach_notifier;
-	nb->priority = -100;
 
 	ret = bus_register_notifier(bus, nb);
 	if (ret) {
@@ -904,10 +896,6 @@ static int __init __iommu_dma_init(void)
 	if (!ret)
 		ret = register_iommu_dma_ops_notifier(&pci_bus_type);
 #endif
-
-	/* handle devices queued before this arch_initcall */
-	if (!ret)
-		__iommu_attach_notifier(NULL, BUS_NOTIFY_ADD_DEVICE, NULL);
 	return ret;
 }
 arch_initcall(__iommu_dma_init);
Signature · 1 line
2.8.1.dirty
author maintainer
#2 lore raw

PATCH v3 2/9 iommu/of: Consolidate device creation workarounds

In reply to Robin Murphy

So far, all the users of the generic of_xlate configuration mechanism are resorting to explicit platform device creation to ensure the IOMMU device is ready before anything tries to refer to it. As I'm about to convert two more drivers that will need exactly the same thing, let's nip that proliferation in the bud and move it to a single place.

A neat solution is to make of_get_iommu_ops() ensure an IOMMU device is instantiated before giving out its associated ops, since it's a fairly safe assumption that the ops aren't going to be much use if the IOMMU can't or won't exist to back them up.

CC Marek Szyprowski <[email protected]>
CC Yong Wu <[email protected]>

---

v3: New.

 drivers/iommu/exynos-iommu.c | 20 +++++++-------------
 drivers/iommu/mtk_iommu.c    |  8 +-------
 drivers/iommu/of_iommu.c     |  6 +++++-
 3 files changed, 13 insertions(+), 21 deletions(-)
drivers/iommu/exynos-iommu.c +7 -13
index 5ecc86cb74c8..97380ee56d71 100644
@@ -665,6 +665,13 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev)
 
 	pm_runtime_enable(dev);
 
+	/*
+	 * use the first registered sysmmu device for performing
+	 * dma mapping operations on iommu page tables (cpu cache flush)
+	 */
+	if (!dma_dev)
+		dma_dev = dev;
+
 	return 0;
 }
 
@@ -1341,22 +1348,9 @@ err_reg_driver:
 
 static int __init exynos_iommu_of_setup(struct device_node *np)
 {
-	struct platform_device *pdev;
-
 	if (!init_done)
 		exynos_iommu_init();
 
-	pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root);
-	if (IS_ERR(pdev))
-		return PTR_ERR(pdev);
-
-	/*
-	 * use the first registered sysmmu device for performing
-	 * dma mapping operations on iommu page tables (cpu cache flush)
-	 */
-	if (!dma_dev)
-		dma_dev = &pdev->dev;
-
 	of_iommu_set_ops(np, &exynos_iommu_ops);
 	return 0;
 }
drivers/iommu/mtk_iommu.c +1 -7
index c3043d8754e3..f7ae87abea99 100644
@@ -724,14 +724,8 @@ static struct platform_driver mtk_iommu_driver = {
 
 static int mtk_iommu_init_fn(struct device_node *np)
 {
-	int ret;
-	struct platform_device *pdev;
+	int ret = platform_driver_register(&mtk_iommu_driver);
 
-	pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root);
-	if (!pdev)
-		return -ENOMEM;
-
-	ret = platform_driver_register(&mtk_iommu_driver);
 	if (ret) {
 		pr_err("%s: Failed to register driver\n", __func__);
 		return ret;
drivers/iommu/of_iommu.c +5 -1
index af499aea0a1a..7e6369cffc95 100644
@@ -22,6 +22,7 @@
 #include <linux/limits.h>
 #include <linux/of.h>
 #include <linux/of_iommu.h>
+#include <linux/of_platform.h>
 #include <linux/slab.h>
 
 static const struct of_device_id __iommu_of_table_sentinel
@@ -127,7 +128,10 @@ const struct iommu_ops *of_iommu_get_ops(struct device_node *np)
 	spin_lock(&of_iommu_lock);
 	list_for_each_entry(node, &of_iommu_list, list)
 		if (node->np == np) {
-			ops = node->ops;
+			if (of_node_check_flag(np, OF_POPULATED) ||
+			    of_platform_device_create(np, NULL,
+					    platform_bus_type.dev_root))
+				ops = node->ops;
 			break;
 		}
 	spin_unlock(&of_iommu_lock);
Signature · 1 line
2.8.1.dirty
#3 lore raw

PATCH v3 2/9 iommu/of: Consolidate device creation workarounds

Quoted 11 lines · Robin Murphy

So far, all the users of the generic of_xlate configuration mechanism are resorting to explicit platform device creation to ensure the IOMMU device is ready before anything tries to refer to it. As I'm about to convert two more drivers that will need exactly the same thing, let's nip that proliferation in the bud and move it to a single place.

A neat solution is to make of_get_iommu_ops() ensure an IOMMU device is instantiated before giving out its associated ops, since it's a fairly safe assumption that the ops aren't going to be much use if the IOMMU can't or won't exist to back them up.

Quoted 4 lines
CC Marek Szyprowski <[email protected]>
CC Yong Wu <[email protected]>

---

Marek, Yong, can I have your acks on this please? It's a fairly straightforward change, but the subsequent arm-smmu patches rely on it.

Thanks,

Will

Quoted 93 lines
 drivers/iommu/exynos-iommu.c | 20 +++++++-------------
 drivers/iommu/mtk_iommu.c    |  8 +-------
 drivers/iommu/of_iommu.c     |  6 +++++-
 3 files changed, 13 insertions(+), 21 deletions(-)
drivers/iommu/exynos-iommu.c +7 -13
index 5ecc86cb74c8..97380ee56d71 100644
@@ -665,6 +665,13 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev)
 
 	pm_runtime_enable(dev);
 
+	/*
+	 * use the first registered sysmmu device for performing
+	 * dma mapping operations on iommu page tables (cpu cache flush)
+	 */
+	if (!dma_dev)
+		dma_dev = dev;
+
 	return 0;
 }
 
@@ -1341,22 +1348,9 @@ err_reg_driver:
 
 static int __init exynos_iommu_of_setup(struct device_node *np)
 {
-	struct platform_device *pdev;
-
 	if (!init_done)
 		exynos_iommu_init();
 
-	pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root);
-	if (IS_ERR(pdev))
-		return PTR_ERR(pdev);
-
-	/*
-	 * use the first registered sysmmu device for performing
-	 * dma mapping operations on iommu page tables (cpu cache flush)
-	 */
-	if (!dma_dev)
-		dma_dev = &pdev->dev;
-
 	of_iommu_set_ops(np, &exynos_iommu_ops);
 	return 0;
 }
drivers/iommu/mtk_iommu.c +1 -7
index c3043d8754e3..f7ae87abea99 100644
@@ -724,14 +724,8 @@ static struct platform_driver mtk_iommu_driver = {
 
 static int mtk_iommu_init_fn(struct device_node *np)
 {
-	int ret;
-	struct platform_device *pdev;
+	int ret = platform_driver_register(&mtk_iommu_driver);
 
-	pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root);
-	if (!pdev)
-		return -ENOMEM;
-
-	ret = platform_driver_register(&mtk_iommu_driver);
 	if (ret) {
 		pr_err("%s: Failed to register driver\n", __func__);
 		return ret;
drivers/iommu/of_iommu.c +5 -1
index af499aea0a1a..7e6369cffc95 100644
@@ -22,6 +22,7 @@
 #include <linux/limits.h>
 #include <linux/of.h>
 #include <linux/of_iommu.h>
+#include <linux/of_platform.h>
 #include <linux/slab.h>
 
 static const struct of_device_id __iommu_of_table_sentinel
@@ -127,7 +128,10 @@ const struct iommu_ops *of_iommu_get_ops(struct device_node *np)
 	spin_lock(&of_iommu_lock);
 	list_for_each_entry(node, &of_iommu_list, list)
 		if (node->np == np) {
-			ops = node->ops;
+			if (of_node_check_flag(np, OF_POPULATED) ||
+			    of_platform_device_create(np, NULL,
+					    platform_bus_type.dev_root))
+				ops = node->ops;
 			break;
 		}
 	spin_unlock(&of_iommu_lock);
Signature · 1 line
2.8.1.dirty
maintainer
#4 lore raw

PATCH v3 2/9 iommu/of: Consolidate device creation workarounds

In reply to Robin Murphy

Hi Robin,

Quoted 6 lines · Robin Murphy

So far, all the users of the generic of_xlate configuration mechanism are resorting to explicit platform device creation to ensure the IOMMU device is ready before anything tries to refer to it. As I'm about to convert two more drivers that will need exactly the same thing, let's nip that proliferation in the bud and move it to a single place.

Quoted 8 lines

A neat solution is to make of_get_iommu_ops() ensure an IOMMU device is instantiated before giving out its associated ops, since it's a fairly safe assumption that the ops aren't going to be much use if the IOMMU can't or won't exist to back them up.

CC Marek Szyprowski <[email protected]>
CC Yong Wu <[email protected]>

Frankly, I would avoid moving this workaround to the iommu core. IMHO the best solution would be to let IOMMU controllers to be instantiated as normal devices and implement proper support in the device core for waiting for the iommu controller. Then the workaround can be removed from exynos and mtk iommu drivers. What's the status of IOMMU deferred probe patches?

I've encountered a serious problems with current code (the one which instantiates iommu controller devices from iommu driver) and its integration with power domains, clocks and runtime pm, which were not possible to resolve
without iommu deferred probe.

Quoted 84 lines

---

v3: New.

  drivers/iommu/exynos-iommu.c | 20 +++++++-------------
  drivers/iommu/mtk_iommu.c    |  8 +-------
  drivers/iommu/of_iommu.c     |  6 +++++-
  3 files changed, 13 insertions(+), 21 deletions(-)
drivers/iommu/exynos-iommu.c +7 -13
index 5ecc86cb74c8..97380ee56d71 100644
@@ -665,6 +665,13 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev)
 
  	pm_runtime_enable(dev);
 
+	/*
+	 * use the first registered sysmmu device for performing
+	 * dma mapping operations on iommu page tables (cpu cache flush)
+	 */
+	if (!dma_dev)
+		dma_dev = dev;
+
  	return 0;
  }
 
@@ -1341,22 +1348,9 @@ err_reg_driver:
 
  static int __init exynos_iommu_of_setup(struct device_node *np)
  {
-	struct platform_device *pdev;
-
  	if (!init_done)
  		exynos_iommu_init();
 
-	pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root);
-	if (IS_ERR(pdev))
-		return PTR_ERR(pdev);
-
-	/*
-	 * use the first registered sysmmu device for performing
-	 * dma mapping operations on iommu page tables (cpu cache flush)
-	 */
-	if (!dma_dev)
-		dma_dev = &pdev->dev;
-
  	of_iommu_set_ops(np, &exynos_iommu_ops);
  	return 0;
  }
drivers/iommu/mtk_iommu.c +1 -7
index c3043d8754e3..f7ae87abea99 100644
@@ -724,14 +724,8 @@ static struct platform_driver mtk_iommu_driver = {
 
  static int mtk_iommu_init_fn(struct device_node *np)
  {
-	int ret;
-	struct platform_device *pdev;
+	int ret = platform_driver_register(&mtk_iommu_driver);
 
-	pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root);
-	if (!pdev)
-		return -ENOMEM;
-
-	ret = platform_driver_register(&mtk_iommu_driver);
  	if (ret) {
  		pr_err("%s: Failed to register driver\n", __func__);
  		return ret;
drivers/iommu/of_iommu.c +1 -0
index af499aea0a1a..7e6369cffc95 100644
@@ -22,6 +22,7 @@
  #include <linux/limits.h>
  #include <linux/of.h>
  #include <linux/of_iommu.h>
+#include <linux/of_platform.h>
  #include <linux/slab.h>
 
  static const struct of_device_id __iommu_of_table_sentinel
@@ -127,7 +128,10 @@ const struct iommu_ops *of_iommu_get_ops(struct device_node *np)
  	spin_lock(&of_iommu_lock);

drivers/iommu/of_iommu.c const struct iommu_ops *of_iommu_get_ops(struct device_node *np) · line 137

Quoted 10 lines
drivers/iommu/of_iommu.c +4 -1
  	list_for_each_entry(node, &of_iommu_list, list)
  		if (node->np == np) {
-			ops = node->ops;
+			if (of_node_check_flag(np, OF_POPULATED) ||
+			    of_platform_device_create(np, NULL,
+					    platform_bus_type.dev_root))
+				ops = node->ops;
  			break;
  		}
  	spin_unlock(&of_iommu_lock);

Best regards

Signature · 2 lines
Marek Szyprowski, PhD
Samsung R&D Institute Poland
author maintainer
#5 lore raw

PATCH v3 2/9 iommu/of: Consolidate device creation workarounds

Hi Marek,

Quoted 19 lines · Marek Szyprowski

Hi Robin,

Quoted 6 lines · Robin Murphy

So far, all the users of the generic of_xlate configuration mechanism are resorting to explicit platform device creation to ensure the IOMMU device is ready before anything tries to refer to it. As I'm about to convert two more drivers that will need exactly the same thing, let's nip that proliferation in the bud and move it to a single place.

Quoted 8 lines

A neat solution is to make of_get_iommu_ops() ensure an IOMMU device is instantiated before giving out its associated ops, since it's a fairly safe assumption that the ops aren't going to be much use if the IOMMU can't or won't exist to back them up.

CC Marek Szyprowski <[email protected]>
CC Yong Wu <[email protected]>
Quoted 6 lines

Frankly, I would avoid moving this workaround to the iommu core. IMHO the best solution would be to let IOMMU controllers to be instantiated as normal
devices and implement proper support in the device core for waiting for the iommu controller. Then the workaround can be removed from exynos and mtk iommu drivers. What's the status of IOMMU deferred probe patches?

I think revisiting probe ordering is now second-from-top on my to-do list after this lot. This patch was kind of thinking ahead to get the "touch all the drivers" aspect out of the way before it grows any bigger, and all the development can then happen in the core code alone, but I admit it's not a particularly strong argument.

Quoted 6 lines

I've encountered a serious problems with current code (the one which instantiates iommu controller devices from iommu driver) and its integration
with power domains, clocks and runtime pm, which were not possible to resolve
without iommu deferred probe.

OK. Do you have any plans to try tweaking the current workaround, or is it really not worth it? FWIW I do have an Exynos 5410 (Odroid-XU) on my desk which I could theoretically test things on, but I suspect it would take a fair amount of work to get the SYSMMUs and relevant media bits up and running on top of Krzysztof's basic support.

Will: for the time being, the alternative to this patch would be to squash the following change into patch 7/9 (without either, patch 8/9 doesn't really work).

Robin.

-----8<-----

drivers/iommu/arm-smmu-v3.c +8 -0
index e4c3916efba9..c002ff06b625 100644
@@ -2678,6 +2678,14 @@ module_exit(arm_smmu_exit);
 
 static int __init arm_smmu_of_init(struct device_node *np)
 {
+	static bool registered;
+
+	if (!registered)
+		registered = !arm_smmu_init();
+
+	if (!of_platform_device_create(np, NULL, platform_bus_type.dev_root))
+		return -ENODEV;
+
 	of_iommu_set_ops(np, &arm_smmu_ops);
 
 	return 0;
maintainer
#6 lore raw

PATCH v3 2/9 iommu/of: Consolidate device creation workarounds

Hi Robin,

Quoted 33 lines · Robin Murphy

Hi Marek,

Quoted 18 lines · Marek Szyprowski

Hi Robin,

Quoted 6 lines · Robin Murphy

So far, all the users of the generic of_xlate configuration mechanism are resorting to explicit platform device creation to ensure the IOMMU device is ready before anything tries to refer to it. As I'm about to convert two more drivers that will need exactly the same thing, let's nip that proliferation in the bud and move it to a single place.

Quoted 8 lines

A neat solution is to make of_get_iommu_ops() ensure an IOMMU device is instantiated before giving out its associated ops, since it's a fairly safe assumption that the ops aren't going to be much use if the IOMMU can't or won't exist to back them up.

CC Marek Szyprowski <[email protected]>
CC Yong Wu <[email protected]>
Quoted 6 lines

Frankly, I would avoid moving this workaround to the iommu core. IMHO the best solution would be to let IOMMU controllers to be instantiated as normal
devices and implement proper support in the device core for waiting for the iommu controller. Then the workaround can be removed from exynos and mtk iommu drivers. What's the status of IOMMU deferred probe patches?

I think revisiting probe ordering is now second-from-top on my to-do list after this lot. This patch was kind of thinking ahead to get the "touch all the drivers" aspect out of the way before it grows any bigger, and all the development can then happen in the core code alone, but I admit it's not a particularly strong argument.

Quoted 8 lines
Quoted 6 lines

I've encountered a serious problems with current code (the one which instantiates iommu controller devices from iommu driver) and its integration
with power domains, clocks and runtime pm, which were not possible to resolve
without iommu deferred probe.

OK. Do you have any plans to try tweaking the current workaround, or is it really not worth it?

I will keep it as is until the core will be ready for initializing Exynos iommu driver simply from the core_initcall (or other), which will just do platform_driver_register(&exynos_sysmmu_driver).

Quoted 4 lines

FWIW I do have an Exynos 5410 (Odroid-XU) on my
desk which I could theoretically test things on, but I suspect it would take a fair amount of work to get the SYSMMUs and relevant media bits up and running on top of Krzysztof's basic support.

It should be quite easy to get Exynos DRM with HDMI display working on Odroid XU1, I can take a look in that after getting back from holidays (probably after 17th July).

Quoted 18 lines

Will: for the time being, the alternative to this patch would be to squash the following change into patch 7/9 (without either, patch 8/9 doesn't really work).

Robin.

-----8<-----

drivers/iommu/arm-smmu-v3.c +1 -0
index e4c3916efba9..c002ff06b625 100644
@@ -2678,6 +2678,14 @@ module_exit(arm_smmu_exit);
 
  static int __init arm_smmu_of_init(struct device_node *np)
  {
+	static bool registered;

drivers/iommu/arm-smmu-v3.c module_exit(arm_smmu_exit); · line 2691

Quoted 10 lines
drivers/iommu/arm-smmu-v3.c +7 -0
+
+	if (!registered)
+		registered = !arm_smmu_init();
+
+	if (!of_platform_device_create(np, NULL, platform_bus_type.dev_root))
+		return -ENODEV;
+
  	of_iommu_set_ops(np, &arm_smmu_ops);
 
  	return 0;

Best regards

Signature · 2 lines
Marek Szyprowski, PhD
Samsung R&D Institute Poland
#7 lore raw

PATCH v3 2/9 iommu/of: Consolidate device creation workarounds

In reply to Robin Murphy
Quoted 27 lines · Robin Murphy
Quoted 6 lines · Marek Szyprowski

Frankly, I would avoid moving this workaround to the iommu core. IMHO the best solution would be to let IOMMU controllers to be instantiated as normal
devices and implement proper support in the device core for waiting for the iommu controller. Then the workaround can be removed from exynos and mtk iommu drivers. What's the status of IOMMU deferred probe patches?

I think revisiting probe ordering is now second-from-top on my to-do list after this lot. This patch was kind of thinking ahead to get the "touch all the drivers" aspect out of the way before it grows any bigger, and all the development can then happen in the core code alone, but I admit it's not a particularly strong argument.

Quoted 6 lines

I've encountered a serious problems with current code (the one which instantiates iommu controller devices from iommu driver) and its integration
with power domains, clocks and runtime pm, which were not possible to resolve
without iommu deferred probe.

OK. Do you have any plans to try tweaking the current workaround, or is it really not worth it? FWIW I do have an Exynos 5410 (Odroid-XU) on my desk which I could theoretically test things on, but I suspect it would take a fair amount of work to get the SYSMMUs and relevant media bits up and running on top of Krzysztof's basic support.

Quoted 3 lines

Will: for the time being, the alternative to this patch would be to squash the following change into patch 7/9 (without either, patch 8/9 doesn't really work).

It's ugly, but I'm fine with it for the moment. No need to wait for the probe deferral stuff before getting this in.

Will

author maintainer
#8 lore raw

PATCH v3 3/9 Docs: dt: add PCI IOMMU map bindings

In reply to Robin Murphy

From: Mark Rutland <[email protected]>

The existing IOMMU bindings are able to specify the relationship between masters and IOMMUs, but they are insufficient for describing the general case of hotpluggable busses such as PCI where the set of masters is not known until runtime, and the relationship between masters and IOMMUs is a property of the integration of the system.

This patch adds a generic binding for mapping PCI devices to IOMMUs, using a new iommu-map property (specific to PCI*) which may be used to map devices (identified by their Requester ID) to sideband data for the IOMMU which they master through.

---

v3: +Will's ack.

 .../devicetree/bindings/pci/pci-iommu.txt          | 171 +++++++++++++++++++++
 1 file changed, 171 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/pci-iommu.txt
Documentation/devicetree/bindings/pci/pci-iommu.txt new file +171 -0
new file mode 100644
index 000000000000..56c829621b9a
@@ -0,0 +1,171 @@
+This document describes the generic device tree binding for describing the
+relationship between PCI(e) devices and IOMMU(s).
+
+Each PCI(e) device under a root complex is uniquely identified by its Requester
+ID (AKA RID). A Requester ID is a triplet of a Bus number, Device number, and
+Function number.
+
+For the purpose of this document, when treated as a numeric value, a RID is
+formatted such that:
+
+* Bits [15:8] are the Bus number.
+* Bits [7:3] are the Device number.
+* Bits [2:0] are the Function number.
+* Any other bits required for padding must be zero.
+
+IOMMUs may distinguish PCI devices through sideband data derived from the
+Requester ID. While a given PCI device can only master through one IOMMU, a
+root complex may split masters across a set of IOMMUs (e.g. with one IOMMU per
+bus).
+
+The generic 'iommus' property is insufficient to describe this relationship,
+and a mechanism is required to map from a PCI device to its IOMMU and sideband
+data.
+
+For generic IOMMU bindings, see
+Documentation/devicetree/bindings/iommu/iommu.txt.
+
+
+PCI root complex
+================
+
+Optional properties
+-------------------
+
+- iommu-map: Maps a Requester ID to an IOMMU and associated iommu-specifier
+  data.
+
+  The property is an arbitrary number of tuples of
+  (rid-base,iommu,iommu-base,length).
+
+  Any RID r in the interval [rid-base, rid-base + length) is associated with
+  the listed IOMMU, with the iommu-specifier (r - rid-base + iommu-base).
+
+- iommu-map-mask: A mask to be applied to each Requester ID prior to being
+  mapped to an iommu-specifier per the iommu-map property.
+
+
+Example (1)
+===========
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	iommu: iommu at a {
+		reg = <0xa 0x1>;
+		compatible = "vendor,some-iommu";
+		#iommu-cells = <1>;
+	};
+
+	pci: pci at f {
+		reg = <0xf 0x1>;
+		compatible = "vendor,pcie-root-complex";
+		device_type = "pci";
+
+		/*
+		 * The sideband data provided to the IOMMU is the RID,
+		 * identity-mapped.
+		 */
+		iommu-map = <0x0 &iommu 0x0 0x10000>;
+	};
+};
+
+
+Example (2)
+===========
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	iommu: iommu at a {
+		reg = <0xa 0x1>;
+		compatible = "vendor,some-iommu";
+		#iommu-cells = <1>;
+	};
+
+	pci: pci at f {
+		reg = <0xf 0x1>;
+		compatible = "vendor,pcie-root-complex";
+		device_type = "pci";
+
+		/*
+		 * The sideband data provided to the IOMMU is the RID with the
+		 * function bits masked out.
+		 */
+		iommu-map = <0x0 &iommu 0x0 0x10000>;
+		iommu-map-mask = <0xfff8>;
+	};
+};
+
+
+Example (3)
+===========
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	iommu: iommu at a {
+		reg = <0xa 0x1>;
+		compatible = "vendor,some-iommu";
+		#iommu-cells = <1>;
+	};
+
+	pci: pci at f {
+		reg = <0xf 0x1>;
+		compatible = "vendor,pcie-root-complex";
+		device_type = "pci";
+
+		/*
+		 * The sideband data provided to the IOMMU is the RID,
+		 * but the high bits of the bus number are flipped.
+		 */
+		iommu-map = <0x0000 &iommu 0x8000 0x8000>,
+			    <0x8000 &iommu 0x0000 0x8000>;
+	};
+};
+
+
+Example (4)
+===========
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	iommu_a: iommu at a {
+		reg = <0xa 0x1>;
+		compatible = "vendor,some-iommu";
+		#iommu-cells = <1>;
+	};
+
+	iommu_b: iommu at b {
+		reg = <0xb 0x1>;
+		compatible = "vendor,some-iommu";
+		#iommu-cells = <1>;
+	};
+
+	iommu_c: iommu at c {
+		reg = <0xc 0x1>;
+		compatible = "vendor,some-iommu";
+		#iommu-cells = <1>;
+	};
+
+	pci: pci at f {
+		reg = <0xf 0x1>;
+		compatible = "vendor,pcie-root-complex";
+		device_type = "pci";
+
+		/*
+		 * Devices with bus number 0-127 are mastered via IOMMU
+		 * a, with sideband data being RID[14:0].
+		 * Devices with bus number 128-255 are mastered via
+		 * IOMMU b, with sideband data being RID[14:0].
+		 * No devices master via IOMMU c.
+		 */
+		iommu-map = <0x0000 &iommu_a 0x0000 0x8000>,
+			    <0x8000 &iommu_b 0x0000 0x8000>;
+	};
+};
Signature · 1 line
2.8.1.dirty
author maintainer
#9 lore raw

PATCH v3 4/9 of/irq: Break out msi-map lookup (again)

In reply to Robin Murphy

The PCI msi-map code is already doing double-duty translating IDs and retrieving MSI parents, which unsurprisingly is the same functionality we need for the identically-formatted PCI iommu-map property. Drag the core parsing routine up yet another layer into the general OF-PCI code, and further generalise it for either kind of lookup in either flavour of map property.

---

v3: +Marc's ack, pass mask name explicitly [Will], further simplify

    __of_msi_map_rid() [Rob].
 drivers/of/irq.c       |  78 ++-----------------------------------
 drivers/of/of_pci.c    | 102 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/of_pci.h |  10 +++++
 3 files changed, 116 insertions(+), 74 deletions(-)
drivers/of/irq.c +4 -74
index 6ec743faabe8..091d45e6d61d 100644
@@ -24,6 +24,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
+#include <linux/of_pci.h>
 #include <linux/string.h>
 #include <linux/slab.h>
 
@@ -587,87 +588,16 @@ static u32 __of_msi_map_rid(struct device *dev, struct device_node **np,
 			    u32 rid_in)
 {
 	struct device *parent_dev;
-	struct device_node *msi_controller_node;
-	struct device_node *msi_np = *np;
-	u32 map_mask, masked_rid, rid_base, msi_base, rid_len, phandle;
-	int msi_map_len;
-	bool matched;
 	u32 rid_out = rid_in;
-	const __be32 *msi_map = NULL;
 
 	/*
 	 * Walk up the device parent links looking for one with a
 	 * "msi-map" property.
 	 */
-	for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) {
-		if (!parent_dev->of_node)
-			continue;
-
-		msi_map = of_get_property(parent_dev->of_node,
-					  "msi-map", &msi_map_len);
-		if (!msi_map)
-			continue;
-
-		if (msi_map_len % (4 * sizeof(__be32))) {
-			dev_err(parent_dev, "Error: Bad msi-map length: %d\n",
-				msi_map_len);
-			return rid_out;
-		}
-		/* We have a good parent_dev and msi_map, let's use them. */
-		break;
-	}
-	if (!msi_map)
-		return rid_out;
-
-	/* The default is to select all bits. */
-	map_mask = 0xffffffff;
-
-	/*
-	 * Can be overridden by "msi-map-mask" property.  If
-	 * of_property_read_u32() fails, the default is used.
-	 */
-	of_property_read_u32(parent_dev->of_node, "msi-map-mask", &map_mask);
-
-	masked_rid = map_mask & rid_in;
-	matched = false;
-	while (!matched && msi_map_len >= 4 * sizeof(__be32)) {
-		rid_base = be32_to_cpup(msi_map + 0);
-		phandle = be32_to_cpup(msi_map + 1);
-		msi_base = be32_to_cpup(msi_map + 2);
-		rid_len = be32_to_cpup(msi_map + 3);
-
-		if (rid_base & ~map_mask) {
-			dev_err(parent_dev,
-				"Invalid msi-map translation - msi-map-mask (0x%x) ignores rid-base (0x%x)\n",
-				map_mask, rid_base);
-			return rid_out;
-		}
-
-		msi_controller_node = of_find_node_by_phandle(phandle);
-
-		matched = (masked_rid >= rid_base &&
-			   masked_rid < rid_base + rid_len);
-		if (msi_np)
-			matched &= msi_np == msi_controller_node;
-
-		if (matched && !msi_np) {
-			*np = msi_np = msi_controller_node;
+	for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent)
+		if (!of_pci_map_rid(parent_dev->of_node, rid_in, "msi-map",
+				    "msi-map-mask", np, &rid_out))
 			break;
-		}
-
-		of_node_put(msi_controller_node);
-		msi_map_len -= 4 * sizeof(__be32);
-		msi_map += 4;
-	}
-	if (!matched)
-		return rid_out;
-
-	rid_out = masked_rid - rid_base + msi_base;
-	dev_dbg(dev,
-		"msi-map at: %s, using mask %08x, rid-base: %08x, msi-base: %08x, length: %08x, rid: %08x -> %08x\n",
-		dev_name(parent_dev), map_mask, rid_base, msi_base,
-		rid_len, rid_in, rid_out);
-
 	return rid_out;
 }
 
drivers/of/of_pci.c +102 -0

105 lines not on this page · raw message

include/linux/of_pci.h +10 -0
index b969e9443962..7fd5cfce9140 100644
@@ -17,6 +17,9 @@ int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin);
 int of_pci_parse_bus_range(struct device_node *node, struct resource *res);
 int of_get_pci_domain_nr(struct device_node *node);
 void of_pci_check_probe_only(void);
+int of_pci_map_rid(struct device_node *np, u32 rid,
+		   const char *map_name, const char *map_mask_name,
+		   struct device_node **target, u32 *id_out);
 #else
 static inline int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq)
 {
@@ -52,6 +55,13 @@ of_get_pci_domain_nr(struct device_node *node)
 	return -1;
 }
 
+static inline int of_pci_map_rid(struct device_node *np, u32 rid,
+			const char *map_name, const char *map_mask_name,
+			struct device_node **target, u32 *id_out)
+{
+	return -EINVAL;
+}
+
 static inline void of_pci_check_probe_only(void) { }
 #endif
 
Signature · 1 line
2.8.1.dirty
author maintainer
#10 lore raw

PATCH v3 5/9 iommu/of: Handle iommu-map property for PCI

In reply to Robin Murphy

Now that we have a way to pick up the RID translation and target IOMMU, hook up of_iommu_configure() to bring PCI devices into the of_xlate mechanism and allow them IOMMU-backed DMA ops without the need for driver-specific handling.

---

v3: Use explicit "iommu-map-mask", drop of_device_is_available() check.

 drivers/iommu/of_iommu.c | 43 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 36 insertions(+), 7 deletions(-)
drivers/iommu/of_iommu.c +36 -7
index 7e6369cffc95..25406a8b9d4e 100644
@@ -22,6 +22,7 @@
 #include <linux/limits.h>
 #include <linux/of.h>
 #include <linux/of_iommu.h>
+#include <linux/of_pci.h>
 #include <linux/of_platform.h>
 #include <linux/slab.h>
 
@@ -138,20 +139,48 @@ const struct iommu_ops *of_iommu_get_ops(struct device_node *np)
 	return ops;
 }
 
+static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
+{
+	struct of_phandle_args *iommu_spec = data;
+
+	iommu_spec->args[0] = alias;
+	return iommu_spec->np == pdev->bus->dev.of_node;
+}
+
 const struct iommu_ops *of_iommu_configure(struct device *dev,
 					   struct device_node *master_np)
 {
 	struct of_phandle_args iommu_spec;
-	struct device_node *np;
+	struct device_node *np = NULL;
 	const struct iommu_ops *ops = NULL;
 	int idx = 0;
 
-	/*
-	 * We can't do much for PCI devices without knowing how
-	 * device IDs are wired up from the PCI bus to the IOMMU.
-	 */
-	if (dev_is_pci(dev))
-		return NULL;
+	if (dev_is_pci(dev)) {
+		/*
+		 * Start by tracing the RID alias down the PCI topology as
+		 * far as the host bridge whose OF node we have...
+		 */
+		iommu_spec.np = master_np;
+		pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
+				       &iommu_spec);
+		/*
+		 * ...then find out what that becomes once it escapes the PCI
+		 * bus into the system beyond, and which IOMMU it ends up at.
+		 */
+		if (of_pci_map_rid(master_np, iommu_spec.args[0], "iommu-map",
+				    "iommu-map-mask", &np, iommu_spec.args))
+			return NULL;
+
+		/* We're not attempting to handle multi-alias devices yet */
+		iommu_spec.np = np;
+		iommu_spec.args_count = 1;
+		ops = of_iommu_get_ops(np);
+		if (!ops || !ops->of_xlate || ops->of_xlate(dev, &iommu_spec))
+			ops = NULL;
+
+		of_node_put(np);
+		return ops;
+	}
 
 	/*
 	 * We don't currently walk up the tree looking for a parent IOMMU.
Signature · 1 line
2.8.1.dirty
#11 lore raw

PATCH v3 5/9 iommu/of: Handle iommu-map property for PCI

Quoted 33 lines · Robin Murphy

Now that we have a way to pick up the RID translation and target IOMMU, hook up of_iommu_configure() to bring PCI devices into the of_xlate mechanism and allow them IOMMU-backed DMA ops without the need for driver-specific handling.

---

v3: Use explicit "iommu-map-mask", drop of_device_is_available() check.

 drivers/iommu/of_iommu.c | 43 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 36 insertions(+), 7 deletions(-)
drivers/iommu/of_iommu.c +5 -0
index 7e6369cffc95..25406a8b9d4e 100644
@@ -22,6 +22,7 @@
 #include <linux/limits.h>
 #include <linux/of.h>
 #include <linux/of_iommu.h>
+#include <linux/of_pci.h>
 #include <linux/of_platform.h>
 #include <linux/slab.h>
 
@@ -138,20 +139,48 @@ const struct iommu_ops *of_iommu_get_ops(struct device_node *np)
 	return ops;
 }
 
+static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
+{
+	struct of_phandle_args *iommu_spec = data;
+

drivers/iommu/of_iommu.c const struct iommu_ops *of_iommu_get_ops(struct device_node *np) · line 154

Quoted 10 lines
drivers/iommu/of_iommu.c +5 -1
+	iommu_spec->args[0] = alias;
+	return iommu_spec->np == pdev->bus->dev.of_node;
+}
+
 const struct iommu_ops *of_iommu_configure(struct device *dev,
 					   struct device_node *master_np)
 {
 	struct of_phandle_args iommu_spec;
-	struct device_node *np;
+	struct device_node *np = NULL;

This sets off some alarm bells...

Quoted 14 lines
drivers/iommu/of_iommu.c +5 -6
 	const struct iommu_ops *ops = NULL;
 	int idx = 0;
 
-	/*
-	 * We can't do much for PCI devices without knowing how
-	 * device IDs are wired up from the PCI bus to the IOMMU.
-	 */
-	if (dev_is_pci(dev))
-		return NULL;
+	if (dev_is_pci(dev)) {
+		/*
+		 * Start by tracing the RID alias down the PCI topology as
+		 * far as the host bridge whose OF node we have...
+		 */

drivers/iommu/of_iommu.c const struct iommu_ops *of_iommu_get_ops(struct device_node *np) · line 172

Quoted 10 lines
drivers/iommu/of_iommu.c +10 -0
+		iommu_spec.np = master_np;
+		pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
+				       &iommu_spec);
+		/*
+		 * ...then find out what that becomes once it escapes the PCI
+		 * bus into the system beyond, and which IOMMU it ends up at.
+		 */
+		if (of_pci_map_rid(master_np, iommu_spec.args[0], "iommu-map",
+				    "iommu-map-mask", &np, iommu_spec.args))
+			return NULL;

... because you're assumedly initialising np to NULL in case of_pci_map_rid returns 0, but doesn't set np...

drivers/iommu/of_iommu.c const struct iommu_ops *of_iommu_get_ops(struct device_node *np) · line 177

Quoted 5 lines
drivers/iommu/of_iommu.c +5 -0
+
+		/* We're not attempting to handle multi-alias devices yet */
+		iommu_spec.np = np;
+		iommu_spec.args_count = 1;
+		ops = of_iommu_get_ops(np);

... and then we call of_iommu_get_ops(NULL). Does that make any sense?

Will

author maintainer
#12 lore raw

PATCH v3 5/9 iommu/of: Handle iommu-map property for PCI

Quoted 66 lines · Will Deacon
Quoted 33 lines · Robin Murphy

Now that we have a way to pick up the RID translation and target IOMMU, hook up of_iommu_configure() to bring PCI devices into the of_xlate mechanism and allow them IOMMU-backed DMA ops without the need for driver-specific handling.

---

v3: Use explicit "iommu-map-mask", drop of_device_is_available() check.

 drivers/iommu/of_iommu.c | 43 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 36 insertions(+), 7 deletions(-)
drivers/iommu/of_iommu.c +5 -0
index 7e6369cffc95..25406a8b9d4e 100644
@@ -22,6 +22,7 @@
 #include <linux/limits.h>
 #include <linux/of.h>
 #include <linux/of_iommu.h>
+#include <linux/of_pci.h>
 #include <linux/of_platform.h>
 #include <linux/slab.h>
 
@@ -138,20 +139,48 @@ const struct iommu_ops *of_iommu_get_ops(struct device_node *np)
 	return ops;
 }
 
+static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
+{
+	struct of_phandle_args *iommu_spec = data;
+

drivers/iommu/of_iommu.c const struct iommu_ops *of_iommu_get_ops(struct device_node *np) · line 154

Quoted 10 lines
drivers/iommu/of_iommu.c +5 -1
+	iommu_spec->args[0] = alias;
+	return iommu_spec->np == pdev->bus->dev.of_node;
+}
+
 const struct iommu_ops *of_iommu_configure(struct device *dev,
 					   struct device_node *master_np)
 {
 	struct of_phandle_args iommu_spec;
-	struct device_node *np;
+	struct device_node *np = NULL;

This sets off some alarm bells...

Quoted 19 lines
drivers/iommu/of_iommu.c +10 -6
 	const struct iommu_ops *ops = NULL;
 	int idx = 0;
 
-	/*
-	 * We can't do much for PCI devices without knowing how
-	 * device IDs are wired up from the PCI bus to the IOMMU.
-	 */
-	if (dev_is_pci(dev))
-		return NULL;
+	if (dev_is_pci(dev)) {
+		/*
+		 * Start by tracing the RID alias down the PCI topology as
+		 * far as the host bridge whose OF node we have...
+		 */
+		iommu_spec.np = master_np;
+		pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
+				       &iommu_spec);
+		/*
+		 * ...then find out what that becomes once it escapes the PCI
Quoted 8 lines

drivers/iommu/of_iommu.c const struct iommu_ops *of_iommu_get_ops(struct device_node *np) · line 172

Quoted 5 lines
drivers/iommu/of_iommu.c +5 -0
+		 * bus into the system beyond, and which IOMMU it ends up at.
+		 */
+		if (of_pci_map_rid(master_np, iommu_spec.args[0], "iommu-map",
+				    "iommu-map-mask", &np, iommu_spec.args))
+			return NULL;

... because you're assumedly initialising np to NULL in case of_pci_map_rid returns 0, but doesn't set np...

Not quite; we're passing &np as the optional "target" argument to of_pci_map_rid - since that argument is provided, it will either find a matching translation (and fill in np in the process) or return -EFAULT.

Quoted 7 lines

drivers/iommu/of_iommu.c const struct iommu_ops *of_iommu_get_ops(struct device_node *np) · line 177

Quoted 5 lines
drivers/iommu/of_iommu.c +5 -0
+
+		/* We're not attempting to handle multi-alias devices yet */
+		iommu_spec.np = np;
+		iommu_spec.args_count = 1;
+		ops = of_iommu_get_ops(np);

... and then we call of_iommu_get_ops(NULL). Does that make any sense?

It would actually work out, since nobody should have registered any ops for a NULL node, but as above won't happen in practice anyway.

Robin.

Quoted 3 lines

Will

author maintainer
#13 lore raw

PATCH v3 6/9 iommu/of: Introduce iommu_fwspec

In reply to Robin Murphy

Introduce a common structure to hold the per-device firmware data that non-architectural IOMMU drivers generally need to keep track of. Initially this is DT-specific to complement the existing of_iommu support code, but will generalise further once other firmware methods (e.g. ACPI IORT) come along.

Ultimately the aim is to promote the fwspec to a first-class member of struct device, and handle the init/free automatically in the firmware code. That way we can have API calls look for dev->fwspec->iommu_ops before falling back to dev->bus->iommu_ops, and thus gracefully handle those troublesome multi-IOMMU systems which we currently cannot. To start with, though, make use of the existing archdata field and delegate the init/free to drivers to allow an incremental conversion rather than the impractical pain of trying to attempt everything in one go.

---

v3: New.

 drivers/iommu/of_iommu.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/of_iommu.h | 18 ++++++++++++++++++
 2 files changed, 65 insertions(+)
drivers/iommu/of_iommu.c +47 -0
index 25406a8b9d4e..2b90c1f56ff2 100644
@@ -220,3 +220,50 @@ void __init of_iommu_init(void)
 				of_node_full_name(np));
 	}
 }
+
+int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np)
+{
+	struct iommu_fwspec *fwspec = dev->archdata.iommu;
+
+	if (fwspec)
+		return 0;
+
+	fwspec = kzalloc(sizeof(*fwspec), GFP_KERNEL);
+	if (!fwspec)
+		return -ENOMEM;
+
+	fwspec->iommu_np = of_node_get(iommu_np);
+	fwspec->iommu_ops = of_iommu_get_ops(iommu_np);
+	dev->archdata.iommu = fwspec;
+	return 0;
+}
+
+void iommu_fwspec_free(struct device *dev)
+{
+	struct iommu_fwspec *fwspec = dev->archdata.iommu;
+
+	if (fwspec) {
+		of_node_put(fwspec->iommu_np);
+		kfree(fwspec);
+	}
+}
+
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
+{
+	struct iommu_fwspec *fwspec = dev->archdata.iommu;
+	size_t size;
+
+	if (!fwspec)
+		return -EINVAL;
+
+	size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + 1]);
+	fwspec = krealloc(dev->archdata.iommu, size, GFP_KERNEL);
+	if (!fwspec)
+		return -ENOMEM;
+
+	while (num_ids--)
+		fwspec->ids[fwspec->num_ids++] = *ids++;
+
+	dev->archdata.iommu = fwspec;
+	return 0;
+}
include/linux/of_iommu.h +18 -0
index bd02b44902d0..5a4f516cfcfe 100644
@@ -15,6 +15,14 @@ extern void of_iommu_init(void);
 extern const struct iommu_ops *of_iommu_configure(struct device *dev,
 					struct device_node *master_np);
 
+struct iommu_fwspec {
+	const struct iommu_ops	*iommu_ops;
+	struct device_node	*iommu_np;
+	void			*iommu_priv;
+	unsigned int		num_ids;
+	u32			ids[];
+};
+
 #else
 
 static inline int of_get_dma_window(struct device_node *dn, const char *prefix,
@@ -31,8 +39,18 @@ static inline const struct iommu_ops *of_iommu_configure(struct device *dev,
 	return NULL;
 }
 
+struct iommu_fwspec;
+
 #endif	/* CONFIG_OF_IOMMU */
 
+int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np);
+void iommu_fwspec_free(struct device *dev);
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
+static inline struct iommu_fwspec *dev_iommu_fwspec(struct device *dev)
+{
+	return dev->archdata.iommu;
+}
+
 void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops);
 const struct iommu_ops *of_iommu_get_ops(struct device_node *np);
 
Signature · 1 line
2.8.1.dirty
#14 lore raw

PATCH v3 6/9 iommu/of: Introduce iommu_fwspec

Quoted 30 lines · Robin Murphy

Introduce a common structure to hold the per-device firmware data that non-architectural IOMMU drivers generally need to keep track of. Initially this is DT-specific to complement the existing of_iommu support code, but will generalise further once other firmware methods (e.g. ACPI IORT) come along.

Ultimately the aim is to promote the fwspec to a first-class member of struct device, and handle the init/free automatically in the firmware code. That way we can have API calls look for dev->fwspec->iommu_ops before falling back to dev->bus->iommu_ops, and thus gracefully handle those troublesome multi-IOMMU systems which we currently cannot. To start with, though, make use of the existing archdata field and delegate the init/free to drivers to allow an incremental conversion rather than the impractical pain of trying to attempt everything in one go.

---

v3: New.

 drivers/iommu/of_iommu.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/of_iommu.h | 18 ++++++++++++++++++
 2 files changed, 65 insertions(+)
drivers/iommu/of_iommu.c +0 -0
index 25406a8b9d4e..2b90c1f56ff2 100644

drivers/iommu/of_iommu.c void __init of_iommu_init(void) · line 224

Quoted 5 lines
drivers/iommu/of_iommu.c +2 -0
@@ -220,3 +220,50 @@ void __init of_iommu_init(void)
 				of_node_full_name(np));
 	}
 }
+
+int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np)

These functions might want an of_* prefix to match the rest of the file, or do you plan to re-use these later for ACPI?

Quoted 24 lines
include/linux/of_iommu.h +9 -0
index bd02b44902d0..5a4f516cfcfe 100644
@@ -15,6 +15,14 @@ extern void of_iommu_init(void);
 extern const struct iommu_ops *of_iommu_configure(struct device *dev,
 					struct device_node *master_np);
 
+struct iommu_fwspec {
+	const struct iommu_ops	*iommu_ops;
+	struct device_node	*iommu_np;
+	void			*iommu_priv;
+	unsigned int		num_ids;
+	u32			ids[];
+};
+
 #else
 
 static inline int of_get_dma_window(struct device_node *dn, const char *prefix,
@@ -31,8 +39,18 @@ static inline const struct iommu_ops *of_iommu_configure(struct device *dev,
 	return NULL;
 }
 
+struct iommu_fwspec;

include/linux/of_iommu.h static inline const struct iommu_ops *of_iommu_configure(struct device *dev, · line 52

Quoted 10 lines
include/linux/of_iommu.h +8 -0
+
 #endif	/* CONFIG_OF_IOMMU */
 
+int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np);
+void iommu_fwspec_free(struct device *dev);
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
+static inline struct iommu_fwspec *dev_iommu_fwspec(struct device *dev)
+{
+	return dev->archdata.iommu;
+}

I'm a bit nervous about putting this inline in a header file, since not all architectures unconditionally provide the iommu field in dev_archdata.

Will

author maintainer
#15 lore raw

PATCH v3 6/9 iommu/of: Introduce iommu_fwspec

Quoted 26 lines · Will Deacon
Quoted 25 lines · Robin Murphy

Introduce a common structure to hold the per-device firmware data that non-architectural IOMMU drivers generally need to keep track of. Initially this is DT-specific to complement the existing of_iommu support code, but will generalise further once other firmware methods (e.g. ACPI IORT) come along.

Ultimately the aim is to promote the fwspec to a first-class member of struct device, and handle the init/free automatically in the firmware code. That way we can have API calls look for dev->fwspec->iommu_ops before falling back to dev->bus->iommu_ops, and thus gracefully handle those troublesome multi-IOMMU systems which we currently cannot. To start with, though, make use of the existing archdata field and delegate the init/free to drivers to allow an incremental conversion rather than the impractical pain of trying to attempt everything in one go.

---

v3: New.

 drivers/iommu/of_iommu.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/of_iommu.h | 18 ++++++++++++++++++
 2 files changed, 65 insertions(+)
Quoted 13 lines

drivers/iommu/of_iommu.c void __init of_iommu_init(void) · line 224

Quoted 10 lines
drivers/iommu/of_iommu.c +2 -0
index 25406a8b9d4e..2b90c1f56ff2 100644
@@ -220,3 +220,50 @@ void __init of_iommu_init(void)
 				of_node_full_name(np));
 	}
 }
+
+int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np)

These functions might want an of_* prefix to match the rest of the file, or do you plan to re-use these later for ACPI?

Indeed, the naming is a deliberate concession to the future - I was trying to pre-empt the idea that the interface might be intended to grow into separate of_* and iort_* versions. I'm just using raw device_nodes for the moment as I don't think we'd reached a consensus on whether fwnode_handle or some other abstraction was the appropriate way to go.

Quoted 27 lines
Quoted 27 lines
include/linux/of_iommu.h +10 -0
index bd02b44902d0..5a4f516cfcfe 100644
@@ -15,6 +15,14 @@ extern void of_iommu_init(void);
 extern const struct iommu_ops *of_iommu_configure(struct device *dev,
 					struct device_node *master_np);
 
+struct iommu_fwspec {
+	const struct iommu_ops	*iommu_ops;
+	struct device_node	*iommu_np;
+	void			*iommu_priv;
+	unsigned int		num_ids;
+	u32			ids[];
+};
+
 #else
 
 static inline int of_get_dma_window(struct device_node *dn, const char *prefix,
@@ -31,8 +39,18 @@ static inline const struct iommu_ops *of_iommu_configure(struct device *dev,
 	return NULL;
 }
 
+struct iommu_fwspec;
+
 #endif	/* CONFIG_OF_IOMMU */
 
Quoted 10 lines

include/linux/of_iommu.h static inline const struct iommu_ops *of_iommu_configure(struct device *dev, · line 52

Quoted 7 lines
include/linux/of_iommu.h +7 -0
+int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np);
+void iommu_fwspec_free(struct device *dev);
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
+static inline struct iommu_fwspec *dev_iommu_fwspec(struct device *dev)
+{
+	return dev->archdata.iommu;
+}

I'm a bit nervous about putting this inline in a header file, since not all architectures unconditionally provide the iommu field in dev_archdata.

Ah yes, you're right, this shouldn't be defined without OF_IOMMU (or the IORT equivalent) anyway. Clearly the premature optimisation demons had begun whispering "A measly one-line accessor in a separate compilation unit? Won't somebody think of the chil^Wbranch predictors!?"

Robin.

Quoted 3 lines

Will

#16 lore raw

PATCH v3 6/9 iommu/of: Introduce iommu_fwspec

Quoted 41 lines · Robin Murphy
Quoted 26 lines · Will Deacon
Quoted 25 lines · Robin Murphy

Introduce a common structure to hold the per-device firmware data that non-architectural IOMMU drivers generally need to keep track of. Initially this is DT-specific to complement the existing of_iommu support code, but will generalise further once other firmware methods (e.g. ACPI IORT) come along.

Ultimately the aim is to promote the fwspec to a first-class member of struct device, and handle the init/free automatically in the firmware code. That way we can have API calls look for dev->fwspec->iommu_ops before falling back to dev->bus->iommu_ops, and thus gracefully handle those troublesome multi-IOMMU systems which we currently cannot. To start with, though, make use of the existing archdata field and delegate the init/free to drivers to allow an incremental conversion rather than the impractical pain of trying to attempt everything in one go.

---

v3: New.

 drivers/iommu/of_iommu.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/of_iommu.h | 18 ++++++++++++++++++
 2 files changed, 65 insertions(+)
Quoted 13 lines

drivers/iommu/of_iommu.c void __init of_iommu_init(void) · line 224

Quoted 10 lines
drivers/iommu/of_iommu.c +2 -0
index 25406a8b9d4e..2b90c1f56ff2 100644
@@ -220,3 +220,50 @@ void __init of_iommu_init(void)
 				of_node_full_name(np));
 	}
 }
+
+int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np)

These functions might want an of_* prefix to match the rest of the file, or do you plan to re-use these later for ACPI?

Quoted 5 lines

Indeed, the naming is a deliberate concession to the future - I was trying to pre-empt the idea that the interface might be intended to grow into separate of_* and iort_* versions. I'm just using raw device_nodes for the moment as I don't think we'd reached a consensus on whether fwnode_handle or some other abstraction was the appropriate way to go.

Yes, for the time being you could just consider the whole thing OF specific and in my series providing IORT support I will have to find a place for this code to live, likely to be a new compilation unit anyway (which means that the raw device_node will become fwnode_handle types too in the process).

Quoted 38 lines
Quoted 27 lines
Quoted 27 lines
include/linux/of_iommu.h +10 -0
index bd02b44902d0..5a4f516cfcfe 100644
@@ -15,6 +15,14 @@ extern void of_iommu_init(void);
 extern const struct iommu_ops *of_iommu_configure(struct device *dev,
 					struct device_node *master_np);
 
+struct iommu_fwspec {
+	const struct iommu_ops	*iommu_ops;
+	struct device_node	*iommu_np;
+	void			*iommu_priv;
+	unsigned int		num_ids;
+	u32			ids[];
+};
+
 #else
 
 static inline int of_get_dma_window(struct device_node *dn, const char *prefix,
@@ -31,8 +39,18 @@ static inline const struct iommu_ops *of_iommu_configure(struct device *dev,
 	return NULL;
 }
 
+struct iommu_fwspec;
+
 #endif	/* CONFIG_OF_IOMMU */
 
Quoted 10 lines

include/linux/of_iommu.h static inline const struct iommu_ops *of_iommu_configure(struct device *dev, · line 52

Quoted 7 lines
include/linux/of_iommu.h +7 -0
+int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np);
+void iommu_fwspec_free(struct device *dev);
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
+static inline struct iommu_fwspec *dev_iommu_fwspec(struct device *dev)
+{
+	return dev->archdata.iommu;
+}

I'm a bit nervous about putting this inline in a header file, since not all architectures unconditionally provide the iommu field in dev_archdata.

Quoted 4 lines

Ah yes, you're right, this shouldn't be defined without OF_IOMMU (or the IORT equivalent) anyway. Clearly the premature optimisation demons had begun whispering "A measly one-line accessor in a separate compilation unit? Won't somebody think of the chil^Wbranch predictors!?"

Well, moving this inline to of_iommu.c (as per v4) improves things a bit, but it looks a bit incosistent to me anyway given that we would *assume* archdata.iommu exists if CONFIG_OF_IOMMU, which should be true (is it on eg powerpc ?) but it is still an assumption AFAIK.

An option could be to add the generic iommu_fwspec API but for the time being decoupling it from struct device and leaving it to IOMMU drivers to decide where the iommu_fwspec structure pointer should be stored (ie on ARM systems dev->archdata.iommu).

Understood, you want to keep struct device part of the API so that, when the iommu_fwspec will be integrated into struct device we won't have to change the iommu_fwspec API, still, it does not look right.

Other option would be adding this code to a separate compilation unit that is compiled only on (ARM || ARM64), not sure what's the most reasonable option here.

Lorenzo

author maintainer
#17 lore raw

PATCH v3 7/9 iommu/arm-smmu: Implement of_xlate() for SMMUv3

In reply to Robin Murphy

Now that we can properly describe the mapping between PCI RIDs and stream IDs via "iommu-map", and have it fed it to the driver automatically via of_xlate(), rework the SMMUv3 driver to benefit from that, and get rid of the current misuse of the "iommus" binding.

Since having of_xlate wired up means that masters will now be given the appropriate DMA ops, we also need to make sure that default domains work properly. This necessitates dispensing with the "whole group at a time" notion for attaching to a domain, as devices which share a group get attached to the group's default domain one by one as they are initially probed.

---

v3: Significant rework, now based around iommu_fwspec; avoid touching
low-level STE code; drop separate "finish off default domains" patch

 drivers/iommu/arm-smmu-v3.c | 270 ++++++++++++++++++--------------------------
 1 file changed, 113 insertions(+), 157 deletions(-)
drivers/iommu/arm-smmu-v3.c +113 -157
index 94b68213c50d..8ce2a4f9342b 100644
@@ -30,6 +30,7 @@
 #include <linux/msi.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_iommu.h>
 #include <linux/of_platform.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
@@ -606,12 +607,9 @@ struct arm_smmu_device {
 	struct arm_smmu_strtab_cfg	strtab_cfg;
 };
 
-/* SMMU private data for an IOMMU group */
-struct arm_smmu_group {
+/* SMMU private data for each master */
+struct arm_smmu_master_data {
 	struct arm_smmu_device		*smmu;
-	struct arm_smmu_domain		*domain;
-	int				num_sids;
-	u32				*sids;
 	struct arm_smmu_strtab_ent	ste;
 };
 
@@ -1575,20 +1573,6 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain)
 	return ret;
 }
 
-static struct arm_smmu_group *arm_smmu_group_get(struct device *dev)
-{
-	struct iommu_group *group;
-	struct arm_smmu_group *smmu_group;
-
-	group = iommu_group_get(dev);
-	if (!group)
-		return NULL;
-
-	smmu_group = iommu_group_get_iommudata(group);
-	iommu_group_put(group);
-	return smmu_group;
-}
-
 static __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
 {
 	__le64 *step;
@@ -1611,27 +1595,17 @@ static __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
 	return step;
 }
 
-static int arm_smmu_install_ste_for_group(struct arm_smmu_group *smmu_group)
+static int arm_smmu_install_ste_for_dev(struct iommu_fwspec *fwspec)
 {
 	int i;
-	struct arm_smmu_domain *smmu_domain = smmu_group->domain;
-	struct arm_smmu_strtab_ent *ste = &smmu_group->ste;
-	struct arm_smmu_device *smmu = smmu_group->smmu;
+	struct arm_smmu_master_data *master = fwspec->iommu_priv;
+	struct arm_smmu_device *smmu = master->smmu;
 
-	if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
-		ste->s1_cfg = &smmu_domain->s1_cfg;
-		ste->s2_cfg = NULL;
-		arm_smmu_write_ctx_desc(smmu, ste->s1_cfg);
-	} else {
-		ste->s1_cfg = NULL;
-		ste->s2_cfg = &smmu_domain->s2_cfg;
-	}
-
-	for (i = 0; i < smmu_group->num_sids; ++i) {
-		u32 sid = smmu_group->sids[i];
+	for (i = 0; i < fwspec->num_ids; ++i) {
+		u32 sid = fwspec->ids[i];
 		__le64 *step = arm_smmu_get_step_for_sid(smmu, sid);
 
-		arm_smmu_write_strtab_ent(smmu, sid, step, ste);
+		arm_smmu_write_strtab_ent(smmu, sid, step, &master->ste);
 	}
 
 	return 0;
@@ -1639,13 +1613,12 @@ static int arm_smmu_install_ste_for_group(struct arm_smmu_group *smmu_group)
 
 static void arm_smmu_detach_dev(struct device *dev)
 {
-	struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev);
+	struct iommu_fwspec *fwspec = dev_iommu_fwspec(dev);
+	struct arm_smmu_master_data *master = fwspec->iommu_priv;
 
-	smmu_group->ste.bypass = true;
-	if (arm_smmu_install_ste_for_group(smmu_group) < 0)
+	master->ste.bypass = true;
+	if (arm_smmu_install_ste_for_dev(fwspec) < 0)
 		dev_warn(dev, "failed to install bypass STE\n");
-
-	smmu_group->domain = NULL;
 }
 
 static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
@@ -1653,16 +1626,21 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	int ret = 0;
 	struct arm_smmu_device *smmu;
 	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
-	struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev);
+	struct arm_smmu_master_data *master;
+	struct arm_smmu_strtab_ent *ste;
+	struct iommu_fwspec *fwspec = dev_iommu_fwspec(dev);
 
-	if (!smmu_group)
+	if (!fwspec)
 		return -ENOENT;
 
+	master = fwspec->iommu_priv;
+	smmu = master->smmu;
+	ste = &master->ste;
+
 	/* Already attached to a different domain? */
-	if (smmu_group->domain && smmu_group->domain != smmu_domain)
+	if (!ste->bypass)
 		arm_smmu_detach_dev(dev);
 
-	smmu = smmu_group->smmu;
 	mutex_lock(&smmu_domain->init_mutex);
 
 	if (!smmu_domain->smmu) {
@@ -1681,21 +1659,21 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 		goto out_unlock;
 	}
 
-	/* Group already attached to this domain? */
-	if (smmu_group->domain)
-		goto out_unlock;
+	ste->bypass = false;
+	ste->valid = true;
 
-	smmu_group->domain	= smmu_domain;
+	if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+		ste->s1_cfg = &smmu_domain->s1_cfg;
+		ste->s2_cfg = NULL;
+		arm_smmu_write_ctx_desc(smmu, ste->s1_cfg);
+	} else {
+		ste->s1_cfg = NULL;
+		ste->s2_cfg = &smmu_domain->s2_cfg;
+	}
 
-	/*
-	 * FIXME: This should always be "false" once we have IOMMU-backed
-	 * DMA ops for all devices behind the SMMU.
-	 */
-	smmu_group->ste.bypass	= domain->type == IOMMU_DOMAIN_DMA;
-
-	ret = arm_smmu_install_ste_for_group(smmu_group);
+	ret = arm_smmu_install_ste_for_dev(fwspec);
 	if (ret < 0)
-		smmu_group->domain = NULL;
+		ste->valid = false;
 
 out_unlock:
 	mutex_unlock(&smmu_domain->init_mutex);
@@ -1754,40 +1732,14 @@ arm_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
 	return ret;
 }
 
-static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *sidp)
+static struct arm_smmu_device *arm_smmu_get_by_node(struct device_node *np)
 {
-	*(u32 *)sidp = alias;
-	return 0; /* Continue walking */
-}
+	struct platform_device *smmu_pdev = of_find_device_by_node(np);
 
-static void __arm_smmu_release_pci_iommudata(void *data)
-{
-	kfree(data);
-}
-
-static struct arm_smmu_device *arm_smmu_get_for_pci_dev(struct pci_dev *pdev)
-{
-	struct device_node *of_node;
-	struct platform_device *smmu_pdev;
-	struct arm_smmu_device *smmu = NULL;
-	struct pci_bus *bus = pdev->bus;
-
-	/* Walk up to the root bus */
-	while (!pci_is_root_bus(bus))
-		bus = bus->parent;
-
-	/* Follow the "iommus" phandle from the host controller */
-	of_node = of_parse_phandle(bus->bridge->parent->of_node, "iommus", 0);
-	if (!of_node)
+	if (!smmu_pdev)
 		return NULL;
 
-	/* See if we can find an SMMU corresponding to the phandle */
-	smmu_pdev = of_find_device_by_node(of_node);
-	if (smmu_pdev)
-		smmu = platform_get_drvdata(smmu_pdev);
-
-	of_node_put(of_node);
-	return smmu;
+	return platform_get_drvdata(smmu_pdev);
 }
 
 static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid)
@@ -1800,94 +1752,74 @@ static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid)
 	return sid < limit;
 }
 
+static struct iommu_ops arm_smmu_ops;
+
 static int arm_smmu_add_device(struct device *dev)
 {
 	int i, ret;
-	u32 sid, *sids;
-	struct pci_dev *pdev;
-	struct iommu_group *group;
-	struct arm_smmu_group *smmu_group;
 	struct arm_smmu_device *smmu;
+	struct arm_smmu_master_data *master;
+	struct iommu_fwspec *fwspec = dev_iommu_fwspec(dev);
+	struct iommu_group *group;
 
-	/* We only support PCI, for now */
-	if (!dev_is_pci(dev))
+	if (!fwspec || fwspec->iommu_ops != &arm_smmu_ops)
 		return -ENODEV;
-
-	pdev = to_pci_dev(dev);
-	group = iommu_group_get_for_dev(dev);
-	if (IS_ERR(group))
-		return PTR_ERR(group);
-
-	smmu_group = iommu_group_get_iommudata(group);
-	if (!smmu_group) {
-		smmu = arm_smmu_get_for_pci_dev(pdev);
-		if (!smmu) {
-			ret = -ENOENT;
-			goto out_remove_dev;
-		}
-
-		smmu_group = kzalloc(sizeof(*smmu_group), GFP_KERNEL);
-		if (!smmu_group) {
-			ret = -ENOMEM;
-			goto out_remove_dev;
-		}
-
-		smmu_group->ste.valid	= true;
-		smmu_group->smmu	= smmu;
-		iommu_group_set_iommudata(group, smmu_group,
-					  __arm_smmu_release_pci_iommudata);
+	/*
+	 * We _can_ actually withstand dodgy bus code re-calling add_device()
+	 * without an intervening remove_device()/of_xlate() sequence, but
+	 * we're not going to do so quietly...
+	 */
+	if (WARN_ON_ONCE(fwspec->iommu_priv)) {
+		master = fwspec->iommu_priv;
+		smmu = master->smmu;
 	} else {
-		smmu = smmu_group->smmu;
+		smmu = arm_smmu_get_by_node(fwspec->iommu_np);
+		if (!smmu)
+			return -ENODEV;
+		master = kzalloc(sizeof(*master), GFP_KERNEL);
+		if (!master)
+			return -ENOMEM;
+
+		master->smmu = smmu;
+		fwspec->iommu_priv = master;
 	}
 
-	/* Assume SID == RID until firmware tells us otherwise */
-	pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid);
-	for (i = 0; i < smmu_group->num_sids; ++i) {
-		/* If we already know about this SID, then we're done */
-		if (smmu_group->sids[i] == sid)
-			goto out_put_group;
+	/* Check the SIDs are in range of the SMMU and our stream table */
+	for (i = 0; i < fwspec->num_ids; i++) {
+		u32 sid = fwspec->ids[i];
+
+		if (!arm_smmu_sid_in_range(smmu, sid))
+			return -ERANGE;
+
+		/* Ensure l2 strtab is initialised */
+		if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
+			ret = arm_smmu_init_l2_strtab(smmu, sid);
+			if (ret)
+				return ret;
+		}
 	}
 
-	/* Check the SID is in range of the SMMU and our stream table */
-	if (!arm_smmu_sid_in_range(smmu, sid)) {
-		ret = -ERANGE;
-		goto out_remove_dev;
-	}
+	group = iommu_group_get_for_dev(dev);
+	if (!IS_ERR(group))
+		iommu_group_put(group);
 
-	/* Ensure l2 strtab is initialised */
-	if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
-		ret = arm_smmu_init_l2_strtab(smmu, sid);
-		if (ret)
-			goto out_remove_dev;
-	}
-
-	/* Resize the SID array for the group */
-	smmu_group->num_sids++;
-	sids = krealloc(smmu_group->sids, smmu_group->num_sids * sizeof(*sids),
-			GFP_KERNEL);
-	if (!sids) {
-		smmu_group->num_sids--;
-		ret = -ENOMEM;
-		goto out_remove_dev;
-	}
-
-	/* Add the new SID */
-	sids[smmu_group->num_sids - 1] = sid;
-	smmu_group->sids = sids;
-
-out_put_group:
-	iommu_group_put(group);
-	return 0;
-
-out_remove_dev:
-	iommu_group_remove_device(dev);
-	iommu_group_put(group);
-	return ret;
+	return PTR_ERR_OR_ZERO(group);
 }
 
 static void arm_smmu_remove_device(struct device *dev)
 {
+	struct iommu_fwspec *fwspec = dev_iommu_fwspec(dev);
+	struct arm_smmu_master_data *master;
+
+	if (!fwspec || fwspec->iommu_ops != &arm_smmu_ops)
+		return;
+
+	master = fwspec->iommu_priv;
+	if (master && master->ste.valid)
+		arm_smmu_detach_dev(dev);
 	iommu_group_remove_device(dev);
+	kfree(master);
+	iommu_fwspec_free(dev);
 }
 
 static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
@@ -1934,6 +1866,21 @@ out_unlock:
 	return ret;
 }
 
+static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
+{
+	int ret;
+
+	/* We only support PCI, for now */
+	if (!dev_is_pci(dev))
+		return -ENODEV;
+
+	ret = iommu_fwspec_init(dev, args->np);
+	if (!ret)
+		ret = iommu_fwspec_add_ids(dev, &args->args[0], 1);
+
+	return ret;
+}
+
 static struct iommu_ops arm_smmu_ops = {
 	.capable		= arm_smmu_capable,
 	.domain_alloc		= arm_smmu_domain_alloc,
@@ -1947,6 +1894,7 @@ static struct iommu_ops arm_smmu_ops = {
 	.device_group		= pci_device_group,
 	.domain_get_attr	= arm_smmu_domain_get_attr,
 	.domain_set_attr	= arm_smmu_domain_set_attr,
+	.of_xlate		= arm_smmu_of_xlate,
 	.pgsize_bitmap		= -1UL, /* Restricted during device attach */
 };
 
@@ -2697,6 +2645,14 @@ static void __exit arm_smmu_exit(void)
 subsys_initcall(arm_smmu_init);
 module_exit(arm_smmu_exit);
 
+static int __init arm_smmu_of_init(struct device_node *np)
+{
+	of_iommu_set_ops(np, &arm_smmu_ops);
+
+	return 0;
+}
+IOMMU_OF_DECLARE(arm_smmuv3, "arm,smmu-v3", arm_smmu_of_init);
+
 MODULE_DESCRIPTION("IOMMU API for ARM architected SMMUv3 implementations");
 MODULE_AUTHOR("Will Deacon <[email protected]>");
 MODULE_LICENSE("GPL v2");
Signature · 1 line
2.8.1.dirty
#18 lore raw

PATCH v3 7/9 iommu/arm-smmu: Implement of_xlate() for SMMUv3

Quoted 14 lines · Robin Murphy

Now that we can properly describe the mapping between PCI RIDs and stream IDs via "iommu-map", and have it fed it to the driver automatically via of_xlate(), rework the SMMUv3 driver to benefit from that, and get rid of the current misuse of the "iommus" binding.

Since having of_xlate wired up means that masters will now be given the appropriate DMA ops, we also need to make sure that default domains work properly. This necessitates dispensing with the "whole group at a time" notion for attaching to a domain, as devices which share a group get attached to the group's default domain one by one as they are initially probed.

---

[...]

Quoted 71 lines

static int arm_smmu_add_device(struct device *dev) {

drivers/iommu/arm-smmu-v3.c +25 -37
 	int i, ret;
-	u32 sid, *sids;
-	struct pci_dev *pdev;
-	struct iommu_group *group;
-	struct arm_smmu_group *smmu_group;
 	struct arm_smmu_device *smmu;
+	struct arm_smmu_master_data *master;
+	struct iommu_fwspec *fwspec = dev_iommu_fwspec(dev);
+	struct iommu_group *group;
 
-	/* We only support PCI, for now */
-	if (!dev_is_pci(dev))
+	if (!fwspec || fwspec->iommu_ops != &arm_smmu_ops)
 		return -ENODEV;
-
-	pdev = to_pci_dev(dev);
-	group = iommu_group_get_for_dev(dev);
-	if (IS_ERR(group))
-		return PTR_ERR(group);
-
-	smmu_group = iommu_group_get_iommudata(group);
-	if (!smmu_group) {
-		smmu = arm_smmu_get_for_pci_dev(pdev);
-		if (!smmu) {
-			ret = -ENOENT;
-			goto out_remove_dev;
-		}
-
-		smmu_group = kzalloc(sizeof(*smmu_group), GFP_KERNEL);
-		if (!smmu_group) {
-			ret = -ENOMEM;
-			goto out_remove_dev;
-		}
-
-		smmu_group->ste.valid	= true;
-		smmu_group->smmu	= smmu;
-		iommu_group_set_iommudata(group, smmu_group,
-					  __arm_smmu_release_pci_iommudata);
+	/*
+	 * We _can_ actually withstand dodgy bus code re-calling add_device()
+	 * without an intervening remove_device()/of_xlate() sequence, but
+	 * we're not going to do so quietly...
+	 */
+	if (WARN_ON_ONCE(fwspec->iommu_priv)) {
+		master = fwspec->iommu_priv;
+		smmu = master->smmu;
 	} else {
-		smmu = smmu_group->smmu;
+		smmu = arm_smmu_get_by_node(fwspec->iommu_np);
+		if (!smmu)
+			return -ENODEV;
+		master = kzalloc(sizeof(*master), GFP_KERNEL);
+		if (!master)
+			return -ENOMEM;
+
+		master->smmu = smmu;
+		fwspec->iommu_priv = master;
 	}
 
-	/* Assume SID == RID until firmware tells us otherwise */
-	pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid);
-	for (i = 0; i < smmu_group->num_sids; ++i) {
-		/* If we already know about this SID, then we're done */
-		if (smmu_group->sids[i] == sid)
-			goto out_put_group;
+	/* Check the SIDs are in range of the SMMU and our stream table */
+	for (i = 0; i < fwspec->num_ids; i++) {
+		u32 sid = fwspec->ids[i];
+

drivers/iommu/arm-smmu-v3.c static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid) · line 1800

Quoted 10 lines
drivers/iommu/arm-smmu-v3.c +9 -0
+		if (!arm_smmu_sid_in_range(smmu, sid))
+			return -ERANGE;
+
+		/* Ensure l2 strtab is initialised */
+		if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
+			ret = arm_smmu_init_l2_strtab(smmu, sid);
+			if (ret)
+				return ret;
+		}
 	}

Do you not need to do some cleanup here, rather than just returning (same for the -ERANGE above)? For example, freeing the master?

Will

author maintainer
#19 lore raw

PATCH v3 7/9 iommu/arm-smmu: Implement of_xlate() for SMMUv3

Quoted 94 lines · Will Deacon
Quoted 14 lines · Robin Murphy

Now that we can properly describe the mapping between PCI RIDs and stream IDs via "iommu-map", and have it fed it to the driver automatically via of_xlate(), rework the SMMUv3 driver to benefit from that, and get rid of the current misuse of the "iommus" binding.

Since having of_xlate wired up means that masters will now be given the appropriate DMA ops, we also need to make sure that default domains work properly. This necessitates dispensing with the "whole group at a time" notion for attaching to a domain, as devices which share a group get attached to the group's default domain one by one as they are initially probed.

---

[...]

Quoted 76 lines

static int arm_smmu_add_device(struct device *dev) {

drivers/iommu/arm-smmu-v3.c +30 -37
 	int i, ret;
-	u32 sid, *sids;
-	struct pci_dev *pdev;
-	struct iommu_group *group;
-	struct arm_smmu_group *smmu_group;
 	struct arm_smmu_device *smmu;
+	struct arm_smmu_master_data *master;
+	struct iommu_fwspec *fwspec = dev_iommu_fwspec(dev);
+	struct iommu_group *group;
 
-	/* We only support PCI, for now */
-	if (!dev_is_pci(dev))
+	if (!fwspec || fwspec->iommu_ops != &arm_smmu_ops)
 		return -ENODEV;
-
-	pdev = to_pci_dev(dev);
-	group = iommu_group_get_for_dev(dev);
-	if (IS_ERR(group))
-		return PTR_ERR(group);
-
-	smmu_group = iommu_group_get_iommudata(group);
-	if (!smmu_group) {
-		smmu = arm_smmu_get_for_pci_dev(pdev);
-		if (!smmu) {
-			ret = -ENOENT;
-			goto out_remove_dev;
-		}
-
-		smmu_group = kzalloc(sizeof(*smmu_group), GFP_KERNEL);
-		if (!smmu_group) {
-			ret = -ENOMEM;
-			goto out_remove_dev;
-		}
-
-		smmu_group->ste.valid	= true;
-		smmu_group->smmu	= smmu;
-		iommu_group_set_iommudata(group, smmu_group,
-					  __arm_smmu_release_pci_iommudata);
+	/*
+	 * We _can_ actually withstand dodgy bus code re-calling add_device()
+	 * without an intervening remove_device()/of_xlate() sequence, but
+	 * we're not going to do so quietly...
+	 */
+	if (WARN_ON_ONCE(fwspec->iommu_priv)) {
+		master = fwspec->iommu_priv;
+		smmu = master->smmu;
 	} else {
-		smmu = smmu_group->smmu;
+		smmu = arm_smmu_get_by_node(fwspec->iommu_np);
+		if (!smmu)
+			return -ENODEV;
+		master = kzalloc(sizeof(*master), GFP_KERNEL);
+		if (!master)
+			return -ENOMEM;
+
+		master->smmu = smmu;
+		fwspec->iommu_priv = master;
 	}
 
-	/* Assume SID == RID until firmware tells us otherwise */
-	pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid);
-	for (i = 0; i < smmu_group->num_sids; ++i) {
-		/* If we already know about this SID, then we're done */
-		if (smmu_group->sids[i] == sid)
-			goto out_put_group;
+	/* Check the SIDs are in range of the SMMU and our stream table */
+	for (i = 0; i < fwspec->num_ids; i++) {
+		u32 sid = fwspec->ids[i];
+
+		if (!arm_smmu_sid_in_range(smmu, sid))
+			return -ERANGE;
+
+		/* Ensure l2 strtab is initialised */
+		if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
Quoted 8 lines

drivers/iommu/arm-smmu-v3.c static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid) · line 1800

Quoted 5 lines
drivers/iommu/arm-smmu-v3.c +4 -0
+			ret = arm_smmu_init_l2_strtab(smmu, sid);
+			if (ret)
+				return ret;
+		}
 	}

Do you not need to do some cleanup here, rather than just returning (same for the -ERANGE above)? For example, freeing the master?

The master will be freed in arm_smmu_remove_device() if and when the device is removed from the bus. By the time we get this add_device call, we're already past the point of it being added to the bus and there's nothing we can do (all the return value here actually does is terminate the notifier chain). By bailing out before assigning the group, though, we prevent the device from being valid for any VFIO/IOMMU API usage.

That does make me think, however, that it would be worth trying to detect in arch_setup_dma_ops() if a device has been rejected by an IOMMU it was supposed to be associated with, and in such cases prevent it from doing DMA *at all* rather than just leaving it with the default SWIOTLB ops. I'll have a think about how feasible that is (with any luck it should slot fairly neatly into the probe ordering work touching those areas).

Robin.

Quoted 3 lines

Will

author maintainer
#20 lore raw

PATCH v3 8/9 iommu/arm-smmu: Support non-PCI devices with SMMUv3

In reply to Robin Murphy

With the device <-> stream ID relationship suitably abstracted and of_xlate() hooked up, the PCI dependency now looks, and is, entirely arbitrary. Any bus using the of_dma_configure() mechanism will work, so extend support to the platform and AMBA buses which do just that.

---

v3: Now actually tested; improve comment about duplicate IDs.

 drivers/iommu/Kconfig       |  2 +-
 drivers/iommu/arm-smmu-v3.c | 42 ++++++++++++++++++++++++++++++++++--------
 2 files changed, 35 insertions(+), 9 deletions(-)
drivers/iommu/Kconfig +1 -1
index ad0860383cb3..d1c66afefeed 100644
@@ -308,7 +308,7 @@ config ARM_SMMU
 
 config ARM_SMMU_V3
 	bool "ARM Ltd. System MMU Version 3 (SMMUv3) Support"
-	depends on ARM64 && PCI
+	depends on ARM64
 	select IOMMU_API
 	select IOMMU_IO_PGTABLE_LPAE
 	select GENERIC_MSI_IRQ_DOMAIN
drivers/iommu/arm-smmu-v3.c +34 -8

73 lines not on this page · raw message

Signature · 1 line
2.8.1.dirty
#21 lore raw

PATCH v3 8/9 iommu/arm-smmu: Support non-PCI devices with SMMUv3

Quoted 42 lines · Robin Murphy

With the device <-> stream ID relationship suitably abstracted and of_xlate() hooked up, the PCI dependency now looks, and is, entirely arbitrary. Any bus using the of_dma_configure() mechanism will work, so extend support to the platform and AMBA buses which do just that.

---

v3: Now actually tested; improve comment about duplicate IDs.

 drivers/iommu/Kconfig       |  2 +-
 drivers/iommu/arm-smmu-v3.c | 42 ++++++++++++++++++++++++++++++++++--------
 2 files changed, 35 insertions(+), 9 deletions(-)
drivers/iommu/Kconfig +1 -1
index ad0860383cb3..d1c66afefeed 100644
@@ -308,7 +308,7 @@ config ARM_SMMU
 
 config ARM_SMMU_V3
 	bool "ARM Ltd. System MMU Version 3 (SMMUv3) Support"
-	depends on ARM64 && PCI
+	depends on ARM64
 	select IOMMU_API
 	select IOMMU_IO_PGTABLE_LPAE
 	select GENERIC_MSI_IRQ_DOMAIN
drivers/iommu/arm-smmu-v3.c +2 -0
index 8ce2a4f9342b..735690e03818 100644
@@ -35,6 +35,8 @@
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 
+#include <linux/amba/bus.h>
+
 #include "io-pgtable.h"
 
 /* MMIO registers */
@@ -1822,6 +1824,25 @@ static void arm_smmu_remove_device(struct device *dev)
 	iommu_fwspec_free(dev);

drivers/iommu/arm-smmu-v3.c static void arm_smmu_remove_device(struct device *dev) · line 1834

Quoted 10 lines
drivers/iommu/arm-smmu-v3.c +8 -0
 }
 
+static struct iommu_group *arm_smmu_device_group(struct device *dev)
+{
+	struct iommu_group *group;
+
+	/*
+	 * The difficulty of efficient stream-ID-to-device lookup prevents us
+	 * from reasonably detecting aliasing outside of PCI buses, but for the
+	 * the same underlying reason (a sparse 32-bit ID space) there's also

extra 'the'

drivers/iommu/arm-smmu-v3.c static void arm_smmu_remove_device(struct device *dev) · line 1837

Quoted 3 lines
drivers/iommu/arm-smmu-v3.c +3 -0
+	 * little excuse for systems to be wired up with non-unique IDs in the
+	 * first place; consider them unsupported.
+	 */

This is a long-winded way of saying "we don't support aliasing SIDs outside of PCI".

Will

author maintainer
#22 lore raw

PATCH v3 8/9 iommu/arm-smmu: Support non-PCI devices with SMMUv3

Quoted 45 lines · Will Deacon
Quoted 44 lines · Robin Murphy

With the device <-> stream ID relationship suitably abstracted and of_xlate() hooked up, the PCI dependency now looks, and is, entirely arbitrary. Any bus using the of_dma_configure() mechanism will work, so extend support to the platform and AMBA buses which do just that.

---

v3: Now actually tested; improve comment about duplicate IDs.

 drivers/iommu/Kconfig       |  2 +-
 drivers/iommu/arm-smmu-v3.c | 42 ++++++++++++++++++++++++++++++++++--------
 2 files changed, 35 insertions(+), 9 deletions(-)
drivers/iommu/Kconfig +1 -1
index ad0860383cb3..d1c66afefeed 100644
@@ -308,7 +308,7 @@ config ARM_SMMU
 
 config ARM_SMMU_V3
 	bool "ARM Ltd. System MMU Version 3 (SMMUv3) Support"
-	depends on ARM64 && PCI
+	depends on ARM64
 	select IOMMU_API
 	select IOMMU_IO_PGTABLE_LPAE
 	select GENERIC_MSI_IRQ_DOMAIN
drivers/iommu/arm-smmu-v3.c +2 -0
index 8ce2a4f9342b..735690e03818 100644
@@ -35,6 +35,8 @@
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 
+#include <linux/amba/bus.h>
+
 #include "io-pgtable.h"
 
 /* MMIO registers */
@@ -1822,6 +1824,25 @@ static void arm_smmu_remove_device(struct device *dev)
 	iommu_fwspec_free(dev);
 }
 
Quoted 10 lines

drivers/iommu/arm-smmu-v3.c static void arm_smmu_remove_device(struct device *dev) · line 1834

Quoted 8 lines
drivers/iommu/arm-smmu-v3.c +8 -0
+static struct iommu_group *arm_smmu_device_group(struct device *dev)
+{
+	struct iommu_group *group;
+
+	/*
+	 * The difficulty of efficient stream-ID-to-device lookup prevents us
+	 * from reasonably detecting aliasing outside of PCI buses, but for the
+	 * the same underlying reason (a sparse 32-bit ID space) there's also

extra 'the'

Oops, thanks.

Quoted 6 lines

drivers/iommu/arm-smmu-v3.c static void arm_smmu_remove_device(struct device *dev) · line 1837

Quoted 3 lines
drivers/iommu/arm-smmu-v3.c +3 -0
+	 * little excuse for systems to be wired up with non-unique IDs in the
+	 * first place; consider them unsupported.
+	 */

This is a long-winded way of saying "we don't support aliasing SIDs outside of PCI".

It is. Someone said of the original terse 2-line comment "Worse: what if a SID in the DT aliases with a PCI master? It might be nice to have some basic sanity checking, at least.", so I expanded it to better clarify why there isn't. I'll try making it a little less florid.

Robin.

Quoted 3 lines

Will

author maintainer
#23 lore raw

PATCH v3 9/9 iommu/arm-smmu: Set PRIVCFG in stage 1 STEs

In reply to Robin Murphy

Implement the SMMUv3 equivalent of d346180e70b9 ("iommu/arm-smmu: Treat all device transactions as unprivileged"), so that once again those pesky DMA controllers with their privileged instruction fetches don't unexpectedly fault in stage 1 domains due to VMSAv8 rules.

---

v3: New.

 drivers/iommu/arm-smmu-v3.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
drivers/iommu/arm-smmu-v3.c +6 -1
index 735690e03818..e4c3916efba9 100644
@@ -263,6 +263,9 @@
 #define STRTAB_STE_1_SHCFG_INCOMING	1UL
 #define STRTAB_STE_1_SHCFG_SHIFT	44
 
+#define STRTAB_STE_1_PRIVCFG_UNPRIV	2UL
+#define STRTAB_STE_1_PRIVCFG_SHIFT	48
+
 #define STRTAB_STE_2_S2VMID_SHIFT	0
 #define STRTAB_STE_2_S2VMID_MASK	0xffffUL
 #define STRTAB_STE_2_VTCR_SHIFT		32
@@ -1070,7 +1073,9 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
 #ifdef CONFIG_PCI_ATS
 			 STRTAB_STE_1_EATS_TRANS << STRTAB_STE_1_EATS_SHIFT |
 #endif
-			 STRTAB_STE_1_STRW_NSEL1 << STRTAB_STE_1_STRW_SHIFT);
+			 STRTAB_STE_1_STRW_NSEL1 << STRTAB_STE_1_STRW_SHIFT |
+			 STRTAB_STE_1_PRIVCFG_UNPRIV <<
+			 STRTAB_STE_1_PRIVCFG_SHIFT);
 
 		if (smmu->features & ARM_SMMU_FEAT_STALLS)
 			dst[1] |= cpu_to_le64(STRTAB_STE_1_S1STALLD);
Signature · 1 line
2.8.1.dirty
author maintainer
#24 lore raw

RFC 1/2 iommu/dma: Restrict IOVAs to physical memory layout

In reply to Robin Murphy

Certain peripherals may be bestowed with knowledge of the physical memory map of the system in which they live, and refuse to handle addresses that they do not think are memory, which causes issues when remapping to arbitrary IOVAs. Sidestep the issue by restricting IOVA domains to only allocate addresses within ranges which match the physical memory layout.

---

Posting this as an RFC because it's something I've been having to use on Juno for all the PCI IOMMU development - it's pretty horrible, but I can't easily think of a nicer solution...

 drivers/iommu/dma-iommu.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
drivers/iommu/dma-iommu.c +19 -0
index ea5a9ebf0f78..2385fab382d8 100644
@@ -25,6 +25,7 @@
 #include <linux/huge_mm.h>
 #include <linux/iommu.h>
 #include <linux/iova.h>
+#include <linux/memblock.h>
 #include <linux/mm.h>
 #include <linux/scatterlist.h>
 #include <linux/vmalloc.h>
@@ -74,6 +75,23 @@ void iommu_put_dma_cookie(struct iommu_domain *domain)
 }
 EXPORT_SYMBOL(iommu_put_dma_cookie);
 
+static void iova_match_mem(struct iova_domain *iovad)
+{
+	struct memblock_region *reg;
+	unsigned long blk, base = iovad->start_pfn;
+	unsigned long shift = iova_shift(iovad);
+
+	for_each_memblock(memory, reg) {
+		blk = (reg->base + iova_mask(iovad)) >> shift;
+		if (blk > base)
+			reserve_iova(iovad, base, blk - 1);
+		base = (reg->base + reg->size) >> shift;
+	}
+	blk = -1UL >> shift;
+	if (blk > base)
+		reserve_iova(iovad, base, blk);
+}
+
 /**
  * iommu_dma_init_domain - Initialise a DMA mapping domain
  * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie()
@@ -123,6 +141,7 @@ int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size
 		iovad->dma_32bit_pfn = end_pfn;
 	} else {
 		init_iova_domain(iovad, 1UL << order, base_pfn, end_pfn);
+		iova_match_mem(iovad);
 	}
 	return 0;
 }
Signature · 1 line
2.8.1.dirty

A message that is not in the archive (sent off-list or not yet received): CALRxmdCuxLPogPTwpn2-B=PU=Ry0eNtgs2z+iZBPYtDc3NX-hQ@mail.gmail.com

#26 lore raw

RFC 1/2 iommu/dma: Restrict IOVAs to physical memory layout

Quoted 16 lines

-----Original Message-----
From: Robin Murphy <[email protected]>
Date: Tue, Jun 28, 2016 at 11:18 AM
Subject: [RFC 1/2] iommu/dma: Restrict IOVAs to physical memory layout
To: iommu at lists.linux-foundation.org, linux-arm-kernel at lists.infradead.org

Certain peripherals may be bestowed with knowledge of the physical memory map of the system in which they live, and refuse to handle addresses that they do not think are memory, which causes issues when remapping to arbitrary IOVAs. Sidestep the issue by restricting IOVA domains to only allocate addresses within ranges which match the physical memory layout.

Quoted 6 lines

---

Posting this as an RFC because it's something I've been having to use on Juno for all the PCI IOMMU development - it's pretty horrible, but I can't easily think of a nicer solution...

Maybe I'm not getting the implications of this looking at the patch in isolation, but how will this impact systems that have devices limited to 32-bit addressing?

In our memory map we have physical memory regions at:
0x00_8000_0000
0x80_8000_0000

Will devices with a 32-bit DMA mask still get 32-bit IOVAs?

Thanks,
Stuart

author maintainer
#27 lore raw

RFC 1/2 iommu/dma: Restrict IOVAs to physical memory layout

Quoted 27 lines · Stuart Yoder
Quoted 16 lines

-----Original Message-----
From: Robin Murphy <[email protected]>
Date: Tue, Jun 28, 2016 at 11:18 AM
Subject: [RFC 1/2] iommu/dma: Restrict IOVAs to physical memory layout
To: iommu at lists.linux-foundation.org, linux-arm-kernel at lists.infradead.org

Certain peripherals may be bestowed with knowledge of the physical memory map of the system in which they live, and refuse to handle addresses that they do not think are memory, which causes issues when remapping to arbitrary IOVAs. Sidestep the issue by restricting IOVA domains to only allocate addresses within ranges which match the physical memory layout.

Quoted 6 lines

---

Posting this as an RFC because it's something I've been having to use on Juno for all the PCI IOMMU development - it's pretty horrible, but I can't easily think of a nicer solution...

Maybe I'm not getting the implications of this looking at the patch in isolation, but how will this impact systems that have devices limited to 32-bit addressing?

Quoted 5 lines

In our memory map we have physical memory regions at:
0x00_8000_0000
0x80_8000_0000

Will devices with a 32-bit DMA mask still get 32-bit IOVAs?

Assuming there's some free IOVA space between 0x80000000 and 0xffffffff, yes, otherwise it gets nothing ;) This has no effect on the allocation behaviour in general, it just makes sure that within that behaviour, we avoid allocating any address that doesn't look "real". The primary issue is with 64-bit DMA masks - since it's a top-down allocator, you typically end up with the poor device issuing its first DMA transaction to 0xfffffffffffff000 which on Juno a) gets silently eaten by the root complex because it doesn't match any window in the PCI-AXI translation table, or b) goes wrong anyway because it's beyond the input address range of the SMMU (and there's something not quite right WRT truncation/sign-extension which I've not looked into closely and am semi-deliberately also sweeping under the rug thanks to the simpler hardware issue...)

As I say, it's hideous, but I can't see what else to do.

Robin.

Quoted 4 lines

Thanks,
Stuart

author maintainer
#28 lore raw

RFC 1/2 iommu/dma: Restrict IOVAs to physical memory layout

Quoted 48 lines · Robin Murphy
Quoted 27 lines · Stuart Yoder
Quoted 16 lines

-----Original Message-----
From: Robin Murphy <[email protected]>
Date: Tue, Jun 28, 2016 at 11:18 AM
Subject: [RFC 1/2] iommu/dma: Restrict IOVAs to physical memory layout
To: iommu at lists.linux-foundation.org, linux-arm-kernel at lists.infradead.org

Certain peripherals may be bestowed with knowledge of the physical memory map of the system in which they live, and refuse to handle addresses that they do not think are memory, which causes issues when remapping to arbitrary IOVAs. Sidestep the issue by restricting IOVA domains to only allocate addresses within ranges which match the physical memory layout.

Quoted 6 lines

---

Posting this as an RFC because it's something I've been having to use on Juno for all the PCI IOMMU development - it's pretty horrible, but I can't easily think of a nicer solution...

Maybe I'm not getting the implications of this looking at the patch in isolation, but how will this impact systems that have devices limited to 32-bit addressing?

Quoted 5 lines

In our memory map we have physical memory regions at:
0x00_8000_0000
0x80_8000_0000

Will devices with a 32-bit DMA mask still get 32-bit IOVAs?

Assuming there's some free IOVA space between 0x80000000 and 0xffffffff, yes, otherwise it gets nothing ;) This has no effect on the allocation behaviour in general, it just makes sure that within that behaviour, we avoid allocating any address that doesn't look "real". The primary issue is with 64-bit DMA masks - since it's a top-down allocator, you typically end up with the poor device issuing its first DMA transaction to 0xfffffffffffff000 which on Juno a) gets silently eaten by the root complex because it doesn't match any window in the PCI-AXI translation table, or b) goes wrong anyway because it's beyond the input address range of the SMMU (and there's something not quite right WRT truncation/sign-extension which I've not looked into closely and am semi-deliberately also sweeping under the rug thanks to the simpler hardware issue...)

Quoted 1 line

As I say, it's hideous, but I can't see what else to do.

Urgh, thinking some more, this is OK on Juno and LS2085 only because there *is* some RAM below 4GB to begin with. On something like Seattle where it's all high up, 32-bit peripherals will be as screwed as if the IOMMU wasn't there :(

How on Earth do we work out which devices can only DMA to arbitrary portions of their addressable range, and which are unrestricted? I guess the reasonable answer is to use "dma-ranges" on the PCI RC to describe the valid regions, but then we have to propagate that to the domain somehow, and only after we've taught Linux how to handle multiple dma-ranges entries at all (currently we'll just ignore everything other than the last one). As for ACPI...

In short; oh dear.

Robin.

Quoted 10 lines
Quoted 4 lines

Thanks,
Stuart

_______________________________________________
iommu mailing list
iommu at lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

#29 lore raw

RFC 1/2 iommu/dma: Restrict IOVAs to physical memory layout

Quoted 58 lines

-----Original Message-----
From: Robin Murphy [mailto:robin.murphy at arm.com]
Sent: Friday, July 01, 2016 12:40 PM
To: Stuart Yoder <[email protected]>
Cc: iommu at lists.linux-foundation.org; linux-arm-kernel at lists.infradead.org
Subject: Re: [RFC 1/2] iommu/dma: Restrict IOVAs to physical memory layout

Quoted 48 lines · Robin Murphy
Quoted 27 lines · Stuart Yoder
Quoted 16 lines

-----Original Message-----
From: Robin Murphy <[email protected]>
Date: Tue, Jun 28, 2016 at 11:18 AM
Subject: [RFC 1/2] iommu/dma: Restrict IOVAs to physical memory layout
To: iommu at lists.linux-foundation.org, linux-arm-kernel at lists.infradead.org

Certain peripherals may be bestowed with knowledge of the physical memory map of the system in which they live, and refuse to handle addresses that they do not think are memory, which causes issues when remapping to arbitrary IOVAs. Sidestep the issue by restricting IOVA domains to only allocate addresses within ranges which match the physical memory layout.

Quoted 6 lines

---

Posting this as an RFC because it's something I've been having to use on Juno for all the PCI IOMMU development - it's pretty horrible, but I can't easily think of a nicer solution...

Maybe I'm not getting the implications of this looking at the patch in isolation, but how will this impact systems that have devices limited to 32-bit addressing?

Quoted 5 lines

In our memory map we have physical memory regions at:
0x00_8000_0000
0x80_8000_0000

Will devices with a 32-bit DMA mask still get 32-bit IOVAs?

Assuming there's some free IOVA space between 0x80000000 and 0xffffffff, yes, otherwise it gets nothing ;) This has no effect on the allocation behaviour in general, it just makes sure that within that behaviour, we avoid allocating any address that doesn't look "real". The primary issue is with 64-bit DMA masks - since it's a top-down allocator, you typically end up with the poor device issuing its first DMA transaction to 0xfffffffffffff000 which on Juno a) gets silently eaten by the root complex because it doesn't match any window in the PCI-AXI translation table, or b) goes wrong anyway because it's beyond the input address range of the SMMU (and there's something not quite right WRT truncation/sign-extension which I've not looked into closely and am semi-deliberately also sweeping under the rug thanks to the simpler hardware issue...)

Quoted 6 lines
Quoted 1 line

As I say, it's hideous, but I can't see what else to do.

Urgh, thinking some more, this is OK on Juno and LS2085 only because there *is* some RAM below 4GB to begin with. On something like Seattle where it's all high up, 32-bit peripherals will be as screwed as if the IOMMU wasn't there :(

Can the "Restrict IOVAs to physical memory layout" be a quirk type property on the SMMU node for hardware that has this issue?

Then it is conditional and seems to only be needed by the Juno platform, for now.

Stuart

author maintainer
#30 lore raw

RFC 2/2 iommu/dma: Identity-map non-RAM regions

In reply to Robin Murphy

There is a fundamental assumption baked into many drivers and subsystems that resources outside of RAM (e.g. MSI controllers, target peripherals for DMA engines, etc.) can be accessed directly by physical address. Whilst work is ongoing to add the necessary APIs and abstractions to make everything more IOMMU-friendly, the time and effort involved is proving to be not insignificant, and in the meantime unconditionally enabling IOMMU translation for DMA leaves many things unworkably broken.

By swinging the big hammer of identity-mapping every potential I/O region, we can at least enable IOMMU remapping for regular DMA to/from RAM areas without regressing existing behaviour, and continue developing incrementally from there.

---

And this is even more horrible, but again, there are platforms that want 32-bit peripherals to be able to access all of (or any of) RAM, but at the same time won't appreciate currently-working MSIs and whatever else being borked.

 drivers/iommu/dma-iommu.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)
drivers/iommu/dma-iommu.c +25 -0
index 2385fab382d8..3b07a8424ac5 100644
@@ -92,6 +92,29 @@ static void iova_match_mem(struct iova_domain *iovad)
 		reserve_iova(iovad, base, blk);
 }
 
+static void domain_idmap_mmio(struct iommu_domain *domain)
+{
+	struct iova_domain *iovad = domain->iova_cookie;
+	struct rb_node *node;
+	unsigned long shift = iova_shift(iovad);
+
+	spin_lock(&iovad->iova_rbtree_lock);
+	for (node = rb_first(&iovad->rbroot); node; node = rb_next(node)) {
+		struct iova *iova = container_of(node, struct iova, node);
+		phys_addr_t phys = iova->pfn_lo << shift;
+		size_t size = (iova->pfn_hi - iova->pfn_lo) << shift;
+
+		size = min_t(phys_addr_t, size,
+				domain->geometry.aperture_end - phys + 1);
+		if (!size)
+			break;
+
+		iommu_map(domain, phys, phys, size,
+				IOMMU_READ | IOMMU_WRITE | IOMMU_MMIO);
+	}
+	spin_unlock(&iovad->iova_rbtree_lock);
+}
+
 /**
  * iommu_dma_init_domain - Initialise a DMA mapping domain
  * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie()
@@ -142,6 +165,8 @@ int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, u64 size
 	} else {
 		init_iova_domain(iovad, 1UL << order, base_pfn, end_pfn);
 		iova_match_mem(iovad);
+		if (domain->geometry.force_aperture)
+			domain_idmap_mmio(domain);
 	}
 	return 0;
 }
Signature · 1 line
2.8.1.dirty