IOMMU Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH 0/2] iommu: Support reserved-memory regions
@ 2019-08-29 11:14 Thierry Reding
  2019-08-29 11:14 ` [PATCH 1/2] iommu: Implement of_iommu_get_resv_regions() Thierry Reding
  2019-08-29 11:14 ` [PATCH 2/2] iommu: dma: Use of_iommu_get_resv_regions() Thierry Reding
  0 siblings, 2 replies; 14+ messages in thread
From: Thierry Reding @ 2019-08-29 11:14 UTC (permalink / raw)
  To: Joerg Roedel
  Cc: devicetree, Frank Rowand, Robin Murphy, linux-kernel, iommu,
	Rob Herring, Will Deacon

From: Thierry Reding <treding@nvidia.com>

These two patches implement support for retrieving a list of reserved
regions for a device from its device tree node. These regions are
described by the reserved-memory bindings:

	Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt

These reserved memory regions will be used to establish 1:1 mappings.
One case where this is useful is when the Linux kernel wants to take
over the display controller configuration from a bootloader. In order
to ensure that the display controller can keep scanning out from the
framebuffer allocated by the bootloader without faulting after the
IOMMU has been enabled, a 1:1 mapping needs to be established.

Thierry

Thierry Reding (2):
  iommu: Implement of_iommu_get_resv_regions()
  iommu: dma: Use of_iommu_get_resv_regions()

 drivers/iommu/dma-iommu.c |  3 +++
 drivers/iommu/of_iommu.c  | 39 +++++++++++++++++++++++++++++++++++++++
 include/linux/of_iommu.h  |  8 ++++++++
 3 files changed, 50 insertions(+)

-- 
2.22.0

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

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 1/2] iommu: Implement of_iommu_get_resv_regions()
  2019-08-29 11:14 [PATCH 0/2] iommu: Support reserved-memory regions Thierry Reding
@ 2019-08-29 11:14 ` Thierry Reding
  2019-09-02 13:38   ` Rob Herring
  2019-09-02 13:54   ` Robin Murphy
  2019-08-29 11:14 ` [PATCH 2/2] iommu: dma: Use of_iommu_get_resv_regions() Thierry Reding
  1 sibling, 2 replies; 14+ messages in thread
From: Thierry Reding @ 2019-08-29 11:14 UTC (permalink / raw)
  To: Joerg Roedel
  Cc: devicetree, Frank Rowand, Robin Murphy, linux-kernel, iommu,
	Rob Herring, Will Deacon

From: Thierry Reding <treding@nvidia.com>

This is an implementation that IOMMU drivers can use to obtain reserved
memory regions from a device tree node. It uses the reserved-memory DT
bindings to find the regions associated with a given device. These
regions will be used to create 1:1 mappings in the IOMMU domain that
the devices will be attached to.

Cc: Rob Herring <robh+dt@kernel.org>
Cc: Frank Rowand <frowand.list@gmail.com>
Cc: devicetree@vger.kernel.org
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/iommu/of_iommu.c | 39 +++++++++++++++++++++++++++++++++++++++
 include/linux/of_iommu.h |  8 ++++++++
 2 files changed, 47 insertions(+)

diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 614a93aa5305..0d47f626b854 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -9,6 +9,7 @@
 #include <linux/iommu.h>
 #include <linux/limits.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/of_iommu.h>
 #include <linux/of_pci.h>
 #include <linux/slab.h>
@@ -225,3 +226,41 @@ const struct iommu_ops *of_iommu_configure(struct device *dev,
 
 	return ops;
 }
+
+/**
+ * of_iommu_get_resv_regions - reserved region driver helper for device tree
+ * @dev: device for which to get reserved regions
+ * @list: reserved region list
+ *
+ * IOMMU drivers can use this to implement their .get_resv_regions() callback
+ * for memory regions attached to a device tree node. See the reserved-memory
+ * device tree bindings on how to use these:
+ *
+ *   Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
+ */
+void of_iommu_get_resv_regions(struct device *dev, struct list_head *list)
+{
+	struct of_phandle_iterator it;
+	int err;
+
+	of_for_each_phandle(&it, err, dev->of_node, "memory-region", NULL, 0) {
+		struct iommu_resv_region *region;
+		struct resource res;
+
+		err = of_address_to_resource(it.node, 0, &res);
+		if (err < 0) {
+			dev_err(dev, "failed to parse memory region %pOF: %d\n",
+				it.node, err);
+			continue;
+		}
+
+		region = iommu_alloc_resv_region(res.start, resource_size(&res),
+						 IOMMU_READ | IOMMU_WRITE,
+						 IOMMU_RESV_DIRECT_RELAXABLE);
+		if (!region)
+			continue;
+
+		list_add_tail(&region->list, list);
+	}
+}
+EXPORT_SYMBOL(of_iommu_get_resv_regions);
diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h
index f3d40dd7bb66..fa16b26f55bc 100644
--- a/include/linux/of_iommu.h
+++ b/include/linux/of_iommu.h
@@ -15,6 +15,9 @@ extern int of_get_dma_window(struct device_node *dn, const char *prefix,
 extern const struct iommu_ops *of_iommu_configure(struct device *dev,
 					struct device_node *master_np);
 
+extern void of_iommu_get_resv_regions(struct device *dev,
+				      struct list_head *list);
+
 #else
 
 static inline int of_get_dma_window(struct device_node *dn, const char *prefix,
@@ -30,6 +33,11 @@ static inline const struct iommu_ops *of_iommu_configure(struct device *dev,
 	return NULL;
 }
 
+static inline void of_iommu_get_resv_regions(struct device *dev,
+					     struct list_head *list)
+{
+}
+
 #endif	/* CONFIG_OF_IOMMU */
 
 #endif /* __OF_IOMMU_H */
-- 
2.22.0

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

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 2/2] iommu: dma: Use of_iommu_get_resv_regions()
  2019-08-29 11:14 [PATCH 0/2] iommu: Support reserved-memory regions Thierry Reding
  2019-08-29 11:14 ` [PATCH 1/2] iommu: Implement of_iommu_get_resv_regions() Thierry Reding
@ 2019-08-29 11:14 ` Thierry Reding
  2019-09-02 14:22   ` Robin Murphy
  1 sibling, 1 reply; 14+ messages in thread
From: Thierry Reding @ 2019-08-29 11:14 UTC (permalink / raw)
  To: Joerg Roedel
  Cc: devicetree, Frank Rowand, Robin Murphy, linux-kernel, iommu,
	Rob Herring, Will Deacon

From: Thierry Reding <treding@nvidia.com>

For device tree nodes, use the standard of_iommu_get_resv_regions()
implementation to obtain the reserved memory regions associated with a
device.

Cc: Rob Herring <robh+dt@kernel.org>
Cc: Frank Rowand <frowand.list@gmail.com>
Cc: devicetree@vger.kernel.org
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/iommu/dma-iommu.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index de68b4a02aea..31d48e55ab55 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -19,6 +19,7 @@
 #include <linux/iova.h>
 #include <linux/irq.h>
 #include <linux/mm.h>
+#include <linux/of_iommu.h>
 #include <linux/pci.h>
 #include <linux/scatterlist.h>
 #include <linux/vmalloc.h>
@@ -164,6 +165,8 @@ void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list)
 	if (!is_of_node(dev_iommu_fwspec_get(dev)->iommu_fwnode))
 		iort_iommu_msi_get_resv_regions(dev, list);
 
+	if (dev->of_node)
+		of_iommu_get_resv_regions(dev, list);
 }
 EXPORT_SYMBOL(iommu_dma_get_resv_regions);
 
-- 
2.22.0

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

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/2] iommu: Implement of_iommu_get_resv_regions()
  2019-08-29 11:14 ` [PATCH 1/2] iommu: Implement of_iommu_get_resv_regions() Thierry Reding
@ 2019-09-02 13:38   ` Rob Herring
  2019-09-02 13:54   ` Robin Murphy
  1 sibling, 0 replies; 14+ messages in thread
From: Rob Herring @ 2019-09-02 13:38 UTC (permalink / raw)
  To: Thierry Reding; +Cc: , devicetree, linux-kernel, iommu, Robin Murphy

On Thu, 29 Aug 2019 13:14:06 +0200, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> This is an implementation that IOMMU drivers can use to obtain reserved
> memory regions from a device tree node. It uses the reserved-memory DT
> bindings to find the regions associated with a given device. These
> regions will be used to create 1:1 mappings in the IOMMU domain that
> the devices will be attached to.
> 
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Frank Rowand <frowand.list@gmail.com>
> Cc: devicetree@vger.kernel.org
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>  drivers/iommu/of_iommu.c | 39 +++++++++++++++++++++++++++++++++++++++
>  include/linux/of_iommu.h |  8 ++++++++
>  2 files changed, 47 insertions(+)
> 

Reviewed-by: Rob Herring <robh@kernel.org>

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

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/2] iommu: Implement of_iommu_get_resv_regions()
  2019-08-29 11:14 ` [PATCH 1/2] iommu: Implement of_iommu_get_resv_regions() Thierry Reding
  2019-09-02 13:38   ` Rob Herring
@ 2019-09-02 13:54   ` Robin Murphy
  2019-09-02 15:00     ` Thierry Reding
  1 sibling, 1 reply; 14+ messages in thread
