Generic DT bindings for PCI IOMMUs and ARM SMMUv3
Subsystems (11)
Robin Murphy
maintainerv3 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
Robin Murphy
maintainerPATCH 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")
---
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
@@ -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
Robin Murphy
maintainerPATCH v3 2/9 iommu/of: Consolidate device creation workarounds
In reply to Robin MurphySo 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.
---
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
@@ -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
@@ -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
@@ -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
PATCH v3 2/9 iommu/of: Consolidate device creation workarounds
Quoted 11 lines · Robin Murphy
So far, all the users of the generic
of_xlateconfiguration 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]>Signed-off-by Robin Murphy <[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@@ -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@@ -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@@ -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
Marek Szyprowski
maintainerPATCH v3 2/9 iommu/of: Consolidate device creation workarounds
In reply to Robin MurphyHi Robin,
Quoted 6 lines · Robin Murphy
So far, all the users of the generic
of_xlateconfiguration 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]>Signed-off-by Robin Murphy <[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@@ -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@@ -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@@ -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
-1list_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
Robin Murphy
maintainerPATCH 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_xlateconfiguration 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]>Signed-off-by Robin Murphy <[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
@@ -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 ;
Marek Szyprowski
maintainerPATCH 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_xlateconfiguration 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]>Signed-off-by Robin Murphy <[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@@ -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
PATCH v3 2/9 iommu/of: Consolidate device creation workarounds
In reply to Robin MurphyQuoted 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
Robin Murphy
maintainerPATCH v3 3/9 Docs: dt: add PCI IOMMU map bindings
In reply to Robin MurphyFrom: 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
@@ -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
Robin Murphy
maintainerPATCH v3 4/9 of/irq: Break out msi-map lookup (again)
In reply to Robin MurphyThe 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
@@ -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
@@ -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
Robin Murphy
maintainerPATCH v3 5/9 iommu/of: Handle iommu-map property for PCI
In reply to Robin MurphyNow 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
@@ -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
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 theof_xlatemechanism and allow them IOMMU-backed DMA ops without the need for driver-specific handling.Signed-off-by Robin Murphy <[email protected]>---
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@@ -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
-6const 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
Robin Murphy
maintainerPATCH 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 theof_xlatemechanism and allow them IOMMU-backed DMA ops without the need for driver-specific handling.Signed-off-by Robin Murphy <[email protected]>---
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@@ -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
-6const 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_ridreturns 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
Robin Murphy
maintainerPATCH v3 6/9 iommu/of: Introduce iommu_fwspec
In reply to Robin MurphyIntroduce 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
@@ -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
@@ -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
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_iommusupport 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_opsbefore falling back todev->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.Suggested-by Will Deacon <[email protected]>Signed-off-by Robin Murphy <[email protected]>---
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
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@@ -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
Robin Murphy
maintainerPATCH 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_iommusupport 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_opsbefore falling back todev->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.Suggested-by Will Deacon <[email protected]>Signed-off-by Robin Murphy <[email protected]>---
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@@ -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@@ -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
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_iommusupport 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_opsbefore falling back todev->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.Suggested-by Will Deacon <[email protected]>Signed-off-by Robin Murphy <[email protected]>---
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@@ -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_nodesfor the moment as I don't think we'd reached a consensus on whetherfwnode_handleor 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@@ -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
Robin Murphy
maintainerPATCH v3 7/9 iommu/arm-smmu: Implement of_xlate() for SMMUv3
In reply to Robin MurphyNow 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
@@ -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
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_xlatewired 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.Signed-off-by Robin Murphy <[email protected]>---
[...]
Quoted 71 lines
static int
arm_smmu_add_device(struct device *dev){drivers/iommu/arm-smmu-v3.c +25
-37int 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
Robin Murphy
maintainerPATCH 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_xlatewired 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.Signed-off-by Robin Murphy <[email protected]>---
[...]
Quoted 76 lines
static int
arm_smmu_add_device(struct device *dev){drivers/iommu/arm-smmu-v3.c +30
-37int 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
-ERANGEabove)? 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
Robin Murphy
maintainerPATCH v3 8/9 iommu/arm-smmu: Support non-PCI devices with SMMUv3
In reply to Robin MurphyWith 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
@@ -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
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 theof_dma_configure()mechanism will work, so extend support to the platform and AMBA buses which do just that.Signed-off-by Robin Murphy <[email protected]>---
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@@ -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_DOMAINdrivers/iommu/arm-smmu-v3.c +2
-0@@ -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
Robin Murphy
maintainerPATCH 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 theof_dma_configure()mechanism will work, so extend support to the platform and AMBA buses which do just that.Signed-off-by Robin Murphy <[email protected]>---
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@@ -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_DOMAINdrivers/iommu/arm-smmu-v3.c +2
-0@@ -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
Robin Murphy
maintainerPATCH v3 9/9 iommu/arm-smmu: Set PRIVCFG in stage 1 STEs
In reply to Robin MurphyImplement 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
@@ -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
Robin Murphy
maintainerRFC 1/2 iommu/dma: Restrict IOVAs to physical memory layout
In reply to Robin MurphyCertain 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
@@ -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
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.orgCertain 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
Signed-off-by Robin Murphy <[email protected]>---
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
Robin Murphy
maintainerRFC 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.orgCertain 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
Signed-off-by Robin Murphy <[email protected]>---
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_0000Will 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
Robin Murphy
maintainerRFC 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.orgCertain 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
Signed-off-by Robin Murphy <[email protected]>---
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_0000Will devices with a 32-bit DMA mask still get 32-bit IOVAs?
Assuming there's some free IOVA space between
0x80000000and0xffffffff, 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 to0xfffffffffffff000which 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
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 layoutQuoted 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.orgCertain 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
Signed-off-by Robin Murphy <[email protected]>---
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_0000Will devices with a 32-bit DMA mask still get 32-bit IOVAs?
Assuming there's some free IOVA space between
0x80000000and0xffffffff, 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 to0xfffffffffffff000which 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
Robin Murphy
maintainerRFC 2/2 iommu/dma: Identity-map non-RAM regions
In reply to Robin MurphyThere 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
@@ -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