From: Robin Murphy @ 2019-09-02 13:54 UTC (permalink / raw)
  To: Thierry Reding, Joerg Roedel
  Cc: devicetree, Frank Rowand, linux-kernel, iommu, Rob Herring, Will Deacon

On 29/08/2019 12:14, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> This is an implementation that IOMMU drivers can use to obtain reserved
> memory regions from a device tree node. It uses the reserved-memory DT
> bindings to find the regions associated with a given device. These
> regions will be used to create 1:1 mappings in the IOMMU domain that
> the devices will be attached to.
> 
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Frank Rowand <frowand.list@gmail.com>
> Cc: devicetree@vger.kernel.org
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>   drivers/iommu/of_iommu.c | 39 +++++++++++++++++++++++++++++++++++++++
>   include/linux/of_iommu.h |  8 ++++++++
>   2 files changed, 47 insertions(+)
> 
> diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
> index 614a93aa5305..0d47f626b854 100644
> --- a/drivers/iommu/of_iommu.c
> +++ b/drivers/iommu/of_iommu.c
> @@ -9,6 +9,7 @@
>   #include <linux/iommu.h>
>   #include <linux/limits.h>
>   #include <linux/of.h>
> +#include <linux/of_address.h>
>   #include <linux/of_iommu.h>
>   #include <linux/of_pci.h>
>   #include <linux/slab.h>
> @@ -225,3 +226,41 @@ const struct iommu_ops *of_iommu_configure(struct device *dev,
>   
>   	return ops;
>   }
> +
> +/**
> + * of_iommu_get_resv_regions - reserved region driver helper for device tree
> + * @dev: device for which to get reserved regions
> + * @list: reserved region list
> + *
> + * IOMMU drivers can use this to implement their .get_resv_regions() callback
> + * for memory regions attached to a device tree node. See the reserved-memory
> + * device tree bindings on how to use these:
> + *
> + *   Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
> + */
> +void of_iommu_get_resv_regions(struct device *dev, struct list_head *list)
> +{
> +	struct of_phandle_iterator it;
> +	int err;
> +
> +	of_for_each_phandle(&it, err, dev->of_node, "memory-region", NULL, 0) {
> +		struct iommu_resv_region *region;
> +		struct resource res;
> +
> +		err = of_address_to_resource(it.node, 0, &res);
> +		if (err < 0) {
> +			dev_err(dev, "failed to parse memory region %pOF: %d\n",
> +				it.node, err);
> +			continue;
> +		}

What if the device node has memory regions for other purposes, like 
private CMA carveouts? We wouldn't want to force mappings of those (and 
in the very worst case doing so could even render them unusable).

Robin.

> +
> +		region = iommu_alloc_resv_region(res.start, resource_size(&res),
> +						 IOMMU_READ | IOMMU_WRITE,
> +						 IOMMU_RESV_DIRECT_RELAXABLE);
> +		if (!region)
> +			continue;
> +
> +		list_add_tail(&region->list, list);
> +	}
> +}
> +EXPORT_SYMBOL(of_iommu_get_resv_regions);
> diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h
> index f3d40dd7bb66..fa16b26f55bc 100644
> --- a/include/linux/of_iommu.h
> +++ b/include/linux/of_iommu.h
> @@ -15,6 +15,9 @@ extern int of_get_dma_window(struct device_node *dn, const char *prefix,
>   extern const struct iommu_ops *of_iommu_configure(struct device *dev,
>   					struct device_node *master_np);
>   
> +extern void of_iommu_get_resv_regions(struct device *dev,
> +				      struct list_head *list);
> +
>   #else
>   
>   static inline int of_get_dma_window(struct device_node *dn, const char *prefix,
> @@ -30,6 +33,11 @@ static inline const struct iommu_ops *of_iommu_configure(struct device *dev,
>   	return NULL;
>   }
>   
> +static inline void of_iommu_get_resv_regions(struct device *dev,
> +					     struct list_head *list)
> +{
> +}
> +
>   #endif	/* CONFIG_OF_IOMMU */
>   
>   #endif /* __OF_IOMMU_H */
> 
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 2/2] iommu: dma: Use of_iommu_get_resv_regions()
  2019-08-29 11:14 ` [PATCH 2/2] iommu: dma: Use of_iommu_get_resv_regions() Thierry Reding
@ 2019-09-02 14:22   ` Robin Murphy
  2019-09-02 14:52     ` Thierry Reding
  0 siblings, 1 reply; 14+ messages in thread
From: Robin Murphy @ 2019-09-02 14:22 UTC (permalink / raw)
  To: Thierry Reding, Joerg Roedel
  Cc: devicetree, Frank Rowand, linux-kernel, iommu, Rob Herring, Will Deacon

On 29/08/2019 12:14, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> For device tree nodes, use the standard of_iommu_get_resv_regions()
> implementation to obtain the reserved memory regions associated with a
> device.

This covers the window between iommu_probe_device() setting up a default 
domain and the device's driver finally probing and taking control, but 
iommu_probe_device() represents the point that the IOMMU driver first 
knows about this device - there's still a window from whenever the IOMMU 
driver itself probed up to here where the "unidentified" traffic may 
have already been disrupted. Some IOMMU drivers have no option but to 
make the necessary configuration during their own probe routine, at 
which point a struct device for the display/etc. endpoint may not even 
exist yet.

Robin.

> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: Frank Rowand <frowand.list@gmail.com>
> Cc: devicetree@vger.kernel.org
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>   drivers/iommu/dma-iommu.c | 3 +++
>   1 file changed, 3 insertions(+)
> 
> diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
> index de68b4a02aea..31d48e55ab55 100644
> --- a/drivers/iommu/dma-iommu.c
> +++ b/drivers/iommu/dma-iommu.c
> @@ -19,6 +19,7 @@
>   #include <linux/iova.h>
>   #include <linux/irq.h>
>   #include <linux/mm.h>
> +#include <linux/of_iommu.h>
>   #include <linux/pci.h>
>   #include <linux/scatterlist.h>
>   #include <linux/vmalloc.h>
> @@ -164,6 +165,8 @@ void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list)
>   	if (!is_of_node(dev_iommu_fwspec_get(dev)->iommu_fwnode))
>   		iort_iommu_msi_get_resv_regions(dev, list);
>   
> +	if (dev->of_node)
> +		of_iommu_get_resv_regions(dev, list);
>   }
>   EXPORT_SYMBOL(iommu_dma_get_resv_regions);
>   
> 
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 2/2] iommu: dma: Use of_iommu_get_resv_regions()
  2019-09-02 14:22   ` Robin Murphy
@ 2019-09-02 14:52     ` Thierry Reding
  2019-09-17 17:59       ` Will Deacon
  0 siblings, 1 reply; 14+ messages in thread
From: Thierry Reding @ 2019-09-02 14:52 UTC (permalink / raw)
  To: Robin Murphy
  Cc: devicetree, Frank Rowand, linux-kernel, iommu, Rob Herring, Will Deacon

[-- Attachment #1.1: Type: text/plain, Size: 2661 bytes --]

On Mon, Sep 02, 2019 at 03:22:35PM +0100, Robin Murphy wrote:
> On 29/08/2019 12:14, Thierry Reding wrote:
> > From: Thierry Reding <treding@nvidia.com>
> > 
> > For device tree nodes, use the standard of_iommu_get_resv_regions()
> > implementation to obtain the reserved memory regions associated with a
> > device.
> 
> This covers the window between iommu_probe_device() setting up a default
> domain and the device's driver finally probing and taking control, but
> iommu_probe_device() represents the point that the IOMMU driver first knows
> about this device - there's still a window from whenever the IOMMU driver
> itself probed up to here where the "unidentified" traffic may have already
> been disrupted. Some IOMMU drivers have no option but to make the necessary
> configuration during their own probe routine, at which point a struct device
> for the display/etc. endpoint may not even exist yet.

Yeah, I think I'm actually running into this issue with the ARM SMMU
driver. The above works fine with the Tegra SMMU driver, though, because
it doesn't touch the SMMU configuration until a device is attached to a
domain.

For anything earlier than iommu_probe_device(), I don't see a way of
doing this generically. I've been working on a prototype to make these
reserved memory regions early on for ARM SMMU but I've been failing so
far. I think it would possibly work if we just switched the default for
stream IDs to be "bypass" if they have any devices that have reserved
memory regions, but again, this isn't quite working (yet).

Thierry

> > Cc: Rob Herring <robh+dt@kernel.org>
> > Cc: Frank Rowand <frowand.list@gmail.com>
> > Cc: devicetree@vger.kernel.org
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > ---
> >   drivers/iommu/dma-iommu.c | 3 +++
> >   1 file changed, 3 insertions(+)
> > 
> > diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
> > index de68b4a02aea..31d48e55ab55 100644
> > --- a/drivers/iommu/dma-iommu.c
> > +++ b/drivers/iommu/dma-iommu.c
> > @@ -19,6 +19,7 @@
> >   #include <linux/iova.h>
> >   #include <linux/irq.h>
> >   #include <linux/mm.h>
> > +#include <linux/of_iommu.h>
> >   #include <linux/pci.h>
> >   #include <linux/scatterlist.h>
> >   #include <linux/vmalloc.h>
> > @@ -164,6 +165,8 @@ void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list)
> >   	if (!is_of_node(dev_iommu_fwspec_get(dev)->iommu_fwnode))
> >   		iort_iommu_msi_get_resv_regions(dev, list);
> > +	if (dev->of_node)
> > +		of_iommu_get_resv_regions(dev, list);
> >   }
> >   EXPORT_SYMBOL(iommu_dma_get_resv_regions);
> > 

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 156 bytes --]

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

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/2] iommu: Implement of_iommu_get_resv_regions()
  2019-09-02 13:54   ` Robin Murphy
@ 2019-09-02 15:00     ` Thierry Reding
  2019-09-09 14:04       ` Will Deacon
  0 siblings, 1 reply; 14+ messages in thread
From: Thierry Reding @ 2019-09-02 15:00 UTC (permalink / raw)
  To: Robin Murphy
  Cc: devicetree, Frank Rowand, linux-kernel, iommu, Rob Herring, Will Deacon

[-- Attachment #1.1: Type: text/plain, Size: 3374 bytes --]

On Mon, Sep 02, 2019 at 02:54:23PM +0100, Robin Murphy wrote:
> On 29/08/2019 12:14, Thierry Reding wrote:
> > From: Thierry Reding <treding@nvidia.com>
> > 
> > This is an implementation that IOMMU drivers can use to obtain reserved
> > memory regions from a device tree node. It uses the reserved-memory DT
> > bindings to find the regions associated with a given device. These
> > regions will be used to create 1:1 mappings in the IOMMU domain that
> > the devices will be attached to.
> > 
> > Cc: Rob Herring <robh+dt@kernel.org>
> > Cc: Frank Rowand <frowand.list@gmail.com>
> > Cc: devicetree@vger.kernel.org
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > ---
> >   drivers/iommu/of_iommu.c | 39 +++++++++++++++++++++++++++++++++++++++
> >   include/linux/of_iommu.h |  8 ++++++++
> >   2 files changed, 47 insertions(+)
> > 
> > diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
> > index 614a93aa5305..0d47f626b854 100644
> > --- a/drivers/iommu/of_iommu.c
> > +++ b/drivers/iommu/of_iommu.c
> > @@ -9,6 +9,7 @@
> >   #include <linux/iommu.h>
> >   #include <linux/limits.h>
> >   #include <linux/of.h>
> > +#include <linux/of_address.h>
> >   #include <linux/of_iommu.h>
> >   #include <linux/of_pci.h>
> >   #include <linux/slab.h>
> > @@ -225,3 +226,41 @@ const struct iommu_ops *of_iommu_configure(struct device *dev,
> >   	return ops;
> >   }
> > +
> > +/**
> > + * of_iommu_get_resv_regions - reserved region driver helper for device tree
> > + * @dev: device for which to get reserved regions
> > + * @list: reserved region list
> > + *
> > + * IOMMU drivers can use this to implement their .get_resv_regions() callback
> > + * for memory regions attached to a device tree node. See the reserved-memory
> > + * device tree bindings on how to use these:
> > + *
> > + *   Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
> > + */
> > +void of_iommu_get_resv_regions(struct device *dev, struct list_head *list)
> > +{
> > +	struct of_phandle_iterator it;
> > +	int err;
> > +
> > +	of_for_each_phandle(&it, err, dev->of_node, "memory-region", NULL, 0) {
> > +		struct iommu_resv_region *region;
> > +		struct resource res;
> > +
> > +		err = of_address_to_resource(it.node, 0, &res);
> > +		if (err < 0) {
> > +			dev_err(dev, "failed to parse memory region %pOF: %d\n",
> > +				it.node, err);
> > +			continue;
> > +		}
> 
> What if the device node has memory regions for other purposes, like private
> CMA carveouts? We wouldn't want to force mappings of those (and in the very
> worst case doing so could even render them unusable).

I suppose we could come up with additional properties to mark such
memory regions and skip them here.

One other alternative might be to make sure that the driver claims
the memory regions that have been mapped and then tells the IOMMU to
undo the mappings for them. That way the driver could set up the new
mappings, reprogram the hardware and then have the old mappings torn
down. I'm not sure that could always be done in a race-free way. For
example, what if the new mappings need to be in a region, such as a
private CMA carveout, that's already mapped. Can we temporarily map
one physical address to two DMA addresses?

The details here probably depend on the IOMMU hardware.

Thierry

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 156 bytes --]

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

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/2] iommu: Implement of_iommu_get_resv_regions()
  2019-09-02 15:00     ` Thierry Reding
@ 2019-09-09 14:04       ` Will Deacon
  0 siblings, 0 replies; 14+ messages in thread
From: Will Deacon @ 2019-09-09 14:04 UTC (permalink / raw)
  To: Thierry Reding
  Cc: devicetree, Robin Murphy, linux-kernel, iommu, Rob Herring, Frank Rowand

On Mon, Sep 02, 2019 at 05:00:56PM +0200, Thierry Reding wrote:
> On Mon, Sep 02, 2019 at 02:54:23PM +0100, Robin Murphy wrote:
> > On 29/08/2019 12:14, Thierry Reding wrote:
> > > From: Thierry Reding <treding@nvidia.com>
> > > 
> > > This is an implementation that IOMMU drivers can use to obtain reserved
> > > memory regions from a device tree node. It uses the reserved-memory DT
> > > bindings to find the regions associated with a given device. These
> > > regions will be used to create 1:1 mappings in the IOMMU domain that
> > > the devices will be attached to.
> > > 
> > > Cc: Rob Herring <robh+dt@kernel.org>
> > > Cc: Frank Rowand <frowand.list@gmail.com>
> > > Cc: devicetree@vger.kernel.org
> > > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > > ---
> > >   drivers/iommu/of_iommu.c | 39 +++++++++++++++++++++++++++++++++++++++
> > >   include/linux/of_iommu.h |  8 ++++++++
> > >   2 files changed, 47 insertions(+)
> > > 
> > > diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
> > > index 614a93aa5305..0d47f626b854 100644
> > > --- a/drivers/iommu/of_iommu.c
> > > +++ b/drivers/iommu/of_iommu.c
> > > @@ -9,6 +9,7 @@
> > >   #include <linux/iommu.h>
> > >   #include <linux/limits.h>
> > >   #include <linux/of.h>
> > > +#include <linux/of_address.h>
> > >   #include <linux/of_iommu.h>
> > >   #include <linux/of_pci.h>
> > >   #include <linux/slab.h>
> > > @@ -225,3 +226,41 @@ const struct iommu_ops *of_iommu_configure(struct device *dev,
> > >   	return ops;
> > >   }
> > > +
> > > +/**
> > > + * of_iommu_get_resv_regions - reserved region driver helper for device tree
> > > + * @dev: device for which to get reserved regions
> > > + * @list: reserved region list
> > > + *
> > > + * IOMMU drivers can use this to implement their .get_resv_regions() callback
> > > + * for memory regions attached to a device tree node. See the reserved-memory
> > > + * device tree bindings on how to use these:
> > > + *
> > > + *   Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
> > > + */
> > > +void of_iommu_get_resv_regions(struct device *dev, struct list_head *list)
> > > +{
> > > +	struct of_phandle_iterator it;
> > > +	int err;
> > > +
> > > +	of_for_each_phandle(&it, err, dev->of_node, "memory-region", NULL, 0) {
> > > +		struct iommu_resv_region *region;
> > > +		struct resource res;
> > > +
> > > +		err = of_address_to_resource(it.node, 0, &res);
> > > +		if (err < 0) {
> > > +			dev_err(dev, "failed to parse memory region %pOF: %d\n",
> > > +				it.node, err);
> > > +			continue;
> > > +		}
> > 
> > What if the device node has memory regions for other purposes, like private
> > CMA carveouts? We wouldn't want to force mappings of those (and in the very
> > worst case doing so could even render them unusable).
> 
> I suppose we could come up with additional properties to mark such
> memory regions and skip them here.

I think we need /something/ like that, both so that we can identify these
memory regions as requiring an identity mapping in the SMMU but also so
that we can place additional requirements on them, such as being 64k-aligned
and mandating properties of the mapping, such as cacheability based on the
device coherency.

I defer to the devicetree folks as to whether this should be an additional
property, or a phandle or whatever.

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

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 2/2] iommu: dma: Use of_iommu_get_resv_regions()
  2019-09-02 14:52     ` Thierry Reding
@ 2019-09-17 17:59       ` Will Deacon
  2019-11-27 12:16         ` Thierry Reding
  0 siblings, 1 reply; 14+ messages in thread
From: Will Deacon @ 2019-09-17 17:59 UTC (permalink / raw)
  To: Thierry Reding
  Cc: devicetree, Robin Murphy, linux-kernel, iommu, Rob Herring, Frank Rowand

On Mon, Sep 02, 2019 at 04:52:45PM +0200, Thierry Reding wrote:
> On Mon, Sep 02, 2019 at 03:22:35PM +0100, Robin Murphy wrote:
> > On 29/08/2019 12:14, Thierry Reding wrote:
> > > From: Thierry Reding <treding@nvidia.com>
> > > 
> > > For device tree nodes, use the standard of_iommu_get_resv_regions()
> > > implementation to obtain the reserved memory regions associated with a
> > > device.
> > 
> > This covers the window between iommu_probe_device() setting up a default
> > domain and the device's driver finally probing and taking control, but
> > iommu_probe_device() represents the point that the IOMMU driver first knows
> > about this device - there's still a window from whenever the IOMMU driver
> > itself probed up to here where the "unidentified" traffic may have already
> > been disrupted. Some IOMMU drivers have no option but to make the necessary
> > configuration during their own probe routine, at which point a struct device
> > for the display/etc. endpoint may not even exist yet.
> 
> Yeah, I think I'm actually running into this issue with the ARM SMMU
> driver. The above works fine with the Tegra SMMU driver, though, because
> it doesn't touch the SMMU configuration until a device is attached to a
> domain.
> 
> For anything earlier than iommu_probe_device(), I don't see a way of
> doing this generically. I've been working on a prototype to make these
> reserved memory regions early on for ARM SMMU but I've been failing so
> far. I think it would possibly work if we just switched the default for
> stream IDs to be "bypass" if they have any devices that have reserved
> memory regions, but again, this isn't quite working (yet).

I think we should avoid the use of "bypass" outside of the IOMMU probe()
routine if at all possible, since it leaves the thing wide open if we don't
subsequently probe the master.

Why can't we initialise a page-table early for StreamIDs with these
reserved regions, and then join that up later on if we see a device with
one of those StreamIDs attaching to a DMA domain? I suppose the nasty
case would be attaching to a domain that already has other masters
attached to it. Can we forbid that somehow for these devices? Otherwise,
I think we'd have to transiently switch to bypass whilst switching page
table.

What problems did you run into trying to implement this?

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

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 2/2] iommu: dma: Use of_iommu_get_resv_regions()
  2019-09-17 17:59       ` Will Deacon
@ 2019-11-27 12:16         ` Thierry Reding
  2019-11-27 14:16           ` Thierry Reding
  0 siblings, 1 reply; 14+ messages in thread
From: Thierry Reding @ 2019-11-27 12:16 UTC (permalink / raw)
  To: Will Deacon
  Cc: devicetree, Robin Murphy, linux-kernel, iommu, Rob Herring, Frank Rowand

[-- Attachment #1.1: Type: text/plain, Size: 18103 bytes --]

On Tue, Sep 17, 2019 at 06:59:50PM +0100, Will Deacon wrote:
> On Mon, Sep 02, 2019 at 04:52:45PM +0200, Thierry Reding wrote:
> > On Mon, Sep 02, 2019 at 03:22:35PM +0100, Robin Murphy wrote:
> > > On 29/08/2019 12:14, Thierry Reding wrote:
> > > > From: Thierry Reding <treding@nvidia.com>
> > > > 
> > > > For device tree nodes, use the standard of_iommu_get_resv_regions()
> > > > implementation to obtain the reserved memory regions associated with a
> > > > device.
> > > 
> > > This covers the window between iommu_probe_device() setting up a default
> > > domain and the device's driver finally probing and taking control, but
> > > iommu_probe_device() represents the point that the IOMMU driver first knows
> > > about this device - there's still a window from whenever the IOMMU driver
> > > itself probed up to here where the "unidentified" traffic may have already
> > > been disrupted. Some IOMMU drivers have no option but to make the necessary
> > > configuration during their own probe routine, at which point a struct device
> > > for the display/etc. endpoint may not even exist yet.
> > 
> > Yeah, I think I'm actually running into this issue with the ARM SMMU
> > driver. The above works fine with the Tegra SMMU driver, though, because
> > it doesn't touch the SMMU configuration until a device is attached to a
> > domain.
> > 
> > For anything earlier than iommu_probe_device(), I don't see a way of
> > doing this generically. I've been working on a prototype to make these
> > reserved memory regions early on for ARM SMMU but I've been failing so
> > far. I think it would possibly work if we just switched the default for
> > stream IDs to be "bypass" if they have any devices that have reserved
> > memory regions, but again, this isn't quite working (yet).
> 
> I think we should avoid the use of "bypass" outside of the IOMMU probe()
> routine if at all possible, since it leaves the thing wide open if we don't
> subsequently probe the master.
> 
> Why can't we initialise a page-table early for StreamIDs with these
> reserved regions, and then join that up later on if we see a device with
> one of those StreamIDs attaching to a DMA domain? I suppose the nasty
> case would be attaching to a domain that already has other masters
> attached to it. Can we forbid that somehow for these devices? Otherwise,
> I think we'd have to transiently switch to bypass whilst switching page
> table.
> 
> What problems did you run into trying to implement this?

I picked this up again and was trying to make this work with your
suggestion. Below is a rough draft and it seems to be working to some
degree. Here's an extract of the log when I run this on Jetson TX2:

	[    3.755328] arm-smmu 12000000.iommu: probing hardware configuration...
	[    3.762187] arm-smmu 12000000.iommu: SMMUv2 with:
	[    3.767137] arm-smmu 12000000.iommu:         stage 1 translation
	[    3.772806] arm-smmu 12000000.iommu:         stage 2 translation
	[    3.778471] arm-smmu 12000000.iommu:         nested translation
	[    3.784050] arm-smmu 12000000.iommu:         stream matching with 128 register groups
	[    3.791651] arm-smmu 12000000.iommu:         64 context banks (0 stage-2 only)
	[    3.798603] arm-smmu 12000000.iommu:         Supported page sizes: 0x61311000
	[    3.805460] arm-smmu 12000000.iommu:         Stage-1: 48-bit VA -> 48-bit IPA
	[    3.812310] arm-smmu 12000000.iommu:         Stage-2: 48-bit IPA -> 48-bit PA
	[    3.819159] arm-smmu 12000000.iommu: > arm_smmu_setup_identity(smmu=ffff0001eabcec80)
	[    3.827373] arm-smmu 12000000.iommu:   identity domain: ffff0001eaf8cae8 (ops: 0x0)
	[    3.835614] arm-smmu 12000000.iommu:   np: /ethernet@2490000
	[    3.841635] arm-smmu 12000000.iommu:   np: /sdhci@3400000
	[    3.847315] arm-smmu 12000000.iommu:   np: /sdhci@3420000
	[    3.852990] arm-smmu 12000000.iommu:   np: /sdhci@3440000
	[    3.858683] arm-smmu 12000000.iommu:   np: /sdhci@3460000
	[    3.864370] arm-smmu 12000000.iommu:   np: /hda@3510000
	[    3.869897] arm-smmu 12000000.iommu:   np: /usb@3530000
	[    3.875421] arm-smmu 12000000.iommu:   np: /pcie@10003000
	[    3.881109] arm-smmu 12000000.iommu:   np: /host1x@13e00000
	[    3.887012] arm-smmu 12000000.iommu:   np: /host1x@13e00000/display-hub@15200000/display@15200000
	[    3.896344] arm-smmu 12000000.iommu:     region: /reserved-memory/framebuffer@9607c000
	[    3.904707] arm-smmu 12000000.iommu:       [mem 0x9607c000-0x9687bfff]
	[    3.915719] arm-smmu 12000000.iommu:     /iommu@12000000: 1 arguments
	[    3.922487] arm-smmu 12000000.iommu:       0: 00000009
	[    3.927888] arm-smmu 12000000.iommu:       SID: 0009 MASK: 7f80
	[    3.934132] arm-smmu 12000000.iommu:       found index: 0
	[    3.939840] arm-smmu 12000000.iommu:   np: /host1x@13e00000/display-hub@15200000/display@15210000
	[    3.949183] arm-smmu 12000000.iommu:   np: /host1x@13e00000/display-hub@15200000/display@15220000
	[    3.958499] arm-smmu 12000000.iommu:   np: /host1x@13e00000/vic@15340000
	[    3.965557] arm-smmu 12000000.iommu:   np: /gpu@17000000
	[    3.971145] arm-smmu 12000000.iommu:   np: /bpmp
	[    3.976084] arm-smmu 12000000.iommu: < arm_smmu_setup_identity()
	[    3.982613] arm-smmu 12000000.iommu: > arm_smmu_write_sme(smmu=ffff0001eabcec80, idx=0)
	[    3.991072] arm-smmu 12000000.iommu:   ARM_SMMU_GR0_S2CR(0) < 00020000
	[    3.997922] arm-smmu 12000000.iommu:   ARM_SMMU_GR0_SMR(0) < ff800009
	[    4.004677] arm-smmu 12000000.iommu: < arm_smmu_write_sme()
	[    4.010528] arm-smmu 12000000.iommu: > arm_smmu_write_sme(smmu=ffff0001eabcec80, idx=1)
	[    4.018919] arm-smmu 12000000.iommu:   ARM_SMMU_GR0_S2CR(1) < 00020000
	[    4.025773] arm-smmu 12000000.iommu:   ARM_SMMU_GR0_SMR(1) < 00000000
	[    4.032543] arm-smmu 12000000.iommu: < arm_smmu_write_sme()
	...

There's a bunch of these, but idx=0 is the only one that's actually
populated because it corresponds to the display controller. However,
shortly after this I see a bunch of these:

	...
	[    7.588908] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x809; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
	[    7.589907] arm-smmu: > arm_smmu_of_xlate(dev=ffff0001eaecf010, args=ffff80001024bae8)
	[    7.603599] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00000809, GFSYNR2 0x00000000
	[    7.604218] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x1409; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
	[    7.611956] arm-smmu: < arm_smmu_of_xlate() = 0
	[    7.622636] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00001409, GFSYNR2 0x00000000
	[    7.622658] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x1809; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
	[    7.622662] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00001809, GFSYNR2 0x00000000
	[    7.622676] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x409; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
	[    7.637739] arm-smmu 12000000.iommu:   ARM_SMMU_GR0_S2CR(1) < 00000001
	[    7.642199] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00000409, GFSYNR2 0x00000000
	[    7.642216] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x9; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
	[    7.642221] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00000009, GFSYNR2 0x00000000
	[    7.642237] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x1c09; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
	[    7.652992] tegra-host1x 13e00000.host1x: Adding to iommu group 0
	[    7.667720] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00001c09, GFSYNR2 0x00000000
	[    7.667732] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x9; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
	[    7.667736] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00000009, GFSYNR2 0x00000000
	[    7.667748] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x1809; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
	[    7.667752] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00001809, GFSYNR2 0x00000000
	[    7.667765] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x9; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
	[    7.678511] arm-smmu 12000000.iommu: > arm_smmu_write_sme(smmu=ffff0001eabcec80, idx=1)
	[    7.693158] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00000009, GFSYNR2 0x00000000
	[    7.693170] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x1009; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
	[    7.693174] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00001009, GFSYNR2 0x00000000

Note that stream ID 0x9 is TEGRA186_SID_NVDISPLAY, which is associated
with the display controllers. One of these display controllers is live
because it was turned on by the bootloader to show a splash screen.

What I don't really understand is why it thinks that that stream ID is
unknown. One possibility I see is that perhaps the S2CR(0) and/or SMR(0)
registers might have gotten overwritten, but I don't see where that may
happen.

The errors stop eventually when the display controller is hooked up
properly via the DMA API, but the whole purpose here is obviously to get
to that point much earlier.

Any ideas what I might be doing wrong? Any comments on the general
approach?

Thierry

--- >8 ---
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 9b2f920ff2f8..13c7282560e6 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -916,6 +916,7 @@ static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
 
 	if (!(smmu->features & ARM_SMMU_FEAT_EXIDS) && smr->valid)
 		reg |= SMR_VALID;
+	dev_info(smmu->dev, "  ARM_SMMU_GR0_SMR(%u) < %08x\n", idx, reg);
 	arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(idx), reg);
 }
 
@@ -929,14 +930,17 @@ static void arm_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx)
 	if (smmu->features & ARM_SMMU_FEAT_EXIDS && smmu->smrs &&
 	    smmu->smrs[idx].valid)
 		reg |= S2CR_EXIDVALID;
+	dev_info(smmu->dev, "  ARM_SMMU_GR0_S2CR(%u) < %08x\n", idx, reg);
 	arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_S2CR(idx), reg);
 }
 
 static void arm_smmu_write_sme(struct arm_smmu_device *smmu, int idx)
 {
+	dev_info(smmu->dev, "> %s(smmu=%px, idx=%d)\n", __func__, smmu, idx);
 	arm_smmu_write_s2cr(smmu, idx);
 	if (smmu->smrs)
 		arm_smmu_write_smr(smmu, idx);
+	dev_info(smmu->dev, "< %s()\n", __func__);
 }
 
 /*
@@ -1010,6 +1014,8 @@ static int arm_smmu_find_sme(struct arm_smmu_device *smmu, u16 id, u16 mask)
 
 static bool arm_smmu_free_sme(struct arm_smmu_device *smmu, int idx)
 {
+	pr_info("> %s(smmu=%px, idx=%d)\n", __func__, smmu, idx);
+
 	if (--smmu->s2crs[idx].count)
 		return false;
 
@@ -1017,6 +1023,7 @@ static bool arm_smmu_free_sme(struct arm_smmu_device *smmu, int idx)
 	if (smmu->smrs)
 		smmu->smrs[idx].valid = false;
 
+	pr_info("< %s()\n", __func__);
 	return true;
 }
 
@@ -1545,19 +1552,34 @@ static int arm_smmu_domain_set_attr(struct iommu_domain *domain,
 	return ret;
 }
 
-static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
+static u32 arm_smmu_of_parse(struct device_node *np, const u32 *args,
+			     unsigned int count)
 {
-	u32 mask, fwid = 0;
+	u32 fwid = 0, mask;
 
-	if (args->args_count > 0)
-		fwid |= FIELD_PREP(SMR_ID, args->args[0]);
+	if (count > 0)
+		fwid |= FIELD_PREP(SMR_ID, args[0]);
 
-	if (args->args_count > 1)
-		fwid |= FIELD_PREP(SMR_MASK, args->args[1]);
-	else if (!of_property_read_u32(args->np, "stream-match-mask", &mask))
+	if (count > 1)
+		fwid |= FIELD_PREP(SMR_MASK, args[1]);
+	else if (!of_property_read_u32(np, "stream-match-mask", &mask))
 		fwid |= FIELD_PREP(SMR_MASK, mask);
 
-	return iommu_fwspec_add_ids(dev, &fwid, 1);
+	return fwid;
+}
+
+static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
+{
+	u32 fwid;
+	int ret;
+
+	pr_info("> %s(dev=%px, args=%px)\n", __func__, dev, args);
+
+	fwid = arm_smmu_of_parse(args->np, args->args, args->args_count);
+	ret = iommu_fwspec_add_ids(dev, &fwid, 1);
+
+	pr_info("< %s() = %d\n", __func__, ret);
+	return ret;
 }
 
 static void arm_smmu_get_resv_regions(struct device *dev,
@@ -1877,6 +1899,138 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 	return 0;
 }
 
+static int arm_smmu_setup_identity(struct arm_smmu_device *smmu)
+{
+	struct arm_smmu_domain *identity;
+	struct device *dev = smmu->dev;
+	unsigned long page_size;
+	struct device_node *np;
+	int ret;
+
+	dev_info(dev, "> %s(smmu=%px)\n", __func__, smmu);
+
+	/* create early identity mapping */
+	smmu->identity = arm_smmu_domain_alloc(IOMMU_DOMAIN_UNMANAGED);
+	if (!smmu->identity) {
+		dev_err(dev, "failed to create identity domain\n");
+		return -ENOMEM;
+	}
+
+	dev_info(dev, "  identity domain: %px (ops: %ps)\n", smmu->identity, smmu->identity->ops);
+	smmu->identity->type = IOMMU_DOMAIN_UNMANAGED;
+	smmu->identity->ops = &arm_smmu_ops;
+
+	ret = arm_smmu_init_domain_context(smmu->identity, smmu);
+	if (ret < 0) {
+		dev_err(dev, "failed to initialize identity domain: %d\n", ret);
+		return ret;
+	}
+
+	page_size = 1UL << __ffs(smmu->identity->pgsize_bitmap);
+	identity = to_smmu_domain(smmu->identity);
+
+	for_each_node_with_property(np, "iommus") {
+		struct arm_smmu_smr *smrs = smmu->smrs;
+		struct of_phandle_iterator it;
+		struct of_phandle_args args;
+		unsigned int index = 0;
+		bool mapped = false;
+
+		dev_info(dev, "  np: %pOF\n", np);
+
+		/* parse memory regions and add them to the identity mapping */
+		of_for_each_phandle(&it, ret, np, "memory-region", NULL, 0) {
+			int prot = IOMMU_READ | IOMMU_WRITE;
+			dma_addr_t start, limit, iova;
+			struct resource res;
+
+			dev_info(dev, "    region: %pOF\n", it.node);
+
+			ret = of_address_to_resource(it.node, 0, &res);
+			if (ret < 0) {
+				continue;
+			}
+
+			/* XXX check that region is not empty? */
+			dev_info(dev, "      %pR\n", &res);
+
+			start = ALIGN(res.start, page_size);
+			limit = ALIGN(res.start + resource_size(&res), page_size);
+
+			for (iova = start; iova < limit; iova += page_size) {
+				phys_addr_t phys;
+
+				phys = iommu_iova_to_phys(smmu->identity, iova);
+				if (phys) {
+					continue;
+				}
+
+				ret = iommu_map(smmu->identity, iova, iova,
+						page_size, prot);
+				if (ret < 0) {
+					dev_err(dev, "failed to map %pad: %d\n", &iova, ret);
+					continue;
+				}
+
+				mapped = true;
+			}
+		}
+
+		if (!mapped)
+			continue;
+
+		/* add stream IDs to the identity mapping */
+		while (!of_parse_phandle_with_args(np, "iommus", "#iommu-cells",
+						   index, &args)) {
+			unsigned int i, idx;
+			u16 sid, mask;
+			u32 fwid;
+
+			dev_info(dev, "    %pOF: %d arguments\n", args.np, args.args_count);
+
+			for (i = 0; i < args.args_count; i++)
+				dev_info(dev, "      %u: %08x\n", i, args.args[i]);
+
+			/* XXX check that args.np is the SMMU */
+			if (args.np != smmu->dev->of_node) {
+				dev_info(dev, "      master %u is not one of ours: %pOF\n", index, args.np);
+				index++;
+				continue;
+			}
+
+			fwid = arm_smmu_of_parse(args.np, args.args, args.args_count);
+			sid = FIELD_GET(SMR_ID, fwid);
+			mask = FIELD_GET(SMR_MASK, fwid);
+
+			dev_info(dev, "      SID: %04x MASK: %04x\n", sid, mask);
+
+			ret = arm_smmu_find_sme(smmu, sid, mask);
+			if (ret < 0) {
+				dev_err(dev, "failed to find SME: %d\n", ret);
+				index++;
+				continue;
+			}
+
+			idx = ret;
+			dev_info(dev, "      found index: %u\n", idx);
+
+			if (smrs && smmu->s2crs[idx].count == 0) {
+				smrs[idx].id = sid;
+				smrs[idx].mask = mask;
+				smrs[idx].valid = true;
+			}
+
+			smmu->s2crs[idx].count++;
+
+			//arm_smmu_write_sme(smmu, idx);
+			index++;
+		}
+	}
+
+	dev_info(dev, "< %s()\n", __func__);
+	return 0;
+}
+
 struct arm_smmu_match_data {
 	enum arm_smmu_arch_version version;
 	enum arm_smmu_implementation model;
@@ -1976,6 +2130,8 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev,
 	struct device *dev = &pdev->dev;
 	bool legacy_binding;
 
+	pr_info("> %s(pdev=%px, smmu=%px)\n", __func__, pdev, smmu);
+
 	if (of_property_read_u32(dev->of_node, "#global-interrupts",
 				 &smmu->num_global_irqs)) {
 		dev_err(dev, "missing #global-interrupts property\n");
@@ -2001,6 +2157,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev,
 	if (of_dma_is_coherent(dev->of_node))
 		smmu->features |= ARM_SMMU_FEAT_COHERENT_WALK;
 
+	pr_info("< %s()\n", __func__);
 	return 0;
 }
 
@@ -2118,6 +2275,10 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
+	err = arm_smmu_setup_identity(smmu);
+	if (err)
+		return err;
+
 	if (smmu->version == ARM_SMMU_V2) {
 		if (smmu->num_context_banks > smmu->num_context_irqs) {
 			dev_err(dev,
diff --git a/drivers/iommu/arm-smmu.h b/drivers/iommu/arm-smmu.h
index 6b6b877135de..001e60a3d18c 100644
--- a/drivers/iommu/arm-smmu.h
+++ b/drivers/iommu/arm-smmu.h
@@ -280,6 +280,8 @@ struct arm_smmu_device {
 
 	/* IOMMU core code handle */
 	struct iommu_device		iommu;
+
+	struct iommu_domain		*identity;
 };
 
 enum arm_smmu_context_fmt {

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 156 bytes --]

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

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 2/2] iommu: dma: Use of_iommu_get_resv_regions()
  2019-11-27 12:16         ` Thierry Reding
@ 2019-11-27 14:16           ` Thierry Reding
  2019-11-27 17:09             ` Robin Murphy
  0 siblings, 1 reply; 14+ messages in thread
From: Thierry Reding @ 2019-11-27 14:16 UTC (permalink / raw)
  To: Will Deacon
  Cc: devicetree, Robin Murphy, linux-kernel, iommu, Rob Herring, Frank Rowand

[-- Attachment #1.1: Type: text/plain, Size: 10764 bytes --]

On Wed, Nov 27, 2019 at 01:16:54PM +0100, Thierry Reding wrote:
> On Tue, Sep 17, 2019 at 06:59:50PM +0100, Will Deacon wrote:
> > On Mon, Sep 02, 2019 at 04:52:45PM +0200, Thierry Reding wrote:
> > > On Mon, Sep 02, 2019 at 03:22:35PM +0100, Robin Murphy wrote:
> > > > On 29/08/2019 12:14, Thierry Reding wrote:
> > > > > From: Thierry Reding <treding@nvidia.com>
> > > > > 
> > > > > For device tree nodes, use the standard of_iommu_get_resv_regions()
> > > > > implementation to obtain the reserved memory regions associated with a
> > > > > device.
> > > > 
> > > > This covers the window between iommu_probe_device() setting up a default
> > > > domain and the device's driver finally probing and taking control, but
> > > > iommu_probe_device() represents the point that the IOMMU driver first knows
> > > > about this device - there's still a window from whenever the IOMMU driver
> > > > itself probed up to here where the "unidentified" traffic may have already
> > > > been disrupted. Some IOMMU drivers have no option but to make the necessary
> > > > configuration during their own probe routine, at which point a struct device
> > > > for the display/etc. endpoint may not even exist yet.
> > > 
> > > Yeah, I think I'm actually running into this issue with the ARM SMMU
> > > driver. The above works fine with the Tegra SMMU driver, though, because
> > > it doesn't touch the SMMU configuration until a device is attached to a
> > > domain.
> > > 
> > > For anything earlier than iommu_probe_device(), I don't see a way of
> > > doing this generically. I've been working on a prototype to make these
> > > reserved memory regions early on for ARM SMMU but I've been failing so
> > > far. I think it would possibly work if we just switched the default for
> > > stream IDs to be "bypass" if they have any devices that have reserved
> > > memory regions, but again, this isn't quite working (yet).
> > 
> > I think we should avoid the use of "bypass" outside of the IOMMU probe()
> > routine if at all possible, since it leaves the thing wide open if we don't
> > subsequently probe the master.
> > 
> > Why can't we initialise a page-table early for StreamIDs with these
> > reserved regions, and then join that up later on if we see a device with
> > one of those StreamIDs attaching to a DMA domain? I suppose the nasty
> > case would be attaching to a domain that already has other masters
> > attached to it. Can we forbid that somehow for these devices? Otherwise,
> > I think we'd have to transiently switch to bypass whilst switching page
> > table.
> > 
> > What problems did you run into trying to implement this?
> 
> I picked this up again and was trying to make this work with your
> suggestion. Below is a rough draft and it seems to be working to some
> degree. Here's an extract of the log when I run this on Jetson TX2:
> 
> 	[    3.755328] arm-smmu 12000000.iommu: probing hardware configuration...
> 	[    3.762187] arm-smmu 12000000.iommu: SMMUv2 with:
> 	[    3.767137] arm-smmu 12000000.iommu:         stage 1 translation
> 	[    3.772806] arm-smmu 12000000.iommu:         stage 2 translation
> 	[    3.778471] arm-smmu 12000000.iommu:         nested translation
> 	[    3.784050] arm-smmu 12000000.iommu:         stream matching with 128 register groups
> 	[    3.791651] arm-smmu 12000000.iommu:         64 context banks (0 stage-2 only)
> 	[    3.798603] arm-smmu 12000000.iommu:         Supported page sizes: 0x61311000
> 	[    3.805460] arm-smmu 12000000.iommu:         Stage-1: 48-bit VA -> 48-bit IPA
> 	[    3.812310] arm-smmu 12000000.iommu:         Stage-2: 48-bit IPA -> 48-bit PA
> 	[    3.819159] arm-smmu 12000000.iommu: > arm_smmu_setup_identity(smmu=ffff0001eabcec80)
> 	[    3.827373] arm-smmu 12000000.iommu:   identity domain: ffff0001eaf8cae8 (ops: 0x0)
> 	[    3.835614] arm-smmu 12000000.iommu:   np: /ethernet@2490000
> 	[    3.841635] arm-smmu 12000000.iommu:   np: /sdhci@3400000
> 	[    3.847315] arm-smmu 12000000.iommu:   np: /sdhci@3420000
> 	[    3.852990] arm-smmu 12000000.iommu:   np: /sdhci@3440000
> 	[    3.858683] arm-smmu 12000000.iommu:   np: /sdhci@3460000
> 	[    3.864370] arm-smmu 12000000.iommu:   np: /hda@3510000
> 	[    3.869897] arm-smmu 12000000.iommu:   np: /usb@3530000
> 	[    3.875421] arm-smmu 12000000.iommu:   np: /pcie@10003000
> 	[    3.881109] arm-smmu 12000000.iommu:   np: /host1x@13e00000
> 	[    3.887012] arm-smmu 12000000.iommu:   np: /host1x@13e00000/display-hub@15200000/display@15200000
> 	[    3.896344] arm-smmu 12000000.iommu:     region: /reserved-memory/framebuffer@9607c000
> 	[    3.904707] arm-smmu 12000000.iommu:       [mem 0x9607c000-0x9687bfff]
> 	[    3.915719] arm-smmu 12000000.iommu:     /iommu@12000000: 1 arguments
> 	[    3.922487] arm-smmu 12000000.iommu:       0: 00000009
> 	[    3.927888] arm-smmu 12000000.iommu:       SID: 0009 MASK: 7f80
> 	[    3.934132] arm-smmu 12000000.iommu:       found index: 0
> 	[    3.939840] arm-smmu 12000000.iommu:   np: /host1x@13e00000/display-hub@15200000/display@15210000
> 	[    3.949183] arm-smmu 12000000.iommu:   np: /host1x@13e00000/display-hub@15200000/display@15220000
> 	[    3.958499] arm-smmu 12000000.iommu:   np: /host1x@13e00000/vic@15340000
> 	[    3.965557] arm-smmu 12000000.iommu:   np: /gpu@17000000
> 	[    3.971145] arm-smmu 12000000.iommu:   np: /bpmp
> 	[    3.976084] arm-smmu 12000000.iommu: < arm_smmu_setup_identity()
> 	[    3.982613] arm-smmu 12000000.iommu: > arm_smmu_write_sme(smmu=ffff0001eabcec80, idx=0)
> 	[    3.991072] arm-smmu 12000000.iommu:   ARM_SMMU_GR0_S2CR(0) < 00020000
> 	[    3.997922] arm-smmu 12000000.iommu:   ARM_SMMU_GR0_SMR(0) < ff800009
> 	[    4.004677] arm-smmu 12000000.iommu: < arm_smmu_write_sme()
> 	[    4.010528] arm-smmu 12000000.iommu: > arm_smmu_write_sme(smmu=ffff0001eabcec80, idx=1)
> 	[    4.018919] arm-smmu 12000000.iommu:   ARM_SMMU_GR0_S2CR(1) < 00020000
> 	[    4.025773] arm-smmu 12000000.iommu:   ARM_SMMU_GR0_SMR(1) < 00000000
> 	[    4.032543] arm-smmu 12000000.iommu: < arm_smmu_write_sme()
> 	...
> 
> There's a bunch of these, but idx=0 is the only one that's actually
> populated because it corresponds to the display controller. However,
> shortly after this I see a bunch of these:
> 
> 	...
> 	[    7.588908] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x809; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
> 	[    7.589907] arm-smmu: > arm_smmu_of_xlate(dev=ffff0001eaecf010, args=ffff80001024bae8)
> 	[    7.603599] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00000809, GFSYNR2 0x00000000
> 	[    7.604218] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x1409; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
> 	[    7.611956] arm-smmu: < arm_smmu_of_xlate() = 0
> 	[    7.622636] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00001409, GFSYNR2 0x00000000
> 	[    7.622658] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x1809; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
> 	[    7.622662] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00001809, GFSYNR2 0x00000000
> 	[    7.622676] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x409; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
> 	[    7.637739] arm-smmu 12000000.iommu:   ARM_SMMU_GR0_S2CR(1) < 00000001
> 	[    7.642199] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00000409, GFSYNR2 0x00000000
> 	[    7.642216] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x9; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
> 	[    7.642221] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00000009, GFSYNR2 0x00000000
> 	[    7.642237] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x1c09; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
> 	[    7.652992] tegra-host1x 13e00000.host1x: Adding to iommu group 0
> 	[    7.667720] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00001c09, GFSYNR2 0x00000000
> 	[    7.667732] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x9; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
> 	[    7.667736] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00000009, GFSYNR2 0x00000000
> 	[    7.667748] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x1809; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
> 	[    7.667752] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00001809, GFSYNR2 0x00000000
> 	[    7.667765] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x9; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
> 	[    7.678511] arm-smmu 12000000.iommu: > arm_smmu_write_sme(smmu=ffff0001eabcec80, idx=1)
> 	[    7.693158] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00000009, GFSYNR2 0x00000000
> 	[    7.693170] arm-smmu 12000000.iommu: Blocked unknown Stream ID 0x1009; boot with "arm-smmu.disable_bypass=0" to allow, but this may have security implications
> 	[    7.693174] arm-smmu 12000000.iommu:         GFSR 0x80000002, GFSYNR0 0x00000000, GFSYNR1 0x00001009, GFSYNR2 0x00000000
> 
> Note that stream ID 0x9 is TEGRA186_SID_NVDISPLAY, which is associated
> with the display controllers. One of these display controllers is live
> because it was turned on by the bootloader to show a splash screen.
> 
> What I don't really understand is why it thinks that that stream ID is
> unknown. One possibility I see is that perhaps the S2CR(0) and/or SMR(0)
> registers might have gotten overwritten, but I don't see where that may
> happen.
> 
> The errors stop eventually when the display controller is hooked up
> properly via the DMA API, but the whole purpose here is obviously to get
> to that point much earlier.
> 
> Any ideas what I might be doing wrong? Any comments on the general
> approach?

Nevermind that, I figured out that I was missingthe initialization of
some of the S2CR variables. I've got something that I think is working
now, though I don't know yet how to go about cleaning up the initial
mapping and "recycling" it.

I'll clean things up a bit, run some more tests and post a new patch
that can serve as a basis for discussion.

Thierry

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 156 bytes --]

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

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 2/2] iommu: dma: Use of_iommu_get_resv_regions()
  2019-11-27 14:16           ` Thierry Reding
@ 2019-11-27 17:09             ` Robin Murphy
  2019-11-28 11:43               ` Thierry Reding
  0 siblings, 1 reply; 14+ messages in thread
From: Robin Murphy @ 2019-11-27 17:09 UTC (permalink / raw)
  To: Thierry Reding, Will Deacon
  Cc: devicetree, linux-kernel, iommu, Rob Herring, Frank Rowand

On 27/11/2019 2:16 pm, Thierry Reding wrote:
[...]
> Nevermind that, I figured out that I was missingthe initialization of
> some of the S2CR variables. I've got something that I think is working
> now, though I don't know yet how to go about cleaning up the initial
> mapping and "recycling" it.
> 
> I'll clean things up a bit, run some more tests and post a new patch
> that can serve as a basis for discussion.

I'm a little puzzled by the smmu->identity domain - disregarding the 
fact that it's not actually used by the given diff ;) - if isolation is 
the reason for not simply using a bypass S2CR for the window between 
reset and the relevant .add_device call where the default domain proper 
comes in[1], then surely exposing the union of memory regions to the 
union of all associated devices isn't all that desirable either.

Either way, I'll give you the pre-emptive warning that this is the SMMU 
in the way of my EFI framebuffer ;)

...
arm-smmu 7fb20000.iommu: 	1 context banks (1 stage-2 only)
...

Robin.

[1] the fact that it currently depends on probe order whether getting 
that .add_device call requires a driver probing for the device is an 
error as discussed elsewhere, and will get fixed separately, I promise.
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 2/2] iommu: dma: Use of_iommu_get_resv_regions()
  2019-11-27 17:09             ` Robin Murphy
@ 2019-11-28 11:43               ` Thierry Reding
  0 siblings, 0 replies; 14+ messages in thread
From: Thierry Reding @ 2019-11-28 11:43 UTC (permalink / raw)
  To: Robin Murphy
  Cc: devicetree, Will Deacon, linux-kernel, iommu, Rob Herring, Frank Rowand

[-- Attachment #1.1.1: Type: text/plain, Size: 3361 bytes --]

On Wed, Nov 27, 2019 at 05:09:41PM +0000, Robin Murphy wrote:
> On 27/11/2019 2:16 pm, Thierry Reding wrote:
> [...]
> > Nevermind that, I figured out that I was missingthe initialization of
> > some of the S2CR variables. I've got something that I think is working
> > now, though I don't know yet how to go about cleaning up the initial
> > mapping and "recycling" it.
> > 
> > I'll clean things up a bit, run some more tests and post a new patch
> > that can serve as a basis for discussion.
> 
> I'm a little puzzled by the smmu->identity domain - disregarding the fact
> that it's not actually used by the given diff ;) - if isolation is the
> reason for not simply using a bypass S2CR for the window between reset and
> the relevant .add_device call where the default domain proper comes in[1],
> then surely exposing the union of memory regions to the union of all
> associated devices isn't all that desirable either.

A bypass S2CR was what I had originally in mind, but Will objected to
that because it "leaves the thing wide open if we don't subsequently
probe the master."[0]

Will went on to suggest setting up a page-table early for stream IDs
with reserved regions, so that's what I implemented. It ends up working
fairly nicely (see attached patch).

I suppose putting all the masters into the same bucket isn't an ideal
solution, but it's pretty simple and straightforward. Also, I don't
expect this to be a very common use-case. In fact, the only place where
I'm aware that this is needed is for display controllers scanning out a
splash screen. So the worst that could happen here is if they somehow
got the addresses mixed up and read each others' framebuffers, which
would really only be possible if they were already doing so before the
SMMU was initialized. Any harm from that would already be done.

I don't think there's a real risk here. Before the ARM SMMU driver takes
over and configures all contexts as fault by default all of these
devices are reading from physical memory without any isolation. Setting
up this identity domain will allow them to keep accessing the regions
that they were meant to access, while still faulting when access happens
outside.

> Either way, I'll give you the pre-emptive warning that this is the SMMU in
> the way of my EFI framebuffer ;)
> 
> ...
> arm-smmu 7fb20000.iommu: 	1 context banks (1 stage-2 only)
> ...

Interesting. How did you avoid getting the faults by default? Do you
just enable bypass by default?

If I understand correctly, this would mean that you can have only a
single IOMMU domain in that case, right? In that case it would perhaps
be better to keep a list of identity IOMMU domains and later on somehow
pass them on when the driver takes over. Basically these would have to
become the IOMMU groups' default domains.

> Robin.
> 
> [1] the fact that it currently depends on probe order whether getting that
> .add_device call requires a driver probing for the device is an error as
> discussed elsewhere, and will get fixed separately, I promise.

I'm not sure I understand how that would fix anything. You'd still need
to program the SMMU first before calling the ->add_device() for all the
masters, in which case you're still going to run into faults.

Thierry

[0]: https://lkml.org/lkml/2019/9/17/745

[-- Attachment #1.1.2: 0001-iommu-arm-smmu-Add-support-for-early-direct-mappings.patch --]
[-- Type: text/plain, Size: 7203 bytes --]

From cd7be912e74bdd463384e42f1aa275e959f4bee2 Mon Sep 17 00:00:00 2001
From: Thierry Reding <treding@nvidia.com>
Date: Thu, 28 Nov 2019 12:03:58 +0100
Subject: [PATCH] iommu: arm-smmu: Add support for early direct mappings

On platforms, the firmware will setup hardware to read from a given
region of memory. One such example is a display controller that is
scanning out a splash screen from physical memory.

During Linux's boot process, the ARM SMMU will configure all contexts to
fault by default. This means that memory accesses that happen by an SMMU
master before its driver has had a chance to properly set up the IOMMU
will cause a fault. This is especially annoying for something like the
display controller scanning out a splash screen because the faults will
result in the display controller getting bogus data (all-ones on Tegra)
and since it repeatedly scans that framebuffer, it will keep triggering
such faults and spam the boot log with them.

In order to work around such problems, scan the device tree for IOMMU
masters and set up a special identity domain that will map 1:1 all of
the reserved regions associated with them. This happens before the SMMU
is enabled, so that the mappings are already set up before translations
begin.

TODO: remove identity domain when no longer in use

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/iommu/arm-smmu.c | 172 ++++++++++++++++++++++++++++++++++++++-
 drivers/iommu/arm-smmu.h |   2 +
 2 files changed, 173 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 58ec52d3c5af..3d6c58ce3bab 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -1887,6 +1887,172 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 	return 0;
 }
 
+static int arm_smmu_identity_map_regions(struct arm_smmu_device *smmu,
+					 struct device_node *np)
+{
+	struct device *dev = smmu->dev;
+	struct of_phandle_iterator it;
+	unsigned long page_size;
+	unsigned int count = 0;
+	int ret;
+
+	page_size = 1UL << __ffs(smmu->identity->pgsize_bitmap);
+
+	/* parse memory regions and add them to the identity mapping */
+	of_for_each_phandle(&it, ret, np, "memory-region", NULL, 0) {
+		int prot = IOMMU_READ | IOMMU_WRITE;
+		dma_addr_t start, limit, iova;
+		struct resource res;
+
+		ret = of_address_to_resource(it.node, 0, &res);
+		if (ret < 0) {
+			dev_err(dev, "failed to parse memory region %pOF: %d\n",
+				it.node, ret);
+			continue;
+		}
+
+		/* check that region is not empty */
+		if (resource_size(&res) == 0) {
+			dev_dbg(dev, "skipping empty memory region %pOF\n",
+				it.node);
+			continue;
+		}
+
+		start = ALIGN(res.start, page_size);
+		limit = ALIGN(res.start + resource_size(&res), page_size);
+
+		for (iova = start; iova < limit; iova += page_size) {
+			phys_addr_t phys;
+
+			/* check that this IOVA isn't already mapped */
+			phys = iommu_iova_to_phys(smmu->identity, iova);
+			if (phys)
+				continue;
+
+			ret = iommu_map(smmu->identity, iova, iova, page_size,
+					prot);
+			if (ret < 0) {
+				dev_err(dev, "failed to map %pad for %pOF: %d\n",
+					&iova, it.node, ret);
+				continue;
+			}
+		}
+
+		dev_dbg(dev, "identity mapped memory region %pR\n", &res);
+		count++;
+	}
+
+	return count;
+}
+
+static int arm_smmu_identity_add_master(struct arm_smmu_device *smmu,
+					struct of_phandle_args *args)
+{
+	struct arm_smmu_domain *identity = to_smmu_domain(smmu->identity);
+	struct arm_smmu_smr *smrs = smmu->smrs;
+	struct device *dev = smmu->dev;
+	unsigned int index;
+	u16 sid, mask;
+	u32 fwid;
+	int ret;
+
+	/* skip masters that aren't ours */
+	if (args->np != dev->of_node)
+		return 0;
+
+	fwid = arm_smmu_of_parse(args->np, args->args, args->args_count);
+	sid = FIELD_GET(SMR_ID, fwid);
+	mask = FIELD_GET(SMR_MASK, fwid);
+
+	ret = arm_smmu_find_sme(smmu, sid, mask);
+	if (ret < 0) {
+		dev_err(dev, "failed to find SME: %d\n", ret);
+		return ret;
+	}
+
+	index = ret;
+
+	if (smrs && smmu->s2crs[index].count == 0) {
+		smrs[index].id = sid;
+		smrs[index].mask = mask;
+		smrs[index].valid = true;
+	}
+
+	smmu->s2crs[index].type = S2CR_TYPE_TRANS;
+	smmu->s2crs[index].privcfg = S2CR_PRIVCFG_DEFAULT;
+	smmu->s2crs[index].cbndx = identity->cfg.cbndx;
+	smmu->s2crs[index].count++;
+
+	return 0;
+}
+
+static int arm_smmu_identity_add_device(struct arm_smmu_device *smmu,
+					struct device_node *np)
+{
+	struct device *dev = smmu->dev;
+	struct of_phandle_args args;
+	unsigned int index = 0;
+	int ret;
+
+	/* add stream IDs to the identity mapping */
+	while (!of_parse_phandle_with_args(np, "iommus", "#iommu-cells",
+					   index, &args)) {
+		ret = arm_smmu_identity_add_master(smmu, &args);
+		if (ret < 0)
+			return ret;
+
+		index++;
+	}
+
+	return 0;
+}
+
+static int arm_smmu_setup_identity(struct arm_smmu_device *smmu)
+{
+	struct arm_smmu_domain *identity;
+	struct device *dev = smmu->dev;
+	struct device_node *np;
+	int ret;
+
+	/* create early identity mapping */
+	smmu->identity = arm_smmu_domain_alloc(IOMMU_DOMAIN_UNMANAGED);
+	if (!smmu->identity) {
+		dev_err(dev, "failed to create identity domain\n");
+		return -ENOMEM;
+	}
+
+	smmu->identity->pgsize_bitmap = smmu->pgsize_bitmap;
+	smmu->identity->type = IOMMU_DOMAIN_UNMANAGED;
+	smmu->identity->ops = &arm_smmu_ops;
+
+	ret = arm_smmu_init_domain_context(smmu->identity, smmu);
+	if (ret < 0) {
+		dev_err(dev, "failed to initialize identity domain: %d\n", ret);
+		return ret;
+	}
+
+	identity = to_smmu_domain(smmu->identity);
+
+	for_each_node_with_property(np, "iommus") {
+		ret = arm_smmu_identity_map_regions(smmu, np);
+		if (ret < 0)
+			continue;
+
+		/*
+		 * Do not add devices to the early identity mapping if they
+		 * do not define any memory-regions.
+		 */
+		if (ret == 0)
+			continue;
+
+		ret = arm_smmu_identity_add_device(smmu, np);
+		if (ret < 0)
+			continue;
+	}
+
+	return 0;
+}
+
 struct arm_smmu_match_data {
 	enum arm_smmu_arch_version version;
 	enum arm_smmu_implementation model;
@@ -2128,6 +2294,10 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
+	err = arm_smmu_setup_identity(smmu);
+	if (err)
+		return err;
+
 	if (smmu->version == ARM_SMMU_V2) {
 		if (smmu->num_context_banks > smmu->num_context_irqs) {
 			dev_err(dev,
@@ -2170,8 +2340,8 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
 	}
 
 	platform_set_drvdata(pdev, smmu);
-	arm_smmu_device_reset(smmu);
 	arm_smmu_test_smr_masks(smmu);
+	arm_smmu_device_reset(smmu);
 
 	/*
 	 * We want to avoid touching dev->power.lock in fastpaths unless
diff --git a/drivers/iommu/arm-smmu.h b/drivers/iommu/arm-smmu.h
index 6b6b877135de..001e60a3d18c 100644
--- a/drivers/iommu/arm-smmu.h
+++ b/drivers/iommu/arm-smmu.h
@@ -280,6 +280,8 @@ struct arm_smmu_device {
 
 	/* IOMMU core code handle */
 	struct iommu_device		iommu;
+
+	struct iommu_domain		*identity;
 };
 
 enum arm_smmu_context_fmt {
-- 
2.23.0


[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 156 bytes --]

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

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, back to index

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-29 11:14 [PATCH 0/2] iommu: Support reserved-memory regions Thierry Reding
2019-08-29 11:14 ` [PATCH 1/2] iommu: Implement of_iommu_get_resv_regions() Thierry Reding
2019-09-02 13:38   ` Rob Herring
2019-09-02 13:54   ` Robin Murphy
2019-09-02 15:00     ` Thierry Reding
2019-09-09 14:04       ` Will Deacon
2019-08-29 11:14 ` [PATCH 2/2] iommu: dma: Use of_iommu_get_resv_regions() Thierry Reding
2019-09-02 14:22   ` Robin Murphy
2019-09-02 14:52     ` Thierry Reding
2019-09-17 17:59       ` Will Deacon
2019-11-27 12:16         ` Thierry Reding
2019-11-27 14:16           ` Thierry Reding
2019-11-27 17:09             ` Robin Murphy
2019-11-28 11:43               ` Thierry Reding

IOMMU Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-iommu/0 linux-iommu/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-iommu linux-iommu/ https://lore.kernel.org/linux-iommu \
		iommu@lists.linux-foundation.org
	public-inbox-index linux-iommu

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.linux-foundation.lists.iommu


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git