All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH for 4.6 00/13] xen/arm: Resync the SMMU driver with the Linux one
@ 2014-12-16 20:08 Julien Grall
  2014-12-16 20:08 ` [PATCH for 4.6 01/13] xen/arm: gic-v2: Change the device name in DT_DEVICE_START Julien Grall
                   ` (12 more replies)
  0 siblings, 13 replies; 41+ messages in thread
From: Julien Grall @ 2014-12-16 20:08 UTC (permalink / raw)
  To: xen-devel
  Cc: stefano.stabellini, manish.jaggi, Julien Grall, tim, ian.campbell

Hello,

The SMMU drivers has diverged from Linux. Having our own driver doesn't make
any benefits and make difficult to backport fixes and/or porting features such
as PCI.

With this series, the core of the SMMU drivers (i.e copied from Linux) is mostly
not modified. If it's the case a comment /* Xen: ... */ has been added
to explain why.

To make the change obvious the resync of the SMMU code is mode in several
step:
    1) Revert the current SMMU driver (patch #6)
    2) Import as it is the driver from Linux (patch #10)
    3) Apply 2 fixes useful to correctly use the SATA with the SMMU on
    calxeda. I don't know why Linux didn't yet applied (patch #11-12)
    4) Changes for Xen (patch #13)

I also took the opportunity of the resync to consolidate the iommu ops in
a single set. When I added the IOMMU set to handle device tree passthrough (
ops assign_dt_device and reassign_dt_device), I didn't think about
merging the ops with the PCI one. In fact Linux is using a single set
and have only few lines per driver specific to each set (PCI or device tree).

A branch is available with all the changes:
    git://xenbits.xen.org/people/julieng/xen-unstable.git branch smmu-rework

Sincerely yours,

Andreas Herrmann (2):
  xen/iommu: smmu: Check for duplicate stream IDs when registering
    master devices
  xen/iommu: smmu: Introduce automatic stream-id-masking

Julien Grall (11):
  xen/arm: gic-v2: Change the device name in DT_DEVICE_START
  xen/arm: vgic: Drop unecessary include asm/device.h
  xen: Introduce ACCESS_ONCE macro
  xen/dt: Extend dt_device_match to possibly store data
  xen/arm: device: Rename device_type into device_match
  xen/iommu: arm: Remove temporary the SMMU driver
  xen: Introduce a generic way to describe device
  xen/iommu: Consolidate device assignment ops into a single set
  xen/arm: Describe device supported by a driver with dt_match_node
  xen/iommu: arm: Import the SMMU driver from Linux
  xen/iommu: smmu: Add Xen specific code to be able to use the driver

 xen/arch/arm/device.c                       |   27 +-
 xen/arch/arm/domain_build.c                 |    2 +-
 xen/arch/arm/gic-v2.c                       |   15 +-
 xen/arch/arm/gic-v3.c                       |   10 +-
 xen/arch/arm/gic.c                          |    2 +-
 xen/arch/arm/platform.c                     |    2 +-
 xen/arch/arm/vgic-v2.c                      |    1 -
 xen/arch/arm/vgic-v3.c                      |    1 -
 xen/common/Makefile                         |    1 +
 xen/common/device.c                         |   21 +
 xen/common/device_tree.c                    |   13 +-
 xen/drivers/char/dt-uart.c                  |    4 +-
 xen/drivers/char/exynos4210-uart.c          |   10 +-
 xen/drivers/char/ns16550.c                  |   14 +-
 xen/drivers/char/omap-uart.c                |   10 +-
 xen/drivers/char/pl011.c                    |   10 +-
 xen/drivers/passthrough/amd/pci_amd_iommu.c |   14 +-
 xen/drivers/passthrough/arm/iommu.c         |    2 +-
 xen/drivers/passthrough/arm/smmu.c          | 4107 +++++++++++++++++----------
 xen/drivers/passthrough/device_tree.c       |    5 +-
 xen/drivers/passthrough/pci.c               |   22 +-
 xen/drivers/passthrough/vtd/iommu.c         |   19 +-
 xen/include/asm-arm/device.h                |   19 +-
 xen/include/asm-arm/gic.h                   |   15 +-
 xen/include/asm-x86/device.h                |   17 +
 xen/include/xen/compiler.h                  |   14 +
 xen/include/xen/device.h                    |   40 +
 xen/include/xen/device_tree.h               |   19 +-
 xen/include/xen/iommu.h                     |   18 +-
 xen/include/xen/pci.h                       |   12 +
 30 files changed, 2843 insertions(+), 1623 deletions(-)
 create mode 100644 xen/common/device.c
 create mode 100644 xen/include/asm-x86/device.h
 create mode 100644 xen/include/xen/device.h

-- 
2.1.3

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

* [PATCH for 4.6 01/13] xen/arm: gic-v2: Change the device name in DT_DEVICE_START
  2014-12-16 20:08 [PATCH for 4.6 00/13] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
@ 2014-12-16 20:08 ` Julien Grall
  2014-12-16 20:08 ` [PATCH for 4.6 02/13] xen/arm: vgic: Drop unecessary include asm/device.h Julien Grall
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 41+ messages in thread
From: Julien Grall @ 2014-12-16 20:08 UTC (permalink / raw)
  To: xen-devel
  Cc: stefano.stabellini, manish.jaggi, Julien Grall, tim, ian.campbell

I'm not sure why a ':' has been added in the name... But none of the
other usages doesn't have it.

Signed-off-by: Julien Grall <julien.grall@linaro.org>
---
 xen/arch/arm/gic-v2.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c
index faad1ff..f149e09 100644
--- a/xen/arch/arm/gic-v2.c
+++ b/xen/arch/arm/gic-v2.c
@@ -772,7 +772,7 @@ static const char * const gicv2_dt_compat[] __initconst =
     NULL
 };
 
-DT_DEVICE_START(gicv2, "GICv2:", DEVICE_GIC)
+DT_DEVICE_START(gicv2, "GICv2", DEVICE_GIC)
         .compatible = gicv2_dt_compat,
         .init = gicv2_init,
 DT_DEVICE_END
-- 
2.1.3

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

* [PATCH for 4.6 02/13] xen/arm: vgic: Drop unecessary include asm/device.h
  2014-12-16 20:08 [PATCH for 4.6 00/13] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
  2014-12-16 20:08 ` [PATCH for 4.6 01/13] xen/arm: gic-v2: Change the device name in DT_DEVICE_START Julien Grall
@ 2014-12-16 20:08 ` Julien Grall
  2014-12-16 20:08 ` [PATCH for 4.6 03/13] xen: Introduce ACCESS_ONCE macro Julien Grall
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 41+ messages in thread
From: Julien Grall @ 2014-12-16 20:08 UTC (permalink / raw)
  To: xen-devel
  Cc: stefano.stabellini, manish.jaggi, Julien Grall, tim, ian.campbell

The header asm/device.h has been included in the vgic code during
splitting to support multiple version. But no code within those files
requires it.

Signed-off-by: Julien Grall <julien.grall@linaro.org>
---
 xen/arch/arm/vgic-v2.c | 1 -
 xen/arch/arm/vgic-v3.c | 1 -
 2 files changed, 2 deletions(-)

diff --git a/xen/arch/arm/vgic-v2.c b/xen/arch/arm/vgic-v2.c
index 1369f78..259d04f 100644
--- a/xen/arch/arm/vgic-v2.c
+++ b/xen/arch/arm/vgic-v2.c
@@ -26,7 +26,6 @@
 #include <xen/sched.h>
 
 #include <asm/current.h>
-#include <asm/device.h>
 
 #include <asm/mmio.h>
 #include <asm/gic.h>
diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index ff99e50..da27605 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -27,7 +27,6 @@
 #include <xen/sched.h>
 #include <xen/sizes.h>
 #include <asm/current.h>
-#include <asm/device.h>
 #include <asm/mmio.h>
 #include <asm/gic_v3_defs.h>
 #include <asm/gic.h>
-- 
2.1.3

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

* [PATCH for 4.6 03/13] xen: Introduce ACCESS_ONCE macro
  2014-12-16 20:08 [PATCH for 4.6 00/13] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
  2014-12-16 20:08 ` [PATCH for 4.6 01/13] xen/arm: gic-v2: Change the device name in DT_DEVICE_START Julien Grall
  2014-12-16 20:08 ` [PATCH for 4.6 02/13] xen/arm: vgic: Drop unecessary include asm/device.h Julien Grall
@ 2014-12-16 20:08 ` Julien Grall
  2014-12-17 10:05   ` Jan Beulich
  2014-12-16 20:08 ` [PATCH for 4.6 04/13] xen/dt: Extend dt_device_match to possibly store data Julien Grall
                   ` (9 subsequent siblings)
  12 siblings, 1 reply; 41+ messages in thread
From: Julien Grall @ 2014-12-16 20:08 UTC (permalink / raw)
  To: xen-devel
  Cc: Keir Fraser, ian.campbell, tim, manish.jaggi, Julien Grall,
	Ian Jackson, stefano.stabellini, Jan Beulich

This macro can be used in drivers imported from Linux.

Signed-off-by: Julien Grall <julien.grall@linaro.org>
CC: Ian Jackson <ian.jackson@eu.citrix.com>
CC: Jan Beulich <jbeulich@suse.com>
CC: Keir Fraser <keir@xen.org>
---
 xen/include/xen/compiler.h | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/xen/include/xen/compiler.h b/xen/include/xen/compiler.h
index 4b3472d..57eb7a6 100644
--- a/xen/include/xen/compiler.h
+++ b/xen/include/xen/compiler.h
@@ -90,4 +90,18 @@
     __asm__ ("" : "=r"(__ptr) : "0"(ptr));      \
     (typeof(ptr)) (__ptr + (off)); })
 
+/*
+ * Prevent the compiler from merging or refetching accesses.  The compiler
+ * is also forbidden from reordering successive instances of ACCESS_ONCE(),
+ * but only when the compiler is aware of some particular ordering.  One way
+ * to make the compiler aware of ordering is to put the two invocations of
+ * ACCESS_ONCE() in different C statements.
+ *
+ * This macro does absolutely -nothing- to prevent the CPU from reordering,
+ * merging, or refetching absolutely anything at any time.  Its main intended
+ * use is to mediate communication between process-level code and irq/NMI
+ * handlers, all running on the same CPU.
+ */
+#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
+
 #endif /* __LINUX_COMPILER_H */
-- 
2.1.3

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

* [PATCH for 4.6 04/13] xen/dt: Extend dt_device_match to possibly store data
  2014-12-16 20:08 [PATCH for 4.6 00/13] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (2 preceding siblings ...)
  2014-12-16 20:08 ` [PATCH for 4.6 03/13] xen: Introduce ACCESS_ONCE macro Julien Grall
@ 2014-12-16 20:08 ` Julien Grall
  2014-12-16 20:08 ` [PATCH for 4.6 05/13] xen/arm: device: Rename device_type into device_match Julien Grall
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 41+ messages in thread
From: Julien Grall @ 2014-12-16 20:08 UTC (permalink / raw)
  To: xen-devel
  Cc: stefano.stabellini, manish.jaggi, Julien Grall, tim, ian.campbell

Some drivers may want to configure differently the device depending on
the compatible string.

Also modify the return type of dt_match_node to return the matching
structure.

Signed-off-by: Julien Grall <julien.grall@linaro.org>
---
 xen/arch/arm/platform.c       |  2 +-
 xen/common/device_tree.c      | 12 ++++++------
 xen/include/xen/device_tree.h |  6 ++++--
 3 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/xen/arch/arm/platform.c b/xen/arch/arm/platform.c
index cb4cda8..a79a098 100644
--- a/xen/arch/arm/platform.c
+++ b/xen/arch/arm/platform.c
@@ -157,7 +157,7 @@ bool_t platform_device_is_blacklisted(const struct dt_device_node *node)
     if ( platform && platform->blacklist_dev )
         blacklist = platform->blacklist_dev;
 
-    return dt_match_node(blacklist, node);
+    return (dt_match_node(blacklist, node) != NULL);
 }
 
 unsigned int platform_dom0_evtchn_ppi(void)
diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c
index f72b2e9..34a1b9e 100644
--- a/xen/common/device_tree.c
+++ b/xen/common/device_tree.c
@@ -290,11 +290,12 @@ struct dt_device_node *dt_find_node_by_alias(const char *alias)
     return NULL;
 }
 
-bool_t dt_match_node(const struct dt_device_match *matches,
-                     const struct dt_device_node *node)
+const struct dt_device_match *
+dt_match_node(const struct dt_device_match *matches,
+              const struct dt_device_node *node)
 {
     if ( !matches )
-        return 0;
+        return NULL;
 
     while ( matches->path || matches->type ||
             matches->compatible || matches->not_available )
@@ -314,12 +315,11 @@ bool_t dt_match_node(const struct dt_device_match *matches,
             match &= !dt_device_is_available(node);
 
         if ( match )
-            return match;
-
+            return matches;
         matches++;
     }
 
-    return 0;
+    return NULL;
 }
 
 const struct dt_device_node *dt_get_parent(const struct dt_device_node *node)
diff --git a/xen/include/xen/device_tree.h b/xen/include/xen/device_tree.h
index 08db8bc..6502369 100644
--- a/xen/include/xen/device_tree.h
+++ b/xen/include/xen/device_tree.h
@@ -28,6 +28,7 @@ struct dt_device_match {
     const char *type;
     const char *compatible;
     const bool_t not_available;
+    const void *data;
 };
 
 #define DT_MATCH_PATH(p)                { .path = p }
@@ -547,8 +548,9 @@ bool_t dt_device_is_available(const struct dt_device_node *device);
  *
  * Returns true if the device node match one of dt_device_match.
  */
-bool_t dt_match_node(const struct dt_device_match *matches,
-                     const struct dt_device_node *node);
+const struct dt_device_match *
+dt_match_node(const struct dt_device_match *matches,
+              const struct dt_device_node *node);
 
 /**
  * dt_find_matching_node - Find a node based on an dt_device_match match table
-- 
2.1.3

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

* [PATCH for 4.6 05/13] xen/arm: device: Rename device_type into device_match
  2014-12-16 20:08 [PATCH for 4.6 00/13] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (3 preceding siblings ...)
  2014-12-16 20:08 ` [PATCH for 4.6 04/13] xen/dt: Extend dt_device_match to possibly store data Julien Grall
@ 2014-12-16 20:08 ` Julien Grall
  2014-12-16 20:08 ` [PATCH for 4.6 06/13] xen/iommu: arm: Remove temporary the SMMU driver Julien Grall
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 41+ messages in thread
From: Julien Grall @ 2014-12-16 20:08 UTC (permalink / raw)
  To: xen-devel
  Cc: stefano.stabellini, manish.jaggi, Julien Grall, tim, ian.campbell

This enum was used for matching a specific device and not to get the
type of device.

Hence the name device_type will be used for another purpose later.

Signed-off-by: Julien Grall <julien.grall@linaro.org>
---
 xen/arch/arm/device.c        | 4 ++--
 xen/include/asm-arm/device.h | 8 ++++----
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/xen/arch/arm/device.c b/xen/arch/arm/device.c
index 59e94c0..693b9af 100644
--- a/xen/arch/arm/device.c
+++ b/xen/arch/arm/device.c
@@ -40,7 +40,7 @@ static bool_t __init device_is_compatible(const struct device_desc *desc,
     return 0;
 }
 
-int __init device_init(struct dt_device_node *dev, enum device_type type,
+int __init device_init(struct dt_device_node *dev, enum device_match type,
                        const void *data)
 {
     const struct device_desc *desc;
@@ -67,7 +67,7 @@ int __init device_init(struct dt_device_node *dev, enum device_type type,
     return -EBADF;
 }
 
-enum device_type device_get_type(const struct dt_device_node *dev)
+enum device_match device_get_type(const struct dt_device_node *dev)
 {
     const struct device_desc *desc;
 
diff --git a/xen/include/asm-arm/device.h b/xen/include/asm-arm/device.h
index 74a80c6..72a9028 100644
--- a/xen/include/asm-arm/device.h
+++ b/xen/include/asm-arm/device.h
@@ -4,7 +4,7 @@
 #include <xen/init.h>
 #include <xen/device_tree.h>
 
-enum device_type
+enum device_match
 {
     DEVICE_SERIAL,
     DEVICE_IOMMU,
@@ -17,7 +17,7 @@ struct device_desc {
     /* Device name */
     const char *name;
     /* Device type */
-    enum device_type type;
+    enum device_match type;
     /* Array of device tree 'compatible' strings */
     const char *const *compatible;
     /* Device initialization */
@@ -32,7 +32,7 @@ struct device_desc {
  *
  *  Return 0 on success.
  */
-int __init device_init(struct dt_device_node *dev, enum device_type type,
+int __init device_init(struct dt_device_node *dev, enum device_match type,
                        const void *data);
 
 /**
@@ -41,7 +41,7 @@ int __init device_init(struct dt_device_node *dev, enum device_type type,
  *
  * Return the device type on success or DEVICE_ANY on failure
  */
-enum device_type device_get_type(const struct dt_device_node *dev);
+enum device_match device_get_type(const struct dt_device_node *dev);
 
 #define DT_DEVICE_START(_name, _namestr, _type)                     \
 static const struct device_desc __dev_desc_##_name __used           \
-- 
2.1.3

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

* [PATCH for 4.6 06/13] xen/iommu: arm: Remove temporary the SMMU driver
  2014-12-16 20:08 [PATCH for 4.6 00/13] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (4 preceding siblings ...)
  2014-12-16 20:08 ` [PATCH for 4.6 05/13] xen/arm: device: Rename device_type into device_match Julien Grall
@ 2014-12-16 20:08 ` Julien Grall
  2014-12-16 20:08 ` [PATCH for 4.6 07/13] xen: Introduce a generic way to describe device Julien Grall
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 41+ messages in thread
From: Julien Grall @ 2014-12-16 20:08 UTC (permalink / raw)
  To: xen-devel
  Cc: stefano.stabellini, manish.jaggi, Julien Grall, tim, ian.campbell

The current SMMU driver has completly diverged. That makes me hard to
maintain.

Signed-off-by: Julien Grall <julien.grall@linaro.org>
---
 xen/drivers/passthrough/arm/Makefile |    1 -
 xen/drivers/passthrough/arm/smmu.c   | 1784 ----------------------------------
 2 files changed, 1785 deletions(-)
 delete mode 100644 xen/drivers/passthrough/arm/smmu.c

diff --git a/xen/drivers/passthrough/arm/Makefile b/xen/drivers/passthrough/arm/Makefile
index f4cd26e..0484b79 100644
--- a/xen/drivers/passthrough/arm/Makefile
+++ b/xen/drivers/passthrough/arm/Makefile
@@ -1,2 +1 @@
 obj-y += iommu.o
-obj-y += smmu.o
diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c
deleted file mode 100644
index 42bde75..0000000
--- a/xen/drivers/passthrough/arm/smmu.c
+++ /dev/null
@@ -1,1784 +0,0 @@
-/*
- * IOMMU API for ARM architected SMMU implementations.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Based on Linux drivers/iommu/arm-smmu.c (commit 89a23cd)
- * Copyright (C) 2013 ARM Limited
- *
- * Author: Will Deacon <will.deacon@arm.com>
- *
- * Xen modification:
- * Julien Grall <julien.grall@linaro.org>
- * Copyright (C) 2014 Linaro Limited.
- *
- * This driver currently supports:
- *  - SMMUv1 and v2 implementations (didn't try v2 SMMU)
- *  - Stream-matching and stream-indexing
- *  - v7/v8 long-descriptor format
- *  - Non-secure access to the SMMU
- *  - 4k pages, p2m shared with the processor
- *  - Up to 40-bit addressing
- *  - Context fault reporting
- */
-
-#include <xen/config.h>
-#include <xen/delay.h>
-#include <xen/errno.h>
-#include <xen/irq.h>
-#include <xen/lib.h>
-#include <xen/list.h>
-#include <xen/mm.h>
-#include <xen/vmap.h>
-#include <xen/rbtree.h>
-#include <xen/sched.h>
-#include <asm/atomic.h>
-#include <asm/device.h>
-#include <asm/io.h>
-#include <asm/platform.h>
-
-/* Driver options */
-#define SMMU_OPT_SECURE_CONFIG_ACCESS   (1 << 0)
-
-/* Maximum number of stream IDs assigned to a single device */
-#define MAX_MASTER_STREAMIDS    MAX_PHANDLE_ARGS
-
-/* Maximum stream ID */
-#define SMMU_MAX_STREAMIDS      (PAGE_SIZE_64K - 1)
-
-/* Maximum number of context banks per SMMU */
-#define SMMU_MAX_CBS        128
-
-/* Maximum number of mapping groups per SMMU */
-#define SMMU_MAX_SMRS       128
-
-/* SMMU global address space */
-#define SMMU_GR0(smmu)      ((smmu)->base)
-#define SMMU_GR1(smmu)      ((smmu)->base + (smmu)->pagesize)
-
-/*
- * SMMU global address space with conditional offset to access secure aliases of
- * non-secure registers (e.g. nsCR0: 0x400, nsGFSR: 0x448, nsGFSYNR0: 0x450)
- */
-#define SMMU_GR0_NS(smmu)                                   \
-    ((smmu)->base +                                         \
-     ((smmu->options & SMMU_OPT_SECURE_CONFIG_ACCESS)    \
-        ? 0x400 : 0))
-
-/* Page table bits */
-#define SMMU_PTE_PAGE           (((pteval_t)3) << 0)
-#define SMMU_PTE_CONT           (((pteval_t)1) << 52)
-#define SMMU_PTE_AF             (((pteval_t)1) << 10)
-#define SMMU_PTE_SH_NS          (((pteval_t)0) << 8)
-#define SMMU_PTE_SH_OS          (((pteval_t)2) << 8)
-#define SMMU_PTE_SH_IS          (((pteval_t)3) << 8)
-
-#if PAGE_SIZE == PAGE_SIZE_4K
-#define SMMU_PTE_CONT_ENTRIES   16
-#elif PAGE_SIZE == PAGE_SIZE_64K
-#define SMMU_PTE_CONT_ENTRIES   32
-#else
-#define SMMU_PTE_CONT_ENTRIES   1
-#endif
-
-#define SMMU_PTE_CONT_SIZE      (PAGE_SIZE * SMMU_PTE_CONT_ENTRIES)
-#define SMMU_PTE_CONT_MASK      (~(SMMU_PTE_CONT_SIZE - 1))
-#define SMMU_PTE_HWTABLE_SIZE   (PTRS_PER_PTE * sizeof(pte_t))
-
-/* Stage-1 PTE */
-#define SMMU_PTE_AP_UNPRIV      (((pteval_t)1) << 6)
-#define SMMU_PTE_AP_RDONLY      (((pteval_t)2) << 6)
-#define SMMU_PTE_ATTRINDX_SHIFT 2
-#define SMMU_PTE_nG             (((pteval_t)1) << 11)
-
-/* Stage-2 PTE */
-#define SMMU_PTE_HAP_FAULT      (((pteval_t)0) << 6)
-#define SMMU_PTE_HAP_READ       (((pteval_t)1) << 6)
-#define SMMU_PTE_HAP_WRITE      (((pteval_t)2) << 6)
-#define SMMU_PTE_MEMATTR_OIWB   (((pteval_t)0xf) << 2)
-#define SMMU_PTE_MEMATTR_NC     (((pteval_t)0x5) << 2)
-#define SMMU_PTE_MEMATTR_DEV    (((pteval_t)0x1) << 2)
-
-/* Configuration registers */
-#define SMMU_GR0_sCR0           0x0
-#define SMMU_sCR0_CLIENTPD      (1 << 0)
-#define SMMU_sCR0_GFRE          (1 << 1)
-#define SMMU_sCR0_GFIE          (1 << 2)
-#define SMMU_sCR0_GCFGFRE       (1 << 4)
-#define SMMU_sCR0_GCFGFIE       (1 << 5)
-#define SMMU_sCR0_USFCFG        (1 << 10)
-#define SMMU_sCR0_VMIDPNE       (1 << 11)
-#define SMMU_sCR0_PTM           (1 << 12)
-#define SMMU_sCR0_FB            (1 << 13)
-#define SMMU_sCR0_BSU_SHIFT     14
-#define SMMU_sCR0_BSU_MASK      0x3
-
-/* Identification registers */
-#define SMMU_GR0_ID0            0x20
-#define SMMU_GR0_ID1            0x24
-#define SMMU_GR0_ID2            0x28
-#define SMMU_GR0_ID3            0x2c
-#define SMMU_GR0_ID4            0x30
-#define SMMU_GR0_ID5            0x34
-#define SMMU_GR0_ID6            0x38
-#define SMMU_GR0_ID7            0x3c
-#define SMMU_GR0_sGFSR          0x48
-#define SMMU_GR0_sGFSYNR0       0x50
-#define SMMU_GR0_sGFSYNR1       0x54
-#define SMMU_GR0_sGFSYNR2       0x58
-#define SMMU_GR0_PIDR0          0xfe0
-#define SMMU_GR0_PIDR1          0xfe4
-#define SMMU_GR0_PIDR2          0xfe8
-
-#define SMMU_ID0_S1TS           (1 << 30)
-#define SMMU_ID0_S2TS           (1 << 29)
-#define SMMU_ID0_NTS            (1 << 28)
-#define SMMU_ID0_SMS            (1 << 27)
-#define SMMU_ID0_PTFS_SHIFT     24
-#define SMMU_ID0_PTFS_MASK      0x2
-#define SMMU_ID0_PTFS_V8_ONLY   0x2
-#define SMMU_ID0_CTTW           (1 << 14)
-#define SMMU_ID0_NUMIRPT_SHIFT  16
-#define SMMU_ID0_NUMIRPT_MASK   0xff
-#define SMMU_ID0_NUMSMRG_SHIFT  0
-#define SMMU_ID0_NUMSMRG_MASK   0xff
-
-#define SMMU_ID1_PAGESIZE            (1 << 31)
-#define SMMU_ID1_NUMPAGENDXB_SHIFT   28
-#define SMMU_ID1_NUMPAGENDXB_MASK    7
-#define SMMU_ID1_NUMS2CB_SHIFT       16
-#define SMMU_ID1_NUMS2CB_MASK        0xff
-#define SMMU_ID1_NUMCB_SHIFT         0
-#define SMMU_ID1_NUMCB_MASK          0xff
-
-#define SMMU_ID2_OAS_SHIFT           4
-#define SMMU_ID2_OAS_MASK            0xf
-#define SMMU_ID2_IAS_SHIFT           0
-#define SMMU_ID2_IAS_MASK            0xf
-#define SMMU_ID2_UBS_SHIFT           8
-#define SMMU_ID2_UBS_MASK            0xf
-#define SMMU_ID2_PTFS_4K             (1 << 12)
-#define SMMU_ID2_PTFS_16K            (1 << 13)
-#define SMMU_ID2_PTFS_64K            (1 << 14)
-
-#define SMMU_PIDR2_ARCH_SHIFT        4
-#define SMMU_PIDR2_ARCH_MASK         0xf
-
-/* Global TLB invalidation */
-#define SMMU_GR0_STLBIALL           0x60
-#define SMMU_GR0_TLBIVMID           0x64
-#define SMMU_GR0_TLBIALLNSNH        0x68
-#define SMMU_GR0_TLBIALLH           0x6c
-#define SMMU_GR0_sTLBGSYNC          0x70
-#define SMMU_GR0_sTLBGSTATUS        0x74
-#define SMMU_sTLBGSTATUS_GSACTIVE   (1 << 0)
-#define SMMU_TLB_LOOP_TIMEOUT       1000000 /* 1s! */
-
-/* Stream mapping registers */
-#define SMMU_GR0_SMR(n)             (0x800 + ((n) << 2))
-#define SMMU_SMR_VALID              (1 << 31)
-#define SMMU_SMR_MASK_SHIFT         16
-#define SMMU_SMR_MASK_MASK          0x7fff
-#define SMMU_SMR_ID_SHIFT           0
-#define SMMU_SMR_ID_MASK            0x7fff
-
-#define SMMU_GR0_S2CR(n)        (0xc00 + ((n) << 2))
-#define SMMU_S2CR_CBNDX_SHIFT   0
-#define SMMU_S2CR_CBNDX_MASK    0xff
-#define SMMU_S2CR_TYPE_SHIFT    16
-#define SMMU_S2CR_TYPE_MASK     0x3
-#define SMMU_S2CR_TYPE_TRANS    (0 << SMMU_S2CR_TYPE_SHIFT)
-#define SMMU_S2CR_TYPE_BYPASS   (1 << SMMU_S2CR_TYPE_SHIFT)
-#define SMMU_S2CR_TYPE_FAULT    (2 << SMMU_S2CR_TYPE_SHIFT)
-
-/* Context bank attribute registers */
-#define SMMU_GR1_CBAR(n)                    (0x0 + ((n) << 2))
-#define SMMU_CBAR_VMID_SHIFT                0
-#define SMMU_CBAR_VMID_MASK                 0xff
-#define SMMU_CBAR_S1_MEMATTR_SHIFT          12
-#define SMMU_CBAR_S1_MEMATTR_MASK           0xf
-#define SMMU_CBAR_S1_MEMATTR_WB             0xf
-#define SMMU_CBAR_TYPE_SHIFT                16
-#define SMMU_CBAR_TYPE_MASK                 0x3
-#define SMMU_CBAR_TYPE_S2_TRANS             (0 << SMMU_CBAR_TYPE_SHIFT)
-#define SMMU_CBAR_TYPE_S1_TRANS_S2_BYPASS   (1 << SMMU_CBAR_TYPE_SHIFT)
-#define SMMU_CBAR_TYPE_S1_TRANS_S2_FAULT    (2 << SMMU_CBAR_TYPE_SHIFT)
-#define SMMU_CBAR_TYPE_S1_TRANS_S2_TRANS    (3 << SMMU_CBAR_TYPE_SHIFT)
-#define SMMU_CBAR_IRPTNDX_SHIFT             24
-#define SMMU_CBAR_IRPTNDX_MASK              0xff
-
-#define SMMU_GR1_CBA2R(n)                   (0x800 + ((n) << 2))
-#define SMMU_CBA2R_RW64_32BIT               (0 << 0)
-#define SMMU_CBA2R_RW64_64BIT               (1 << 0)
-
-/* Translation context bank */
-#define SMMU_CB_BASE(smmu)                  ((smmu)->base + ((smmu)->size >> 1))
-#define SMMU_CB(smmu, n)                    ((n) * (smmu)->pagesize)
-
-#define SMMU_CB_SCTLR                       0x0
-#define SMMU_CB_RESUME                      0x8
-#define SMMU_CB_TCR2                        0x10
-#define SMMU_CB_TTBR0_LO                    0x20
-#define SMMU_CB_TTBR0_HI                    0x24
-#define SMMU_CB_TCR                         0x30
-#define SMMU_CB_S1_MAIR0                    0x38
-#define SMMU_CB_FSR                         0x58
-#define SMMU_CB_FAR_LO                      0x60
-#define SMMU_CB_FAR_HI                      0x64
-#define SMMU_CB_FSYNR0                      0x68
-#define SMMU_CB_S1_TLBIASID                 0x610
-
-#define SMMU_SCTLR_S1_ASIDPNE               (1 << 12)
-#define SMMU_SCTLR_CFCFG                    (1 << 7)
-#define SMMU_SCTLR_CFIE                     (1 << 6)
-#define SMMU_SCTLR_CFRE                     (1 << 5)
-#define SMMU_SCTLR_E                        (1 << 4)
-#define SMMU_SCTLR_AFE                      (1 << 2)
-#define SMMU_SCTLR_TRE                      (1 << 1)
-#define SMMU_SCTLR_M                        (1 << 0)
-#define SMMU_SCTLR_EAE_SBOP                 (SMMU_SCTLR_AFE | SMMU_SCTLR_TRE)
-
-#define SMMU_RESUME_RETRY                   (0 << 0)
-#define SMMU_RESUME_TERMINATE               (1 << 0)
-
-#define SMMU_TCR_EAE                        (1 << 31)
-
-#define SMMU_TCR_PASIZE_SHIFT               16
-#define SMMU_TCR_PASIZE_MASK                0x7
-
-#define SMMU_TCR_TG0_4K                     (0 << 14)
-#define SMMU_TCR_TG0_64K                    (1 << 14)
-
-#define SMMU_TCR_SH0_SHIFT                  12
-#define SMMU_TCR_SH0_MASK                   0x3
-#define SMMU_TCR_SH_NS                      0
-#define SMMU_TCR_SH_OS                      2
-#define SMMU_TCR_SH_IS                      3
-
-#define SMMU_TCR_ORGN0_SHIFT                10
-#define SMMU_TCR_IRGN0_SHIFT                8
-#define SMMU_TCR_RGN_MASK                   0x3
-#define SMMU_TCR_RGN_NC                     0
-#define SMMU_TCR_RGN_WBWA                   1
-#define SMMU_TCR_RGN_WT                     2
-#define SMMU_TCR_RGN_WB                     3
-
-#define SMMU_TCR_SL0_SHIFT                  6
-#define SMMU_TCR_SL0_MASK                   0x3
-#define SMMU_TCR_SL0_LVL_2                  0
-#define SMMU_TCR_SL0_LVL_1                  1
-
-#define SMMU_TCR_T1SZ_SHIFT                 16
-#define SMMU_TCR_T0SZ_SHIFT                 0
-#define SMMU_TCR_SZ_MASK                    0xf
-
-#define SMMU_TCR2_SEP_SHIFT                 15
-#define SMMU_TCR2_SEP_MASK                  0x7
-
-#define SMMU_TCR2_PASIZE_SHIFT              0
-#define SMMU_TCR2_PASIZE_MASK               0x7
-
-/* Common definitions for PASize and SEP fields */
-#define SMMU_TCR2_ADDR_32                   0
-#define SMMU_TCR2_ADDR_36                   1
-#define SMMU_TCR2_ADDR_40                   2
-#define SMMU_TCR2_ADDR_42                   3
-#define SMMU_TCR2_ADDR_44                   4
-#define SMMU_TCR2_ADDR_48                   5
-
-#define SMMU_TTBRn_HI_ASID_SHIFT            16
-
-#define SMMU_MAIR_ATTR_SHIFT(n)             ((n) << 3)
-#define SMMU_MAIR_ATTR_MASK                 0xff
-#define SMMU_MAIR_ATTR_DEVICE               0x04
-#define SMMU_MAIR_ATTR_NC                   0x44
-#define SMMU_MAIR_ATTR_WBRWA                0xff
-#define SMMU_MAIR_ATTR_IDX_NC               0
-#define SMMU_MAIR_ATTR_IDX_CACHE            1
-#define SMMU_MAIR_ATTR_IDX_DEV              2
-
-#define SMMU_FSR_MULTI                      (1 << 31)
-#define SMMU_FSR_SS                         (1 << 30)
-#define SMMU_FSR_UUT                        (1 << 8)
-#define SMMU_FSR_ASF                        (1 << 7)
-#define SMMU_FSR_TLBLKF                     (1 << 6)
-#define SMMU_FSR_TLBMCF                     (1 << 5)
-#define SMMU_FSR_EF                         (1 << 4)
-#define SMMU_FSR_PF                         (1 << 3)
-#define SMMU_FSR_AFF                        (1 << 2)
-#define SMMU_FSR_TF                         (1 << 1)
-
-#define SMMU_FSR_IGN                        (SMMU_FSR_AFF | SMMU_FSR_ASF |    \
-                                             SMMU_FSR_TLBMCF | SMMU_FSR_TLBLKF)
-#define SMMU_FSR_FAULT                      (SMMU_FSR_MULTI | SMMU_FSR_SS |   \
-                                             SMMU_FSR_UUT | SMMU_FSR_EF |     \
-                                             SMMU_FSR_PF | SMMU_FSR_TF |      \
-                                             SMMU_FSR_IGN)
-
-#define SMMU_FSYNR0_WNR                     (1 << 4)
-
-#define smmu_print(dev, lvl, fmt, ...)                                        \
-    printk(lvl "smmu: %s: " fmt, dt_node_full_name(dev->node), ## __VA_ARGS__)
-
-#define smmu_err(dev, fmt, ...) smmu_print(dev, XENLOG_ERR, fmt, ## __VA_ARGS__)
-
-#define smmu_dbg(dev, fmt, ...)                                             \
-    smmu_print(dev, XENLOG_DEBUG, fmt, ## __VA_ARGS__)
-
-#define smmu_info(dev, fmt, ...)                                            \
-    smmu_print(dev, XENLOG_INFO, fmt, ## __VA_ARGS__)
-
-#define smmu_warn(dev, fmt, ...)                                            \
-    smmu_print(dev, XENLOG_WARNING, fmt, ## __VA_ARGS__)
-
-struct arm_smmu_device {
-    const struct dt_device_node *node;
-
-    void __iomem                *base;
-    unsigned long               size;
-    unsigned long               pagesize;
-
-#define SMMU_FEAT_COHERENT_WALK (1 << 0)
-#define SMMU_FEAT_STREAM_MATCH  (1 << 1)
-#define SMMU_FEAT_TRANS_S1      (1 << 2)
-#define SMMU_FEAT_TRANS_S2      (1 << 3)
-#define SMMU_FEAT_TRANS_NESTED  (1 << 4)
-    u32                         features;
-    u32                         options;
-    int                         version;
-
-    u32                         num_context_banks;
-    u32                         num_s2_context_banks;
-    DECLARE_BITMAP(context_map, SMMU_MAX_CBS);
-    atomic_t                    irptndx;
-
-    u32                         num_mapping_groups;
-    DECLARE_BITMAP(smr_map, SMMU_MAX_SMRS);
-
-    unsigned long               input_size;
-    unsigned long               s1_output_size;
-    unsigned long               s2_output_size;
-
-    u32                         num_global_irqs;
-    u32                         num_context_irqs;
-    unsigned int                *irqs;
-
-    u32                         smr_mask_mask;
-    u32                         smr_id_mask;
-
-    unsigned long               *sids;
-
-    struct list_head            list;
-    struct rb_root              masters;
-};
-
-struct arm_smmu_smr {
-    u8                          idx;
-    u16                         mask;
-    u16                         id;
-};
-
-#define INVALID_IRPTNDX         0xff
-
-#define SMMU_CB_ASID(cfg)       ((cfg)->cbndx)
-#define SMMU_CB_VMID(cfg)       ((cfg)->cbndx + 1)
-
-struct arm_smmu_domain_cfg {
-    struct arm_smmu_device  *smmu;
-    u8                      cbndx;
-    u8                      irptndx;
-    u32                     cbar;
-    /* Domain associated to this device */
-    struct domain           *domain;
-    /* List of master which use this structure */
-    struct list_head        masters;
-
-    /* Used to link domain context for a same domain */
-    struct list_head        list;
-};
-
-struct arm_smmu_master {
-    const struct dt_device_node *dt_node;
-
-    /*
-     * The following is specific to the master's position in the
-     * SMMU chain.
-     */
-    struct rb_node              node;
-    u32                         num_streamids;
-    u16                         streamids[MAX_MASTER_STREAMIDS];
-    int                         num_s2crs;
-
-    struct arm_smmu_smr         *smrs;
-    struct arm_smmu_domain_cfg  *cfg;
-
-    /* Used to link masters in a same domain context */
-    struct list_head            list;
-};
-
-static LIST_HEAD(arm_smmu_devices);
-
-struct arm_smmu_domain {
-    spinlock_t lock;
-    struct list_head contexts;
-};
-
-struct arm_smmu_option_prop {
-    u32         opt;
-    const char  *prop;
-};
-
-static const struct arm_smmu_option_prop arm_smmu_options [] __initconst =
-{
-    { SMMU_OPT_SECURE_CONFIG_ACCESS, "calxeda,smmu-secure-config-access" },
-    { 0, NULL},
-};
-
-static void __init check_driver_options(struct arm_smmu_device *smmu)
-{
-    int i = 0;
-
-    do {
-        if ( dt_property_read_bool(smmu->node, arm_smmu_options[i].prop) )
-        {
-            smmu->options |= arm_smmu_options[i].opt;
-            smmu_dbg(smmu, "option %s\n", arm_smmu_options[i].prop);
-        }
-    } while ( arm_smmu_options[++i].opt );
-}
-
-static void arm_smmu_context_fault(int irq, void *data,
-                                   struct cpu_user_regs *regs)
-{
-    u32 fsr, far, fsynr;
-    uint64_t iova;
-    struct arm_smmu_domain_cfg *cfg = data;
-    struct arm_smmu_device *smmu = cfg->smmu;
-    void __iomem *cb_base;
-
-    cb_base = SMMU_CB_BASE(smmu) + SMMU_CB(smmu, cfg->cbndx);
-    fsr = readl_relaxed(cb_base + SMMU_CB_FSR);
-
-    if ( !(fsr & SMMU_FSR_FAULT) )
-        return;
-
-    if ( fsr & SMMU_FSR_IGN )
-        smmu_err(smmu, "Unexpected context fault (fsr 0x%u)\n", fsr);
-
-    fsynr = readl_relaxed(cb_base + SMMU_CB_FSYNR0);
-    far = readl_relaxed(cb_base + SMMU_CB_FAR_LO);
-    iova = far;
-    far = readl_relaxed(cb_base + SMMU_CB_FAR_HI);
-    iova |= ((uint64_t)far << 32);
-
-    smmu_err(smmu, "Unhandled context fault for domain %u\n",
-             cfg->domain->domain_id);
-    smmu_err(smmu, "\tFSR 0x%x, IOVA 0x%"PRIx64", FSYNR 0x%x,  CB %d\n",
-             fsr, iova, fsynr, cfg->cbndx);
-
-    /* Clear the faulting FSR */
-    writel(fsr, cb_base + SMMU_CB_FSR);
-
-    /* Terminate any stalled transactions */
-    if ( fsr & SMMU_FSR_SS )
-        writel_relaxed(SMMU_RESUME_TERMINATE, cb_base + SMMU_CB_RESUME);
-}
-
-static void arm_smmu_global_fault(int irq, void *data,
-                                  struct cpu_user_regs *regs)
-{
-    u32 gfsr, gfsynr0, gfsynr1, gfsynr2;
-    struct arm_smmu_device *smmu = data;
-    void __iomem *gr0_base = SMMU_GR0_NS(smmu);
-
-    gfsr = readl_relaxed(gr0_base + SMMU_GR0_sGFSR);
-    gfsynr0 = readl_relaxed(gr0_base + SMMU_GR0_sGFSYNR0);
-    gfsynr1 = readl_relaxed(gr0_base + SMMU_GR0_sGFSYNR1);
-    gfsynr2 = readl_relaxed(gr0_base + SMMU_GR0_sGFSYNR2);
-
-    if ( !gfsr )
-        return;
-
-    smmu_err(smmu, "Unexpected global fault, this could be serious\n");
-    smmu_err(smmu,
-             "\tGFSR 0x%08x, GFSYNR0 0x%08x, GFSYNR1 0x%08x, GFSYNR2 0x%08x\n",
-             gfsr, gfsynr0, gfsynr1, gfsynr2);
-    writel(gfsr, gr0_base + SMMU_GR0_sGFSR);
-}
-
-static struct arm_smmu_master *
-find_smmu_master(struct arm_smmu_device *smmu,
-                 const struct dt_device_node *dev_node)
-{
-    struct rb_node *node = smmu->masters.rb_node;
-
-    while ( node )
-    {
-        struct arm_smmu_master *master;
-
-        master = container_of(node, struct arm_smmu_master, node);
-
-        if ( dev_node < master->dt_node )
-            node = node->rb_left;
-        else if ( dev_node > master->dt_node )
-            node = node->rb_right;
-        else
-            return master;
-    }
-
-    return NULL;
-}
-
-static __init int insert_smmu_master(struct arm_smmu_device *smmu,
-                                     struct arm_smmu_master *master)
-{
-    struct rb_node **new, *parent;
-
-    new = &smmu->masters.rb_node;
-    parent = NULL;
-    while ( *new )
-    {
-        struct arm_smmu_master *this;
-
-        this = container_of(*new, struct arm_smmu_master, node);
-
-        parent = *new;
-        if ( master->dt_node < this->dt_node )
-            new = &((*new)->rb_left);
-        else if (master->dt_node > this->dt_node)
-            new = &((*new)->rb_right);
-        else
-            return -EEXIST;
-    }
-
-    rb_link_node(&master->node, parent, new);
-    rb_insert_color(&master->node, &smmu->masters);
-    return 0;
-}
-
-static __init int register_smmu_master(struct arm_smmu_device *smmu,
-                                       struct dt_phandle_args *masterspec)
-{
-    int i, sid;
-    struct arm_smmu_master *master;
-    int rc = 0;
-
-    smmu_dbg(smmu, "Try to add master %s\n", masterspec->np->name);
-
-    master = find_smmu_master(smmu, masterspec->np);
-    if ( master )
-    {
-        smmu_err(smmu,
-                 "rejecting multiple registrations for master device %s\n",
-                 masterspec->np->name);
-        return -EBUSY;
-    }
-
-    if ( masterspec->args_count > MAX_MASTER_STREAMIDS )
-    {
-        smmu_err(smmu,
-            "reached maximum number (%d) of stream IDs for master device %s\n",
-            MAX_MASTER_STREAMIDS, masterspec->np->name);
-        return -ENOSPC;
-    }
-
-    master = xzalloc(struct arm_smmu_master);
-    if ( !master )
-        return -ENOMEM;
-
-    INIT_LIST_HEAD(&master->list);
-    master->dt_node = masterspec->np;
-    master->num_streamids = masterspec->args_count;
-
-    dt_device_set_protected(masterspec->np);
-
-    for ( i = 0; i < master->num_streamids; ++i )
-    {
-        sid = masterspec->args[i];
-        if ( test_and_set_bit(sid, smmu->sids) )
-        {
-            smmu_err(smmu, "duplicate stream ID (%d)\n", sid);
-            xfree(master);
-            return -EEXIST;
-        }
-        master->streamids[i] = masterspec->args[i];
-    }
-
-    rc = insert_smmu_master(smmu, master);
-    /* Insertion should never fail */
-    ASSERT(rc == 0);
-
-    return 0;
-}
-
-static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
-{
-    int idx;
-
-    do
-    {
-        idx = find_next_zero_bit(map, end, start);
-        if ( idx == end )
-            return -ENOSPC;
-    } while ( test_and_set_bit(idx, map) );
-
-    return idx;
-}
-
-static void __arm_smmu_free_bitmap(unsigned long *map, int idx)
-{
-    clear_bit(idx, map);
-}
-
-static void arm_smmu_tlb_sync(struct arm_smmu_device *smmu)
-{
-    int count = 0;
-    void __iomem *gr0_base = SMMU_GR0(smmu);
-
-    writel_relaxed(0, gr0_base + SMMU_GR0_sTLBGSYNC);
-    while ( readl_relaxed(gr0_base + SMMU_GR0_sTLBGSTATUS) &
-            SMMU_sTLBGSTATUS_GSACTIVE )
-    {
-        cpu_relax();
-        if ( ++count == SMMU_TLB_LOOP_TIMEOUT )
-        {
-            smmu_err(smmu, "TLB sync timed out -- SMMU may be deadlocked\n");
-            return;
-        }
-        udelay(1);
-    }
-}
-
-static void arm_smmu_tlb_inv_context(struct arm_smmu_domain_cfg *cfg)
-{
-    struct arm_smmu_device *smmu = cfg->smmu;
-    void __iomem *base = SMMU_GR0(smmu);
-
-    writel_relaxed(SMMU_CB_VMID(cfg),
-                   base + SMMU_GR0_TLBIVMID);
-
-    arm_smmu_tlb_sync(smmu);
-}
-
-static void arm_smmu_iotlb_flush_all(struct domain *d)
-{
-    struct arm_smmu_domain *smmu_domain = domain_hvm_iommu(d)->arch.priv;
-    struct arm_smmu_domain_cfg *cfg;
-
-    spin_lock(&smmu_domain->lock);
-    list_for_each_entry(cfg, &smmu_domain->contexts, list)
-        arm_smmu_tlb_inv_context(cfg);
-    spin_unlock(&smmu_domain->lock);
-}
-
-static void arm_smmu_iotlb_flush(struct domain *d, unsigned long gfn,
-                                 unsigned int page_count)
-{
-    /* ARM SMMU v1 doesn't have flush by VMA and VMID */
-    arm_smmu_iotlb_flush_all(d);
-}
-
-static int determine_smr_mask(struct arm_smmu_device *smmu,
-                              struct arm_smmu_master *master,
-                              struct arm_smmu_smr *smr, int start, int order)
-{
-    u16 i, zero_bits_mask, one_bits_mask, const_mask;
-    int nr;
-
-    nr = 1 << order;
-
-    if ( nr == 1 )
-    {
-        /* no mask, use streamid to match and be done with it */
-        smr->mask = 0;
-        smr->id = master->streamids[start];
-        return 0;
-    }
-
-    zero_bits_mask = 0;
-    one_bits_mask = 0xffff;
-    for ( i = start; i < start + nr; i++)
-    {
-        zero_bits_mask |= master->streamids[i];   /* const 0 bits */
-        one_bits_mask &= master->streamids[i]; /* const 1 bits */
-    }
-    zero_bits_mask = ~zero_bits_mask;
-
-    /* bits having constant values (either 0 or 1) */
-    const_mask = zero_bits_mask | one_bits_mask;
-
-    i = hweight16(~const_mask);
-    if ( (1 << i) == nr )
-    {
-        smr->mask = ~const_mask;
-        smr->id = one_bits_mask;
-    }
-    else
-        /* no usable mask for this set of streamids */
-        return 1;
-
-    if ( ((smr->mask & smmu->smr_mask_mask) != smr->mask) ||
-         ((smr->id & smmu->smr_id_mask) != smr->id) )
-        /* insufficient number of mask/id bits */
-        return 1;
-
-    return 0;
-}
-
-static int determine_smr_mapping(struct arm_smmu_device *smmu,
-                                 struct arm_smmu_master *master,
-                                 struct arm_smmu_smr *smrs, int max_smrs)
-{
-    int nr_sid, nr, i, bit, start;
-
-    /*
-     * This function is called only once -- when a master is added
-     * to a domain. If master->num_s2crs != 0 then this master
-     * was already added to a domain.
-     */
-    BUG_ON(master->num_s2crs);
-
-    start = nr = 0;
-    nr_sid = master->num_streamids;
-    do
-    {
-        /*
-         * largest power-of-2 number of streamids for which to
-         * determine a usable mask/id pair for stream matching
-         */
-        bit = fls(nr_sid);
-        if (!bit)
-            return 0;
-
-        /*
-         * iterate over power-of-2 numbers to determine
-         * largest possible mask/id pair for stream matching
-         * of next 2**i streamids
-         */
-        for ( i = bit - 1; i >= 0; i-- )
-        {
-            if( !determine_smr_mask(smmu, master,
-                                    &smrs[master->num_s2crs],
-                                    start, i))
-                break;
-        }
-
-        if ( i < 0 )
-            goto out;
-
-        nr = 1 << i;
-        nr_sid -= nr;
-        start += nr;
-        master->num_s2crs++;
-    } while ( master->num_s2crs <= max_smrs );
-
-out:
-    if ( nr_sid )
-    {
-        /* not enough mapping groups available */
-        master->num_s2crs = 0;
-        return -ENOSPC;
-    }
-
-    return 0;
-}
-
-static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu,
-                                          struct arm_smmu_master *master)
-{
-    int i, max_smrs, ret;
-    struct arm_smmu_smr *smrs;
-    void __iomem *gr0_base = SMMU_GR0(smmu);
-
-    if ( !(smmu->features & SMMU_FEAT_STREAM_MATCH) )
-        return 0;
-
-    if ( master->smrs )
-        return -EEXIST;
-
-    max_smrs = min(smmu->num_mapping_groups, master->num_streamids);
-    smrs = xmalloc_array(struct arm_smmu_smr, max_smrs);
-    if ( !smrs )
-    {
-        smmu_err(smmu, "failed to allocated %d SMRs for master %s\n",
-                 max_smrs, dt_node_name(master->dt_node));
-        return -ENOMEM;
-    }
-
-    ret = determine_smr_mapping(smmu, master, smrs, max_smrs);
-    if ( ret )
-        goto err_free_smrs;
-
-    /* Allocate the SMRs on the root SMMU */
-    for ( i = 0; i < master->num_s2crs; ++i )
-    {
-        int idx = __arm_smmu_alloc_bitmap(smmu->smr_map, 0,
-                                          smmu->num_mapping_groups);
-        if ( idx < 0 )
-        {
-            smmu_err(smmu, "failed to allocate free SMR\n");
-            goto err_free_bitmap;
-        }
-        smrs[i].idx = idx;
-    }
-
-    /* It worked! Now, poke the actual hardware */
-    for ( i = 0; i < master->num_s2crs; ++i )
-    {
-        u32 reg = SMMU_SMR_VALID | smrs[i].id << SMMU_SMR_ID_SHIFT |
-            smrs[i].mask << SMMU_SMR_MASK_SHIFT;
-        smmu_dbg(smmu, "SMR%d: 0x%x\n", smrs[i].idx, reg);
-        writel_relaxed(reg, gr0_base + SMMU_GR0_SMR(smrs[i].idx));
-    }
-
-    master->smrs = smrs;
-    return 0;
-
-err_free_bitmap:
-    while (--i >= 0)
-        __arm_smmu_free_bitmap(smmu->smr_map, smrs[i].idx);
-    master->num_s2crs = 0;
-err_free_smrs:
-    xfree(smrs);
-    return -ENOSPC;
-}
-
-/* Forward declaration */
-static void arm_smmu_destroy_domain_context(struct arm_smmu_domain_cfg *cfg);
-
-static int arm_smmu_domain_add_master(struct domain *d,
-                                      struct arm_smmu_domain_cfg *cfg,
-                                      struct arm_smmu_master *master)
-{
-    int i, ret;
-    struct arm_smmu_device *smmu = cfg->smmu;
-    void __iomem *gr0_base = SMMU_GR0(smmu);
-    struct arm_smmu_smr *smrs = master->smrs;
-
-    if ( master->cfg )
-        return -EBUSY;
-
-    ret = arm_smmu_master_configure_smrs(smmu, master);
-    if ( ret )
-        return ret;
-
-    /* Now we're at the root, time to point at our context bank */
-    if ( !master->num_s2crs )
-        master->num_s2crs = master->num_streamids;
-
-    for ( i = 0; i < master->num_s2crs; ++i )
-    {
-        u32 idx, s2cr;
-
-        idx = smrs ? smrs[i].idx : master->streamids[i];
-        s2cr = (SMMU_S2CR_TYPE_TRANS << SMMU_S2CR_TYPE_SHIFT) |
-            (cfg->cbndx << SMMU_S2CR_CBNDX_SHIFT);
-        smmu_dbg(smmu, "S2CR%d: 0x%x\n", idx, s2cr);
-        writel_relaxed(s2cr, gr0_base + SMMU_GR0_S2CR(idx));
-    }
-
-    master->cfg = cfg;
-    list_add(&master->list, &cfg->masters);
-
-    return 0;
-}
-
-static void arm_smmu_domain_remove_master(struct arm_smmu_master *master)
-{
-    int i;
-    struct arm_smmu_domain_cfg *cfg = master->cfg;
-    struct arm_smmu_device *smmu = cfg->smmu;
-    void __iomem *gr0_base = SMMU_GR0(smmu);
-    struct arm_smmu_smr *smrs = master->smrs;
-
-    /*
-     * We *must* clear the S2CR first, because freeing the SMR means
-     * that it can be reallocated immediately
-     */
-    for ( i = 0; i < master->num_streamids; ++i )
-    {
-        u16 sid = master->streamids[i];
-        writel_relaxed(SMMU_S2CR_TYPE_FAULT,
-                       gr0_base + SMMU_GR0_S2CR(sid));
-    }
-
-    /* Invalidate the SMRs before freeing back to the allocator */
-    for (i = 0; i < master->num_s2crs; ++i) {
-        u8 idx = smrs[i].idx;
-        writel_relaxed(~SMMU_SMR_VALID, gr0_base + SMMU_GR0_SMR(idx));
-        __arm_smmu_free_bitmap(smmu->smr_map, idx);
-    }
-
-    master->smrs = NULL;
-    master->num_s2crs = 0;
-    xfree(smrs);
-
-    master->cfg = NULL;
-    list_del(&master->list);
-    INIT_LIST_HEAD(&master->list);
-}
-
-static void arm_smmu_init_context_bank(struct arm_smmu_domain_cfg *cfg)
-{
-    u32 reg;
-    struct arm_smmu_device *smmu = cfg->smmu;
-    void __iomem *cb_base, *gr1_base;
-    paddr_t p2maddr;
-
-    ASSERT(cfg->domain != NULL);
-    p2maddr = page_to_maddr(cfg->domain->arch.p2m.root);
-
-    gr1_base = SMMU_GR1(smmu);
-    cb_base = SMMU_CB_BASE(smmu) + SMMU_CB(smmu, cfg->cbndx);
-
-    /* CBAR */
-    reg = cfg->cbar;
-    if ( smmu->version == 1 )
-        reg |= cfg->irptndx << SMMU_CBAR_IRPTNDX_SHIFT;
-
-    reg |= SMMU_CB_VMID(cfg) << SMMU_CBAR_VMID_SHIFT;
-    writel_relaxed(reg, gr1_base + SMMU_GR1_CBAR(cfg->cbndx));
-
-    if ( smmu->version > 1 )
-    {
-        /* CBA2R */
-#ifdef CONFIG_ARM_64
-        reg = SMMU_CBA2R_RW64_64BIT;
-#else
-        reg = SMMU_CBA2R_RW64_32BIT;
-#endif
-        writel_relaxed(reg, gr1_base + SMMU_GR1_CBA2R(cfg->cbndx));
-    }
-
-    /* TTBR0 */
-    reg = (p2maddr & ((1ULL << 32) - 1));
-    writel_relaxed(reg, cb_base + SMMU_CB_TTBR0_LO);
-    reg = (p2maddr >> 32);
-    writel_relaxed(reg, cb_base + SMMU_CB_TTBR0_HI);
-
-    /*
-     * TCR
-     * We use long descriptor, with inner-shareable WBWA tables in TTBR0.
-     */
-    if ( smmu->version > 1 )
-    {
-        /* 4K Page Table */
-        if ( PAGE_SIZE == PAGE_SIZE_4K )
-            reg = SMMU_TCR_TG0_4K;
-        else
-            reg = SMMU_TCR_TG0_64K;
-
-        switch ( smmu->s2_output_size ) {
-        case 32:
-            reg |= (SMMU_TCR2_ADDR_32 << SMMU_TCR_PASIZE_SHIFT);
-            break;
-        case 36:
-            reg |= (SMMU_TCR2_ADDR_36 << SMMU_TCR_PASIZE_SHIFT);
-            break;
-        case 40:
-            reg |= (SMMU_TCR2_ADDR_40 << SMMU_TCR_PASIZE_SHIFT);
-            break;
-        case 42:
-            reg |= (SMMU_TCR2_ADDR_42 << SMMU_TCR_PASIZE_SHIFT);
-            break;
-        case 44:
-            reg |= (SMMU_TCR2_ADDR_44 << SMMU_TCR_PASIZE_SHIFT);
-            break;
-        case 48:
-            reg |= (SMMU_TCR2_ADDR_48 << SMMU_TCR_PASIZE_SHIFT);
-            break;
-        }
-    }
-    else
-        reg = 0;
-
-    /* The attribute to walk the page table should be the same as VTCR_EL2 */
-    reg |= SMMU_TCR_EAE |
-        (SMMU_TCR_SH_IS << SMMU_TCR_SH0_SHIFT) |
-        (SMMU_TCR_RGN_WBWA << SMMU_TCR_ORGN0_SHIFT) |
-        (SMMU_TCR_RGN_WBWA << SMMU_TCR_IRGN0_SHIFT) |
-        (SMMU_TCR_SL0_LVL_1 << SMMU_TCR_SL0_SHIFT) |
-        /* T0SZ=(1)100 = -8 ( 32 -(-8) = 40 bit physical addresses ) */
-        (0x18 << SMMU_TCR_T0SZ_SHIFT);
-    writel_relaxed(reg, cb_base + SMMU_CB_TCR);
-
-    /* SCTLR */
-    reg = SMMU_SCTLR_CFCFG |
-        SMMU_SCTLR_CFIE |
-        SMMU_SCTLR_CFRE |
-        SMMU_SCTLR_M |
-        SMMU_SCTLR_EAE_SBOP;
-
-    writel_relaxed(reg, cb_base + SMMU_CB_SCTLR);
-}
-
-static struct arm_smmu_domain_cfg *
-arm_smmu_alloc_domain_context(struct domain *d,
-                              struct arm_smmu_device *smmu)
-{
-    unsigned int irq;
-    int ret, start;
-    struct arm_smmu_domain_cfg *cfg;
-    struct arm_smmu_domain *smmu_domain = domain_hvm_iommu(d)->arch.priv;
-
-    ASSERT(spin_is_locked(&smmu_domain->lock));
-
-    cfg = xzalloc(struct arm_smmu_domain_cfg);
-    if ( !cfg )
-        return NULL;
-
-    /* Master already initialized to another domain ... */
-    if ( cfg->domain != NULL )
-        goto out_free_mem;
-
-    cfg->cbar = SMMU_CBAR_TYPE_S2_TRANS;
-    start = 0;
-
-    ret = __arm_smmu_alloc_bitmap(smmu->context_map, start,
-                                  smmu->num_context_banks);
-    if ( ret < 0 )
-        goto out_free_mem;
-
-    cfg->cbndx = ret;
-    if ( smmu->version == 1 )
-    {
-        cfg->irptndx = atomic_inc_return(&smmu->irptndx);
-        cfg->irptndx %= smmu->num_context_irqs;
-    }
-    else
-        cfg->irptndx = cfg->cbndx;
-
-    irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx];
-    ret = request_irq(irq, IRQF_SHARED, arm_smmu_context_fault,
-                      "arm-smmu-context-fault", cfg);
-    if ( ret )
-    {
-        smmu_err(smmu, "failed to request context IRQ %d (%u)\n",
-                 cfg->irptndx, irq);
-        cfg->irptndx = INVALID_IRPTNDX;
-        goto out_free_context;
-    }
-
-    cfg->domain = d;
-    cfg->smmu = smmu;
-    if ( smmu->features & SMMU_FEAT_COHERENT_WALK )
-        iommu_set_feature(d, IOMMU_FEAT_COHERENT_WALK);
-
-    arm_smmu_init_context_bank(cfg);
-    list_add(&cfg->list, &smmu_domain->contexts);
-    INIT_LIST_HEAD(&cfg->masters);
-
-    return cfg;
-
-out_free_context:
-    __arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx);
-out_free_mem:
-    xfree(cfg);
-
-    return NULL;
-}
-
-static void arm_smmu_destroy_domain_context(struct arm_smmu_domain_cfg *cfg)
-{
-    struct domain *d = cfg->domain;
-    struct arm_smmu_domain *smmu_domain = domain_hvm_iommu(d)->arch.priv;
-    struct arm_smmu_device *smmu = cfg->smmu;
-    void __iomem *cb_base;
-    unsigned int irq;
-
-    ASSERT(spin_is_locked(&smmu_domain->lock));
-    BUG_ON(!list_empty(&cfg->masters));
-
-    /* Disable the context bank and nuke the TLB before freeing it */
-    cb_base = SMMU_CB_BASE(smmu) + SMMU_CB(smmu, cfg->cbndx);
-    writel_relaxed(0, cb_base + SMMU_CB_SCTLR);
-    arm_smmu_tlb_inv_context(cfg);
-
-    if ( cfg->irptndx != INVALID_IRPTNDX )
-    {
-        irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx];
-        release_irq(irq, cfg);
-    }
-
-    __arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx);
-    list_del(&cfg->list);
-    xfree(cfg);
-}
-
-static struct arm_smmu_device *
-arm_smmu_find_smmu_by_dev(const struct dt_device_node *dev)
-{
-    struct arm_smmu_device *smmu;
-    struct arm_smmu_master *master = NULL;
-
-    list_for_each_entry( smmu, &arm_smmu_devices, list )
-    {
-        master = find_smmu_master(smmu, dev);
-        if ( master )
-            break;
-    }
-
-    if ( !master )
-        return NULL;
-
-    return smmu;
-}
-
-static int arm_smmu_attach_dev(struct domain *d,
-                               const struct dt_device_node *dev)
-{
-    struct arm_smmu_device *smmu = arm_smmu_find_smmu_by_dev(dev);
-    struct arm_smmu_master *master;
-    struct arm_smmu_domain *smmu_domain = domain_hvm_iommu(d)->arch.priv;
-    struct arm_smmu_domain_cfg *cfg = NULL;
-    struct arm_smmu_domain_cfg *curr;
-    int ret;
-
-    printk(XENLOG_DEBUG "arm-smmu: attach %s to domain %d\n",
-           dt_node_full_name(dev), d->domain_id);
-
-    if ( !smmu )
-    {
-        printk(XENLOG_ERR "%s: cannot attach to SMMU, is it on the same bus?\n",
-               dt_node_full_name(dev));
-        return -ENODEV;
-    }
-
-    master = find_smmu_master(smmu, dev);
-    BUG_ON(master == NULL);
-
-    /* Check if the device is already assigned to someone */
-    if ( master->cfg )
-        return -EBUSY;
-
-    spin_lock(&smmu_domain->lock);
-    list_for_each_entry( curr, &smmu_domain->contexts, list )
-    {
-        if ( curr->smmu == smmu )
-        {
-            cfg = curr;
-            break;
-        }
-    }
-
-    if ( !cfg )
-    {
-        cfg = arm_smmu_alloc_domain_context(d, smmu);
-        if ( !cfg )
-        {
-            smmu_err(smmu, "unable to allocate context for domain %u\n",
-                     d->domain_id);
-            spin_unlock(&smmu_domain->lock);
-            return -ENOMEM;
-        }
-    }
-    spin_unlock(&smmu_domain->lock);
-
-    ret = arm_smmu_domain_add_master(d, cfg, master);
-    if ( ret )
-    {
-        spin_lock(&smmu_domain->lock);
-        if ( list_empty(&cfg->masters) )
-            arm_smmu_destroy_domain_context(cfg);
-        spin_unlock(&smmu_domain->lock);
-    }
-
-    return ret;
-}
-
-static int arm_smmu_detach_dev(struct domain *d,
-                               const struct dt_device_node *dev)
-{
-    struct arm_smmu_domain *smmu_domain = domain_hvm_iommu(d)->arch.priv;
-    struct arm_smmu_master *master;
-    struct arm_smmu_device *smmu = arm_smmu_find_smmu_by_dev(dev);
-    struct arm_smmu_domain_cfg *cfg;
-
-    printk(XENLOG_DEBUG "arm-smmu: detach %s to domain %d\n",
-           dt_node_full_name(dev), d->domain_id);
-
-    if ( !smmu )
-    {
-        printk(XENLOG_ERR "%s: cannot find the SMMU, is it on the same bus?\n",
-               dt_node_full_name(dev));
-        return -ENODEV;
-    }
-
-    master = find_smmu_master(smmu, dev);
-    BUG_ON(master == NULL);
-
-    cfg = master->cfg;
-
-    /* Sanity check to avoid removing a device that doesn't belong to
-     * the domain
-     */
-    if ( !cfg || cfg->domain != d )
-    {
-        printk(XENLOG_ERR "%s: was not attach to domain %d\n",
-               dt_node_full_name(dev), d->domain_id);
-        return -ESRCH;
-    }
-
-    arm_smmu_domain_remove_master(master);
-
-    spin_lock(&smmu_domain->lock);
-    if ( list_empty(&cfg->masters) )
-        arm_smmu_destroy_domain_context(cfg);
-    spin_unlock(&smmu_domain->lock);
-
-    return 0;
-}
-
-static int arm_smmu_reassign_dt_dev(struct domain *s, struct domain *t,
-                                    const struct dt_device_node *dev)
-{
-    int ret = 0;
-
-    /* Don't allow remapping on other domain than hwdom */
-    if ( t != hardware_domain )
-        return -EPERM;
-
-    if ( t == s )
-        return 0;
-
-    ret = arm_smmu_detach_dev(s, dev);
-    if ( ret )
-        return ret;
-
-    ret = arm_smmu_attach_dev(t, dev);
-
-    return ret;
-}
-
-static __init int arm_smmu_id_size_to_bits(int size)
-{
-    switch ( size )
-    {
-    case 0:
-        return 32;
-    case 1:
-        return 36;
-    case 2:
-        return 40;
-    case 3:
-        return 42;
-    case 4:
-        return 44;
-    case 5:
-    default:
-        return 48;
-    }
-}
-
-static __init int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
-{
-    unsigned long size;
-    void __iomem *gr0_base = SMMU_GR0(smmu);
-    u32 id;
-
-    smmu_info(smmu, "probing hardware configuration...\n");
-
-    /*
-     * Primecell ID
-     */
-    id = readl_relaxed(gr0_base + SMMU_GR0_PIDR2);
-    smmu->version = ((id >> SMMU_PIDR2_ARCH_SHIFT) & SMMU_PIDR2_ARCH_MASK) + 1;
-    smmu_info(smmu, "SMMUv%d with:\n", smmu->version);
-
-    /* ID0 */
-    id = readl_relaxed(gr0_base + SMMU_GR0_ID0);
-#ifndef CONFIG_ARM_64
-    if ( ((id >> SMMU_ID0_PTFS_SHIFT) & SMMU_ID0_PTFS_MASK) ==
-            SMMU_ID0_PTFS_V8_ONLY )
-    {
-        smmu_err(smmu, "\tno v7 descriptor support!\n");
-        return -ENODEV;
-    }
-#endif
-    if ( id & SMMU_ID0_S1TS )
-    {
-        smmu->features |= SMMU_FEAT_TRANS_S1;
-        smmu_info(smmu, "\tstage 1 translation\n");
-    }
-
-    if ( id & SMMU_ID0_S2TS )
-    {
-        smmu->features |= SMMU_FEAT_TRANS_S2;
-        smmu_info(smmu, "\tstage 2 translation\n");
-    }
-
-    if ( id & SMMU_ID0_NTS )
-    {
-        smmu->features |= SMMU_FEAT_TRANS_NESTED;
-        smmu_info(smmu, "\tnested translation\n");
-    }
-
-    if ( !(smmu->features &
-           (SMMU_FEAT_TRANS_S1 | SMMU_FEAT_TRANS_S2 |
-            SMMU_FEAT_TRANS_NESTED)) )
-    {
-        smmu_err(smmu, "\tno translation support!\n");
-        return -ENODEV;
-    }
-
-    /* We need at least support for Stage 2 */
-    if ( !(smmu->features & SMMU_FEAT_TRANS_S2) )
-    {
-        smmu_err(smmu, "\tno stage 2 translation!\n");
-        return -ENODEV;
-    }
-
-    if ( id & SMMU_ID0_CTTW )
-    {
-        smmu->features |= SMMU_FEAT_COHERENT_WALK;
-        smmu_info(smmu, "\tcoherent table walk\n");
-    }
-
-    if ( id & SMMU_ID0_SMS )
-    {
-        u32 smr, sid, mask;
-
-        smmu->features |= SMMU_FEAT_STREAM_MATCH;
-        smmu->num_mapping_groups = (id >> SMMU_ID0_NUMSMRG_SHIFT) &
-            SMMU_ID0_NUMSMRG_MASK;
-        if ( smmu->num_mapping_groups == 0 )
-        {
-            smmu_err(smmu,
-                     "stream-matching supported, but no SMRs present!\n");
-            return -ENODEV;
-        }
-
-        smr = SMMU_SMR_MASK_MASK << SMMU_SMR_MASK_SHIFT;
-        smr |= (SMMU_SMR_ID_MASK << SMMU_SMR_ID_SHIFT);
-        writel_relaxed(smr, gr0_base + SMMU_GR0_SMR(0));
-        smr = readl_relaxed(gr0_base + SMMU_GR0_SMR(0));
-
-        mask = (smr >> SMMU_SMR_MASK_SHIFT) & SMMU_SMR_MASK_MASK;
-        sid = (smr >> SMMU_SMR_ID_SHIFT) & SMMU_SMR_ID_MASK;
-        if ( (mask & sid) != sid )
-        {
-            smmu_err(smmu,
-                     "SMR mask bits (0x%x) insufficient for ID field (0x%x)\n",
-                     mask, sid);
-            return -ENODEV;
-        }
-        smmu->smr_mask_mask = mask;
-        smmu->smr_id_mask = sid;
-
-        smmu_info(smmu,
-                  "\tstream matching with %u register groups, mask 0x%x\n",
-                  smmu->num_mapping_groups, mask);
-    }
-
-    /* ID1 */
-    id = readl_relaxed(gr0_base + SMMU_GR0_ID1);
-    smmu->pagesize = (id & SMMU_ID1_PAGESIZE) ? PAGE_SIZE_64K : PAGE_SIZE_4K;
-
-    /* Check for size mismatch of SMMU address space from mapped region */
-    size = 1 << (((id >> SMMU_ID1_NUMPAGENDXB_SHIFT) &
-                  SMMU_ID1_NUMPAGENDXB_MASK) + 1);
-    size *= (smmu->pagesize << 1);
-    if ( smmu->size != size )
-        smmu_warn(smmu, "SMMU address space size (0x%lx) differs "
-                  "from mapped region size (0x%lx)!\n", size, smmu->size);
-
-    smmu->num_s2_context_banks = (id >> SMMU_ID1_NUMS2CB_SHIFT) &
-        SMMU_ID1_NUMS2CB_MASK;
-    smmu->num_context_banks = (id >> SMMU_ID1_NUMCB_SHIFT) &
-        SMMU_ID1_NUMCB_MASK;
-    if ( smmu->num_s2_context_banks > smmu->num_context_banks )
-    {
-        smmu_err(smmu, "impossible number of S2 context banks!\n");
-        return -ENODEV;
-    }
-    smmu_info(smmu, "\t%u context banks (%u stage-2 only)\n",
-              smmu->num_context_banks, smmu->num_s2_context_banks);
-
-    /* ID2 */
-    id = readl_relaxed(gr0_base + SMMU_GR0_ID2);
-    size = arm_smmu_id_size_to_bits((id >> SMMU_ID2_IAS_SHIFT) &
-                                    SMMU_ID2_IAS_MASK);
-
-    /*
-     * Stage-1 output limited by stage-2 input size due to VTCR_EL2
-     * setup (see setup_virt_paging)
-     */
-    /* Current maximum output size of 40 bits */
-    smmu->s1_output_size = min(40UL, size);
-
-    /* The stage-2 output mask is also applied for bypass */
-    size = arm_smmu_id_size_to_bits((id >> SMMU_ID2_OAS_SHIFT) &
-                                    SMMU_ID2_OAS_MASK);
-    smmu->s2_output_size = min((unsigned long)PADDR_BITS, size);
-
-    if ( smmu->version == 1 )
-        smmu->input_size = 32;
-    else
-    {
-#ifdef CONFIG_ARM_64
-        size = (id >> SMMU_ID2_UBS_SHIFT) & SMMU_ID2_UBS_MASK;
-        size = min(39, arm_smmu_id_size_to_bits(size));
-#else
-        size = 32;
-#endif
-        smmu->input_size = size;
-
-        if ( (PAGE_SIZE == PAGE_SIZE_4K && !(id & SMMU_ID2_PTFS_4K) ) ||
-             (PAGE_SIZE == PAGE_SIZE_64K && !(id & SMMU_ID2_PTFS_64K)) ||
-             (PAGE_SIZE != PAGE_SIZE_4K && PAGE_SIZE != PAGE_SIZE_64K) )
-        {
-            smmu_err(smmu, "CPU page size 0x%lx unsupported\n",
-                     PAGE_SIZE);
-            return -ENODEV;
-        }
-    }
-
-    smmu_info(smmu, "\t%lu-bit VA, %lu-bit IPA, %lu-bit PA\n",
-              smmu->input_size, smmu->s1_output_size, smmu->s2_output_size);
-    return 0;
-}
-
-static __init void arm_smmu_device_reset(struct arm_smmu_device *smmu)
-{
-    void __iomem *gr0_base = SMMU_GR0(smmu);
-    void __iomem *cb_base;
-    int i = 0;
-    u32 reg;
-
-    smmu_dbg(smmu, "device reset\n");
-
-    /* Clear Global FSR */
-    reg = readl_relaxed(SMMU_GR0_NS(smmu) + SMMU_GR0_sGFSR);
-    writel(reg, SMMU_GR0_NS(smmu) + SMMU_GR0_sGFSR);
-
-    /* Mark all SMRn as invalid and all S2CRn as fault */
-    for ( i = 0; i < smmu->num_mapping_groups; ++i )
-    {
-        writel_relaxed(~SMMU_SMR_VALID, gr0_base + SMMU_GR0_SMR(i));
-        writel_relaxed(SMMU_S2CR_TYPE_FAULT, gr0_base + SMMU_GR0_S2CR(i));
-    }
-
-    /* Make sure all context banks are disabled and clear CB_FSR  */
-    for ( i = 0; i < smmu->num_context_banks; ++i )
-    {
-        cb_base = SMMU_CB_BASE(smmu) + SMMU_CB(smmu, i);
-        writel_relaxed(0, cb_base + SMMU_CB_SCTLR);
-        writel_relaxed(SMMU_FSR_FAULT, cb_base + SMMU_CB_FSR);
-    }
-
-    /* Invalidate the TLB, just in case */
-    writel_relaxed(0, gr0_base + SMMU_GR0_STLBIALL);
-    writel_relaxed(0, gr0_base + SMMU_GR0_TLBIALLH);
-    writel_relaxed(0, gr0_base + SMMU_GR0_TLBIALLNSNH);
-
-    reg = readl_relaxed(SMMU_GR0_NS(smmu) + SMMU_GR0_sCR0);
-
-    /* Enable fault reporting */
-    reg |= (SMMU_sCR0_GFRE | SMMU_sCR0_GFIE |
-            SMMU_sCR0_GCFGFRE | SMMU_sCR0_GCFGFIE);
-
-    /* Disable TLB broadcasting. */
-    reg |= (SMMU_sCR0_VMIDPNE | SMMU_sCR0_PTM);
-
-    /* Enable client access, generate a fault if no mapping is found */
-    reg &= ~(SMMU_sCR0_CLIENTPD);
-    reg |= SMMU_sCR0_USFCFG;
-
-    /* Disable forced broadcasting */
-    reg &= ~SMMU_sCR0_FB;
-
-    /* Don't upgrade barriers when client devices are not mapped to
-     * a translation context banks (just here for clarity as Xen policy
-     * is to deny invalid transaction). */
-    reg &= ~(SMMU_sCR0_BSU_MASK << SMMU_sCR0_BSU_SHIFT);
-
-    /* Push the button */
-    arm_smmu_tlb_sync(smmu);
-    writel_relaxed(reg, SMMU_GR0_NS(smmu) + SMMU_GR0_sCR0);
-}
-
-static int arm_smmu_iommu_domain_init(struct domain *d)
-{
-    struct arm_smmu_domain *smmu_domain;
-
-    smmu_domain = xzalloc(struct arm_smmu_domain);
-    if ( !smmu_domain )
-        return -ENOMEM;
-
-    spin_lock_init(&smmu_domain->lock);
-    INIT_LIST_HEAD(&smmu_domain->contexts);
-
-    domain_hvm_iommu(d)->arch.priv = smmu_domain;
-
-    return 0;
-}
-
-static void __hwdom_init arm_smmu_iommu_hwdom_init(struct domain *d)
-{
-}
-
-static void arm_smmu_iommu_domain_teardown(struct domain *d)
-{
-    struct arm_smmu_domain *smmu_domain = domain_hvm_iommu(d)->arch.priv;
-
-    ASSERT(list_empty(&smmu_domain->contexts));
-    xfree(smmu_domain);
-}
-
-static int arm_smmu_map_page(struct domain *d, unsigned long gfn,
-                             unsigned long mfn, unsigned int flags)
-{
-    p2m_type_t t;
-
-    /* Grant mappings can be used for DMA requests. The dev_bus_addr returned by
-     * the hypercall is the MFN (not the IPA). For device protected by
-     * an IOMMU, Xen needs to add a 1:1 mapping in the domain p2m to
-     * allow DMA request to work.
-     * This is only valid when the domain is directed mapped. Hence this
-     * function should only be used by gnttab code with gfn == mfn.
-     */
-    BUG_ON(!is_domain_direct_mapped(d));
-    BUG_ON(mfn != gfn);
-
-    /* We only support readable and writable flags */
-    if ( !(flags & (IOMMUF_readable | IOMMUF_writable)) )
-        return -EINVAL;
-
-    t = (flags & IOMMUF_writable) ? p2m_iommu_map_rw : p2m_iommu_map_ro;
-
-    /* The function guest_physmap_add_entry replaces the current mapping
-     * if there is already one...
-     */
-    return guest_physmap_add_entry(d, gfn, mfn, 0, t);
-}
-
-static int arm_smmu_unmap_page(struct domain *d, unsigned long gfn)
-{
-    /* This function should only be used by gnttab code when the domain
-     * is direct mapped
-     */
-    if ( !is_domain_direct_mapped(d) )
-        return -EINVAL;
-
-    guest_physmap_remove_page(d, gfn, gfn, 0);
-
-    return 0;
-}
-
-static const struct iommu_ops arm_smmu_iommu_ops = {
-    .init = arm_smmu_iommu_domain_init,
-    .hwdom_init = arm_smmu_iommu_hwdom_init,
-    .teardown = arm_smmu_iommu_domain_teardown,
-    .iotlb_flush = arm_smmu_iotlb_flush,
-    .iotlb_flush_all = arm_smmu_iotlb_flush_all,
-    .assign_dt_device = arm_smmu_attach_dev,
-    .reassign_dt_device = arm_smmu_reassign_dt_dev,
-    .map_page = arm_smmu_map_page,
-    .unmap_page = arm_smmu_unmap_page,
-};
-
-static int __init smmu_init(struct dt_device_node *dev,
-                            const void *data)
-{
-    struct arm_smmu_device *smmu;
-    int res;
-    u64 addr, size;
-    unsigned int num_irqs, i;
-    struct dt_phandle_args masterspec;
-    struct rb_node *node;
-
-    /* Even if the device can't be initialized, we don't want to give
-     * the smmu device to dom0.
-     */
-    dt_device_set_used_by(dev, DOMID_XEN);
-
-    smmu = xzalloc(struct arm_smmu_device);
-    if ( !smmu )
-    {
-        printk(XENLOG_ERR "%s: failed to allocate arm_smmu_device\n",
-               dt_node_full_name(dev));
-        return -ENOMEM;
-    }
-
-    smmu->node = dev;
-    check_driver_options(smmu);
-
-    res = dt_device_get_address(smmu->node, 0, &addr, &size);
-    if ( res )
-    {
-        smmu_err(smmu, "unable to retrieve the base address of the SMMU\n");
-        goto out_err;
-    }
-
-    smmu->base = ioremap_nocache(addr, size);
-    if ( !smmu->base )
-    {
-        smmu_err(smmu, "unable to map the SMMU memory\n");
-        goto out_err;
-    }
-
-    smmu->size = size;
-
-    if ( !dt_property_read_u32(smmu->node, "#global-interrupts",
-                               &smmu->num_global_irqs) )
-    {
-        smmu_err(smmu, "missing #global-interrupts\n");
-        goto out_unmap;
-    }
-
-    num_irqs = dt_number_of_irq(smmu->node);
-    if ( num_irqs > smmu->num_global_irqs )
-        smmu->num_context_irqs = num_irqs - smmu->num_global_irqs;
-
-    if ( !smmu->num_context_irqs )
-    {
-        smmu_err(smmu, "found %d interrupts but expected at least %d\n",
-                 num_irqs, smmu->num_global_irqs + 1);
-        goto out_unmap;
-    }
-
-    smmu->irqs = xzalloc_array(unsigned int, num_irqs);
-    if ( !smmu->irqs )
-    {
-        smmu_err(smmu, "failed to allocated %d irqs\n", num_irqs);
-        goto out_unmap;
-    }
-
-    for ( i = 0; i < num_irqs; i++ )
-    {
-        res = platform_get_irq(smmu->node, i);
-        if ( res < 0 )
-        {
-            smmu_err(smmu, "failed to get irq index %d\n", i);
-            goto out_free_irqs;
-        }
-        smmu->irqs[i] = res;
-    }
-
-    smmu->sids = xzalloc_array(unsigned long,
-                               BITS_TO_LONGS(SMMU_MAX_STREAMIDS));
-    if ( !smmu->sids )
-    {
-        smmu_err(smmu, "failed to allocated bitmap for stream ID tracking\n");
-        goto out_free_masters;
-    }
-
-
-    i = 0;
-    smmu->masters = RB_ROOT;
-    while ( !dt_parse_phandle_with_args(smmu->node, "mmu-masters",
-                                        "#stream-id-cells", i, &masterspec) )
-    {
-        res = register_smmu_master(smmu, &masterspec);
-        if ( res )
-        {
-            smmu_err(smmu, "failed to add master %s\n",
-                     masterspec.np->name);
-            goto out_free_masters;
-        }
-        i++;
-    }
-
-    smmu_info(smmu, "registered %d master devices\n", i);
-
-    res = arm_smmu_device_cfg_probe(smmu);
-    if ( res )
-    {
-        smmu_err(smmu, "failed to probe the SMMU\n");
-        goto out_free_masters;
-    }
-
-    if ( smmu->version > 1 &&
-         smmu->num_context_banks != smmu->num_context_irqs )
-    {
-        smmu_err(smmu,
-                 "found only %d context interrupt(s) but %d required\n",
-                 smmu->num_context_irqs, smmu->num_context_banks);
-        goto out_free_masters;
-    }
-
-    smmu_dbg(smmu, "register global IRQs handler\n");
-
-    for ( i = 0; i < smmu->num_global_irqs; ++i )
-    {
-        smmu_dbg(smmu, "\t- global IRQ %u\n", smmu->irqs[i]);
-        res = request_irq(smmu->irqs[i], IRQF_SHARED, arm_smmu_global_fault,
-                          "arm-smmu global fault", smmu);
-        if ( res )
-        {
-            smmu_err(smmu, "failed to request global IRQ %d (%u)\n",
-                     i, smmu->irqs[i]);
-            goto out_release_irqs;
-        }
-    }
-
-    INIT_LIST_HEAD(&smmu->list);
-    list_add(&smmu->list, &arm_smmu_devices);
-
-    arm_smmu_device_reset(smmu);
-
-    iommu_set_ops(&arm_smmu_iommu_ops);
-
-    /* sids field can be freed... */
-    xfree(smmu->sids);
-    smmu->sids = NULL;
-
-    return 0;
-
-out_release_irqs:
-    while (i--)
-        release_irq(smmu->irqs[i], smmu);
-
-out_free_masters:
-    for ( node = rb_first(&smmu->masters); node; node = rb_next(node) )
-    {
-        struct arm_smmu_master *master;
-
-        master = container_of(node, struct arm_smmu_master, node);
-        xfree(master);
-    }
-
-    xfree(smmu->sids);
-
-out_free_irqs:
-    xfree(smmu->irqs);
-
-out_unmap:
-    iounmap(smmu->base);
-
-out_err:
-    xfree(smmu);
-
-    return -ENODEV;
-}
-
-static const char * const smmu_dt_compat[] __initconst =
-{
-    "arm,mmu-400",
-    NULL
-};
-
-DT_DEVICE_START(smmu, "ARM SMMU", DEVICE_IOMMU)
-    .compatible = smmu_dt_compat,
-    .init = smmu_init,
-DT_DEVICE_END
-
-/*
- * Local variables:
- * mode: C
- * c-file-style: "BSD"
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- */
-- 
2.1.3

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

* [PATCH for 4.6 07/13] xen: Introduce a generic way to describe device
  2014-12-16 20:08 [PATCH for 4.6 00/13] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (5 preceding siblings ...)
  2014-12-16 20:08 ` [PATCH for 4.6 06/13] xen/iommu: arm: Remove temporary the SMMU driver Julien Grall
@ 2014-12-16 20:08 ` Julien Grall
  2014-12-17 10:16   ` Jan Beulich
  2014-12-18  1:12   ` Zhang, Yang Z
  2014-12-16 20:08 ` [PATCH for 4.6 08/13] xen/iommu: Consolidate device assignment ops into a single set Julien Grall
                   ` (5 subsequent siblings)
  12 siblings, 2 replies; 41+ messages in thread
From: Julien Grall @ 2014-12-16 20:08 UTC (permalink / raw)
  To: xen-devel
  Cc: Keir Fraser, ian.campbell, manish.jaggi, Julien Grall, tim,
	stefano.stabellini, Jan Beulich

Currently, Xen is supporting PCI and Platform device (based on Device Tree).

While we don't support both at the same time: platform device for ARM
and PCI for x86, ARM will gain support on PCI soon.

Some drivers, such as IOMMU drivers, may handle PCI and platform device in
the same way. Only few lines of code differs.

Rather than requesting to provide 2 set of functions (one for PCI and
one for platform device), introduce a generic structure "device" which
is embedded in each specialized device.

At the same time replace any use of asm/device.h by xen/device.h. This
is required to be able to compile ARM correctly.

Signed-off-by: Julien Grall <julien.grall@linaro.org>
CC: Jan Beulich <jbeulich@suse.com>
CC: Keir Fraser <keir@xen.org>
---
 xen/arch/arm/device.c               |  2 +-
 xen/arch/arm/domain_build.c         |  2 +-
 xen/arch/arm/gic-v2.c               |  3 +--
 xen/arch/arm/gic-v3.c               |  2 +-
 xen/arch/arm/gic.c                  |  2 +-
 xen/common/Makefile                 |  1 +
 xen/common/device.c                 | 21 +++++++++++++++++++
 xen/common/device_tree.c            |  1 +
 xen/drivers/char/dt-uart.c          |  4 ++--
 xen/drivers/char/exynos4210-uart.c  |  2 +-
 xen/drivers/char/ns16550.c          |  2 +-
 xen/drivers/char/omap-uart.c        |  2 +-
 xen/drivers/char/pl011.c            |  2 +-
 xen/drivers/passthrough/arm/iommu.c |  2 +-
 xen/drivers/passthrough/pci.c       |  2 ++
 xen/include/asm-arm/device.h        |  3 ++-
 xen/include/asm-x86/device.h        | 17 ++++++++++++++++
 xen/include/xen/device.h            | 40 +++++++++++++++++++++++++++++++++++++
 xen/include/xen/device_tree.h       | 13 ++++++++++++
 xen/include/xen/pci.h               | 12 +++++++++++
 20 files changed, 121 insertions(+), 14 deletions(-)
 create mode 100644 xen/common/device.c
 create mode 100644 xen/include/asm-x86/device.h
 create mode 100644 xen/include/xen/device.h

diff --git a/xen/arch/arm/device.c b/xen/arch/arm/device.c
index 693b9af..de702ff 100644
--- a/xen/arch/arm/device.c
+++ b/xen/arch/arm/device.c
@@ -17,7 +17,7 @@
  * GNU General Public License for more details.
  */
 
-#include <asm/device.h>
+#include <xen/device.h>
 #include <xen/errno.h>
 #include <xen/lib.h>
 
diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c
index de180d8..b701a2f 100644
--- a/xen/arch/arm/domain_build.c
+++ b/xen/arch/arm/domain_build.c
@@ -9,10 +9,10 @@
 #include <asm/regs.h>
 #include <xen/errno.h>
 #include <xen/device_tree.h>
+#include <xen/device.h>
 #include <xen/libfdt/libfdt.h>
 #include <xen/guest_access.h>
 #include <xen/iocap.h>
-#include <asm/device.h>
 #include <asm/setup.h>
 #include <asm/platform.h>
 #include <asm/psci.h>
diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c
index f149e09..048350b 100644
--- a/xen/arch/arm/gic-v2.c
+++ b/xen/arch/arm/gic-v2.c
@@ -27,12 +27,11 @@
 #include <xen/softirq.h>
 #include <xen/list.h>
 #include <xen/device_tree.h>
+#include <xen/device.h>
 #include <xen/libfdt/libfdt.h>
 #include <asm/p2m.h>
 #include <asm/domain.h>
 #include <asm/platform.h>
-#include <asm/device.h>
-
 #include <asm/io.h>
 #include <asm/gic.h>
 
diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
index 076aa62..c6d1876 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -31,12 +31,12 @@
 #include <xen/errno.h>
 #include <xen/delay.h>
 #include <xen/device_tree.h>
+#include <xen/device.h>
 #include <xen/sizes.h>
 #include <xen/libfdt/libfdt.h>
 #include <asm/p2m.h>
 #include <asm/domain.h>
 #include <asm/io.h>
-#include <asm/device.h>
 #include <asm/gic.h>
 #include <asm/gic_v3_defs.h>
 #include <asm/cpufeature.h>
diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
index e7a1af5..d1ab6b5 100644
--- a/xen/arch/arm/gic.c
+++ b/xen/arch/arm/gic.c
@@ -27,10 +27,10 @@
 #include <xen/softirq.h>
 #include <xen/list.h>
 #include <xen/device_tree.h>
+#include <xen/device.h>
 #include <asm/p2m.h>
 #include <asm/domain.h>
 #include <asm/platform.h>
-#include <asm/device.h>
 #include <asm/io.h>
 #include <asm/gic.h>
 #include <asm/vgic.h>
diff --git a/xen/common/Makefile b/xen/common/Makefile
index 8391246..03ed719 100644
--- a/xen/common/Makefile
+++ b/xen/common/Makefile
@@ -2,6 +2,7 @@ obj-y += bitmap.o
 obj-y += core_parking.o
 obj-y += cpu.o
 obj-y += cpupool.o
+obj-y += device.o
 obj-$(HAS_DEVICE_TREE) += device_tree.o
 obj-y += domctl.o
 obj-y += domain.o
diff --git a/xen/common/device.c b/xen/common/device.c
new file mode 100644
index 0000000..3450f20
--- /dev/null
+++ b/xen/common/device.c
@@ -0,0 +1,21 @@
+#include <xen/types.h>
+#include <xen/device.h>
+
+void device_initialize(struct device *dev, enum device_type type)
+{
+    dev->type = type;
+
+#ifdef HAS_DEVICE_TREE
+    if ( type == DEV_DT )
+        dev->of_node = dev_to_dt(dev);
+#endif
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c
index 34a1b9e..f471008 100644
--- a/xen/common/device_tree.c
+++ b/xen/common/device_tree.c
@@ -1450,6 +1450,7 @@ static unsigned long __init unflatten_dt_node(const void *fdt,
             prev_pp = &pp->next;
 #endif
             np->name = pp->value;
+            device_initialize(&np->dev, DEV_DT);
             memcpy(pp->value, ps, sz - 1);
             ((char *)pp->value)[sz - 1] = 0;
             dt_dprintk("fixed up name for %s -> %s\n", pathp,
diff --git a/xen/drivers/char/dt-uart.c b/xen/drivers/char/dt-uart.c
index fa92b5c..01eced1 100644
--- a/xen/drivers/char/dt-uart.c
+++ b/xen/drivers/char/dt-uart.c
@@ -17,11 +17,11 @@
  * GNU General Public License for more details.
  */
 
-#include <asm/device.h>
-#include <asm/types.h>
+#include <xen/device.h>
 #include <xen/console.h>
 #include <xen/device_tree.h>
 #include <xen/serial.h>
+#include <asm/types.h>
 
 /*
  * Configure UART port with a string:
diff --git a/xen/drivers/char/exynos4210-uart.c b/xen/drivers/char/exynos4210-uart.c
index cba8729..75246e1 100644
--- a/xen/drivers/char/exynos4210-uart.c
+++ b/xen/drivers/char/exynos4210-uart.c
@@ -24,7 +24,7 @@
 #include <xen/init.h>
 #include <xen/irq.h>
 #include <xen/mm.h>
-#include <asm/device.h>
+#include <xen/device.h>
 #include <asm/exynos4210-uart.h>
 #include <asm/io.h>
 
diff --git a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c
index 161b251..6df3f95 100644
--- a/xen/drivers/char/ns16550.c
+++ b/xen/drivers/char/ns16550.c
@@ -25,7 +25,7 @@
 #include <xen/vmap.h>
 #include <asm/io.h>
 #ifdef HAS_DEVICE_TREE
-#include <asm/device.h>
+#include <xen/device.h>
 #endif
 #ifdef CONFIG_X86
 #include <asm/fixmap.h>
diff --git a/xen/drivers/char/omap-uart.c b/xen/drivers/char/omap-uart.c
index 16d1454..c4cd442 100644
--- a/xen/drivers/char/omap-uart.c
+++ b/xen/drivers/char/omap-uart.c
@@ -16,7 +16,7 @@
 #include <xen/init.h>
 #include <xen/irq.h>
 #include <xen/device_tree.h>
-#include <asm/device.h>
+#include <xen/device.h>
 #include <xen/errno.h>
 #include <xen/mm.h>
 #include <xen/vmap.h>
diff --git a/xen/drivers/char/pl011.c b/xen/drivers/char/pl011.c
index dd19ce8..cc91224 100644
--- a/xen/drivers/char/pl011.c
+++ b/xen/drivers/char/pl011.c
@@ -23,8 +23,8 @@
 #include <xen/init.h>
 #include <xen/irq.h>
 #include <xen/device_tree.h>
+#include <xen/device.h>
 #include <xen/errno.h>
-#include <asm/device.h>
 #include <xen/mm.h>
 #include <xen/vmap.h>
 #include <asm/pl011-uart.h>
diff --git a/xen/drivers/passthrough/arm/iommu.c b/xen/drivers/passthrough/arm/iommu.c
index 3007b99..3e9303a 100644
--- a/xen/drivers/passthrough/arm/iommu.c
+++ b/xen/drivers/passthrough/arm/iommu.c
@@ -18,7 +18,7 @@
 #include <xen/lib.h>
 #include <xen/iommu.h>
 #include <xen/device_tree.h>
-#include <asm/device.h>
+#include <xen/device.h>
 
 static const struct iommu_ops *iommu_ops;
 
diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c
index 78c6977..9fbd2a2 100644
--- a/xen/drivers/passthrough/pci.c
+++ b/xen/drivers/passthrough/pci.c
@@ -278,6 +278,8 @@ static struct pci_dev *alloc_pdev(struct pci_seg *pseg, u8 bus, u8 devfn)
     if ( !pdev )
         return NULL;
 
+    device_initialize(&pdev->dev, DEV_PCI);
+
     *(u16*) &pdev->seg = pseg->nr;
     *((u8*) &pdev->bus) = bus;
     *((u8*) &pdev->devfn) = devfn;
diff --git a/xen/include/asm-arm/device.h b/xen/include/asm-arm/device.h
index 72a9028..fdcd097 100644
--- a/xen/include/asm-arm/device.h
+++ b/xen/include/asm-arm/device.h
@@ -2,7 +2,8 @@
 #define __ASM_ARM_DEVICE_H
 
 #include <xen/init.h>
-#include <xen/device_tree.h>
+
+struct dt_device_node;
 
 enum device_match
 {
diff --git a/xen/include/asm-x86/device.h b/xen/include/asm-x86/device.h
new file mode 100644
index 0000000..9d4c352
--- /dev/null
+++ b/xen/include/asm-x86/device.h
@@ -0,0 +1,17 @@
+#ifndef __ASM_X86_DEVICE_H
+#define __ASM_X86_DEVICE_H
+
+struct dev_archdata {
+    /* No device specific arch data */
+};
+
+#endif /* __ASM_X86_DEVICE_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/include/xen/device.h b/xen/include/xen/device.h
new file mode 100644
index 0000000..a9e20ad
--- /dev/null
+++ b/xen/include/xen/device.h
@@ -0,0 +1,40 @@
+#ifndef __XEN_DEVICE_H__
+#define __XEN_DEVICE_H__
+
+#include <asm/device.h>
+
+enum device_type
+{
+    DEV_PCI,
+    DEV_DT,
+};
+
+/* struct device - The basic device structure */
+struct device
+{
+    enum device_type type;
+#ifdef HAS_DEVICE_TREE
+    struct dt_device_node *of_node; /* Used by drivers imported from Linux */
+#endif
+    struct dev_archdata archdata;
+};
+
+#define dev_is_pci(dev) ((dev)->type == DEV_PCI)
+#define dev_is_dt(dev)  ((dev->type == DEV_DT)
+
+#ifdef HAS_DEVICE_TREE
+#include <xen/device_tree.h>
+#endif
+
+void device_initialize(struct device *dev, enum device_type type);
+
+#endif /* __XEN_DEVICE_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/include/xen/device_tree.h b/xen/include/xen/device_tree.h
index 6502369..890d356 100644
--- a/xen/include/xen/device_tree.h
+++ b/xen/include/xen/device_tree.h
@@ -12,6 +12,8 @@
 
 #include <asm/byteorder.h>
 #include <public/xen.h>
+#include <xen/device.h>
+#include <xen/kernel.h>
 #include <xen/init.h>
 #include <xen/string.h>
 #include <xen/types.h>
@@ -80,8 +82,19 @@ struct dt_device_node {
     /* IOMMU specific fields */
     bool is_protected;
     struct list_head domain_list;
+
+    struct device dev;
 };
 
+#define dt_to_dev(dt_node)  (&(dt_node)->dev)
+
+static inline struct dt_device_node *dev_to_dt(struct device *dev)
+{
+    ASSERT(dev->type == DEV_DT);
+
+    return container_of(dev, struct dt_device_node, dev);
+}
+
 #define MAX_PHANDLE_ARGS 16
 struct dt_phandle_args {
     struct dt_device_node *np;
diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h
index 5f295f3..6ace79d 100644
--- a/xen/include/xen/pci.h
+++ b/xen/include/xen/pci.h
@@ -13,6 +13,7 @@
 #include <xen/irq.h>
 #include <xen/pci_regs.h>
 #include <xen/pfn.h>
+#include <xen/device.h>
 #include <asm/pci.h>
 
 /*
@@ -75,8 +76,19 @@ struct pci_dev {
 #define PT_FAULT_THRESHOLD 10
     } fault;
     u64 vf_rlen[6];
+
+    struct device dev;
 };
 
+#define pci_to_dev(pcidev)  (&(pcidev)->dev)
+
+static inline struct pci_dev *dev_to_pci(struct device *dev)
+{
+    ASSERT(dev->type == DEV_PCI);
+
+    return container_of(dev, struct pci_dev, dev);
+}
+
 #define for_each_pdev(domain, pdev) \
     list_for_each_entry(pdev, &(domain->arch.pdev_list), domain_list)
 
-- 
2.1.3

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

* [PATCH for 4.6 08/13] xen/iommu: Consolidate device assignment ops into a single set
  2014-12-16 20:08 [PATCH for 4.6 00/13] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (6 preceding siblings ...)
  2014-12-16 20:08 ` [PATCH for 4.6 07/13] xen: Introduce a generic way to describe device Julien Grall
@ 2014-12-16 20:08 ` Julien Grall
  2014-12-17 10:20   ` Jan Beulich
  2014-12-16 20:08 ` [PATCH for 4.6 09/13] xen/arm: Describe device supported by a driver with dt_match_node Julien Grall
                   ` (4 subsequent siblings)
  12 siblings, 1 reply; 41+ messages in thread
From: Julien Grall @ 2014-12-16 20:08 UTC (permalink / raw)
  To: xen-devel
  Cc: Kevin Tian, ian.campbell, manish.jaggi, Julien Grall, tim,
	Jan Beulich, stefano.stabellini, Suravee Suthikulpanit,
	Yang Zhang, Aravind Gopalakrishnan

On ARM, the way to assign device tree node is exactly the same as PCI.
Futhermore, all devices can be represented by a "struct device'.
Therefore there is no need to add separate ops.

Signed-off-by: Julien Grall <julien.grall@linaro.org>
CC: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
CC: Aravind Gopalakrishnan <Aravind.Gopalakrishnan@amd.com>
CC: Jan Beulich <jbeulich@suse.com>
CC: Yang Zhang <yang.z.zhang@intel.com>
CC: Kevin Tian <kevin.tian@intel.com>
---
 xen/drivers/passthrough/amd/pci_amd_iommu.c | 14 +++++++++-----
 xen/drivers/passthrough/device_tree.c       |  5 +++--
 xen/drivers/passthrough/pci.c               | 20 +++++++++++---------
 xen/drivers/passthrough/vtd/iommu.c         | 19 ++++++++++++-------
 xen/include/xen/iommu.h                     | 18 +++++++-----------
 5 files changed, 42 insertions(+), 34 deletions(-)

diff --git a/xen/drivers/passthrough/amd/pci_amd_iommu.c b/xen/drivers/passthrough/amd/pci_amd_iommu.c
index e83bb35..0af13fb 100644
--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c
+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c
@@ -355,8 +355,9 @@ void amd_iommu_disable_domain_device(struct domain *domain,
 }
 
 static int reassign_device(struct domain *source, struct domain *target,
-                           u8 devfn, struct pci_dev *pdev)
+                           u8 devfn, struct device *dev)
 {
+    struct pci_dev *pdev = dev_to_pci(dev);
     struct amd_iommu *iommu;
     int bdf;
     struct hvm_iommu *t = domain_hvm_iommu(target);
@@ -394,8 +395,9 @@ static int reassign_device(struct domain *source, struct domain *target,
 }
 
 static int amd_iommu_assign_device(struct domain *d, u8 devfn,
-                                   struct pci_dev *pdev)
+                                   struct device *dev)
 {
+    struct pci_dev *pdev = dev_to_pci(dev);
     struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg);
     int bdf = PCI_BDF2(pdev->bus, devfn);
     int req_id = get_dma_requestor_id(pdev->seg, bdf);
@@ -410,7 +412,7 @@ static int amd_iommu_assign_device(struct domain *d, u8 devfn,
             ivrs_mappings[req_id].read_permission);
     }
 
-    return reassign_device(hardware_domain, d, devfn, pdev);
+    return reassign_device(hardware_domain, d, devfn, dev);
 }
 
 static void deallocate_next_page_table(struct page_info *pg, int level)
@@ -481,8 +483,9 @@ static void amd_iommu_domain_destroy(struct domain *d)
     amd_iommu_flush_all_pages(d);
 }
 
-static int amd_iommu_add_device(u8 devfn, struct pci_dev *pdev)
+static int amd_iommu_add_device(u8 devfn, struct device *dev)
 {
+    struct pci_dev *pdev = dev_to_pci(dev);
     struct amd_iommu *iommu;
     u16 bdf;
     if ( !pdev->domain )
@@ -503,8 +506,9 @@ static int amd_iommu_add_device(u8 devfn, struct pci_dev *pdev)
     return 0;
 }
 
-static int amd_iommu_remove_device(u8 devfn, struct pci_dev *pdev)
+static int amd_iommu_remove_device(u8 devfn, struct device *dev)
 {
+    struct pci_dev *pdev = dev_to_pci(dev);
     struct amd_iommu *iommu;
     u16 bdf;
     if ( !pdev->domain )
diff --git a/xen/drivers/passthrough/device_tree.c b/xen/drivers/passthrough/device_tree.c
index 3e47df5..377d41d 100644
--- a/xen/drivers/passthrough/device_tree.c
+++ b/xen/drivers/passthrough/device_tree.c
@@ -41,7 +41,7 @@ int iommu_assign_dt_device(struct domain *d, struct dt_device_node *dev)
     if ( !list_empty(&dev->domain_list) )
         goto fail;
 
-    rc = hd->platform_ops->assign_dt_device(d, dev);
+    rc = hd->platform_ops->assign_device(d, 0, dt_to_dev(dev));
 
     if ( rc )
         goto fail;
@@ -68,7 +68,8 @@ int iommu_deassign_dt_device(struct domain *d, struct dt_device_node *dev)
 
     spin_lock(&dtdevs_lock);
 
-    rc = hd->platform_ops->reassign_dt_device(d, hardware_domain, dev);
+    rc = hd->platform_ops->reassign_device(d, hardware_domain,
+                                           0, dt_to_dev(dev));
     if ( rc )
         goto fail;
 
diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c
index 9fbd2a2..43ce5dc 100644
--- a/xen/drivers/passthrough/pci.c
+++ b/xen/drivers/passthrough/pci.c
@@ -1256,7 +1256,7 @@ int iommu_add_device(struct pci_dev *pdev)
     if ( !iommu_enabled || !hd->platform_ops )
         return 0;
 
-    rc = hd->platform_ops->add_device(pdev->devfn, pdev);
+    rc = hd->platform_ops->add_device(pdev->devfn, pci_to_dev(pdev));
     if ( rc || !pdev->phantom_stride )
         return rc;
 
@@ -1265,7 +1265,7 @@ int iommu_add_device(struct pci_dev *pdev)
         devfn += pdev->phantom_stride;
         if ( PCI_SLOT(devfn) != PCI_SLOT(pdev->devfn) )
             return 0;
-        rc = hd->platform_ops->add_device(devfn, pdev);
+        rc = hd->platform_ops->add_device(devfn, pci_to_dev(pdev));
         if ( rc )
             printk(XENLOG_WARNING "IOMMU: add %04x:%02x:%02x.%u failed (%d)\n",
                    pdev->seg, pdev->bus, PCI_SLOT(devfn), PCI_FUNC(devfn), rc);
@@ -1286,7 +1286,7 @@ int iommu_enable_device(struct pci_dev *pdev)
          !hd->platform_ops->enable_device )
         return 0;
 
-    return hd->platform_ops->enable_device(pdev);
+    return hd->platform_ops->enable_device(pci_to_dev(pdev));
 }
 
 int iommu_remove_device(struct pci_dev *pdev)
@@ -1308,7 +1308,7 @@ int iommu_remove_device(struct pci_dev *pdev)
         devfn += pdev->phantom_stride;
         if ( PCI_SLOT(devfn) != PCI_SLOT(pdev->devfn) )
             break;
-        rc = hd->platform_ops->remove_device(devfn, pdev);
+        rc = hd->platform_ops->remove_device(devfn, pci_to_dev(pdev));
         if ( !rc )
             continue;
 
@@ -1317,7 +1317,7 @@ int iommu_remove_device(struct pci_dev *pdev)
         return rc;
     }
 
-    return hd->platform_ops->remove_device(pdev->devfn, pdev);
+    return hd->platform_ops->remove_device(pdev->devfn, pci_to_dev(pdev));
 }
 
 /*
@@ -1378,7 +1378,7 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn)
 
     pdev->fault.count = 0;
 
-    if ( (rc = hd->platform_ops->assign_device(d, devfn, pdev)) )
+    if ( (rc = hd->platform_ops->assign_device(d, devfn, pci_to_dev(pdev))) )
         goto done;
 
     for ( ; pdev->phantom_stride; rc = 0 )
@@ -1386,7 +1386,7 @@ static int assign_device(struct domain *d, u16 seg, u8 bus, u8 devfn)
         devfn += pdev->phantom_stride;
         if ( PCI_SLOT(devfn) != PCI_SLOT(pdev->devfn) )
             break;
-        rc = hd->platform_ops->assign_device(d, devfn, pdev);
+        rc = hd->platform_ops->assign_device(d, devfn, pci_to_dev(pdev));
         if ( rc )
             printk(XENLOG_G_WARNING "d%d: assign %04x:%02x:%02x.%u failed (%d)\n",
                    d->domain_id, seg, bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
@@ -1421,7 +1421,8 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn)
         devfn += pdev->phantom_stride;
         if ( PCI_SLOT(devfn) != PCI_SLOT(pdev->devfn) )
             break;
-        ret = hd->platform_ops->reassign_device(d, hardware_domain, devfn, pdev);
+        ret = hd->platform_ops->reassign_device(d, hardware_domain, devfn,
+                                                pci_to_dev(pdev));
         if ( !ret )
             continue;
 
@@ -1431,7 +1432,8 @@ int deassign_device(struct domain *d, u16 seg, u8 bus, u8 devfn)
     }
 
     devfn = pdev->devfn;
-    ret = hd->platform_ops->reassign_device(d, hardware_domain, devfn, pdev);
+    ret = hd->platform_ops->reassign_device(d, hardware_domain, devfn,
+                                            pci_to_dev(pdev));
     if ( ret )
     {
         dprintk(XENLOG_G_ERR,
diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c
index 19d8165..213a471 100644
--- a/xen/drivers/passthrough/vtd/iommu.c
+++ b/xen/drivers/passthrough/vtd/iommu.c
@@ -1875,8 +1875,9 @@ static int rmrr_identity_mapping(struct domain *d, bool_t map,
     return 0;
 }
 
-static int intel_iommu_add_device(u8 devfn, struct pci_dev *pdev)
+static int intel_iommu_add_device(u8 devfn, struct device *dev)
 {
+    struct pci_dev *pdev = dev_to_pci(dev);
     struct acpi_rmrr_unit *rmrr;
     u16 bdf;
     int ret, i;
@@ -1910,8 +1911,9 @@ static int intel_iommu_add_device(u8 devfn, struct pci_dev *pdev)
     return 0;
 }
 
-static int intel_iommu_enable_device(struct pci_dev *pdev)
+static int intel_iommu_enable_device(struct device *dev)
 {
+    struct pci_dev *pdev = dev_to_pci(dev);
     struct acpi_drhd_unit *drhd = acpi_find_matched_drhd_unit(pdev);
     int ret = drhd ? ats_device(pdev, drhd) : -ENODEV;
 
@@ -1925,8 +1927,9 @@ static int intel_iommu_enable_device(struct pci_dev *pdev)
     return ret >= 0 ? 0 : ret;
 }
 
-static int intel_iommu_remove_device(u8 devfn, struct pci_dev *pdev)
+static int intel_iommu_remove_device(u8 devfn, struct device *dev)
 {
+    struct pci_dev *pdev = dev_to_pci(dev);
     struct acpi_rmrr_unit *rmrr;
     u16 bdf;
     int i;
@@ -2212,8 +2215,9 @@ int __init intel_vtd_setup(void)
 static int reassign_device_ownership(
     struct domain *source,
     struct domain *target,
-    u8 devfn, struct pci_dev *pdev)
+    u8 devfn, struct device *dev)
 {
+    struct pci_dev *pdev = dev_to_pci(dev);
     int ret;
 
     /*
@@ -2266,8 +2270,9 @@ static int reassign_device_ownership(
 }
 
 static int intel_iommu_assign_device(
-    struct domain *d, u8 devfn, struct pci_dev *pdev)
+    struct domain *d, u8 devfn, struct device *dev)
 {
+    struct pci_dev *pdev = dev_to_pci(dev);
     struct acpi_rmrr_unit *rmrr;
     int ret = 0, i;
     u16 bdf, seg;
@@ -2276,7 +2281,7 @@ static int intel_iommu_assign_device(
     if ( list_empty(&acpi_drhd_units) )
         return -ENODEV;
 
-    ret = reassign_device_ownership(hardware_domain, d, devfn, pdev);
+    ret = reassign_device_ownership(hardware_domain, d, devfn, dev);
     if ( ret )
         return ret;
 
@@ -2298,7 +2303,7 @@ static int intel_iommu_assign_device(
             ret = rmrr_identity_mapping(d, 1, rmrr);
             if ( ret )
             {
-                reassign_device_ownership(d, hardware_domain, devfn, pdev);
+                reassign_device_ownership(d, hardware_domain, devfn, dev);
                 printk(XENLOG_G_ERR VTDPREFIX
                        " cannot map reserved region (%"PRIx64",%"PRIx64"] for Dom%d (%d)\n",
                        rmrr->base_address, rmrr->end_address,
diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h
index 8eb764a..d0f99ef 100644
--- a/xen/include/xen/iommu.h
+++ b/xen/include/xen/iommu.h
@@ -23,6 +23,7 @@
 #include <xen/init.h>
 #include <xen/spinlock.h>
 #include <xen/pci.h>
+#include <xen/device.h>
 #include <public/hvm/ioreq.h>
 #include <public/domctl.h>
 #include <asm/iommu.h>
@@ -123,22 +124,17 @@ struct page_info;
 struct iommu_ops {
     int (*init)(struct domain *d);
     void (*hwdom_init)(struct domain *d);
-#ifdef HAS_PCI
-    int (*add_device)(u8 devfn, struct pci_dev *);
-    int (*enable_device)(struct pci_dev *pdev);
-    int (*remove_device)(u8 devfn, struct pci_dev *);
-    int (*assign_device)(struct domain *, u8 devfn, struct pci_dev *);
+    int (*add_device)(u8 devfn, struct device *);
+    int (*enable_device)(struct device *dev);
+    int (*remove_device)(u8 devfn, struct device *);
+    int (*assign_device)(struct domain *, u8 devfn, struct device *);
     int (*reassign_device)(struct domain *s, struct domain *t,
-			   u8 devfn, struct pci_dev *);
+                           u8 devfn, struct device *);
+#ifdef HAS_PCI
     int (*get_device_group_id)(u16 seg, u8 bus, u8 devfn);
     int (*update_ire_from_msi)(struct msi_desc *msi_desc, struct msi_msg *msg);
     void (*read_msi_from_ire)(struct msi_desc *msi_desc, struct msi_msg *msg);
 #endif /* HAS_PCI */
-#ifdef HAS_DEVICE_TREE
-    int (*assign_dt_device)(struct domain *d, const struct dt_device_node *dev);
-    int (*reassign_dt_device)(struct domain *s, struct domain *t,
-                              const struct dt_device_node *dev);
-#endif
 
     void (*teardown)(struct domain *d);
     int (*map_page)(struct domain *d, unsigned long gfn, unsigned long mfn,
-- 
2.1.3

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

* [PATCH for 4.6 09/13] xen/arm: Describe device supported by a driver with dt_match_node
  2014-12-16 20:08 [PATCH for 4.6 00/13] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (7 preceding siblings ...)
  2014-12-16 20:08 ` [PATCH for 4.6 08/13] xen/iommu: Consolidate device assignment ops into a single set Julien Grall
@ 2014-12-16 20:08 ` Julien Grall
  2014-12-16 20:08 ` [PATCH for 4.6 10/13] xen/iommu: arm: Import the SMMU driver from Linux Julien Grall
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 41+ messages in thread
From: Julien Grall @ 2014-12-16 20:08 UTC (permalink / raw)
  To: xen-devel
  Cc: stefano.stabellini, manish.jaggi, Julien Grall, tim, ian.campbell

Xen is currently using list a compatible string to know if the driver
can use device node. This leads to have double definition in the GIC
code.

Futhermore Linux drivers is using dt_match_node (actually called of_device_id
in Linux) to list device supported by the drivers.

Signed-off-by: Julien Grall <julien.grall@linaro.org>
---
 xen/arch/arm/device.c              | 21 ++-------------------
 xen/arch/arm/gic-v2.c              | 10 ++++------
 xen/arch/arm/gic-v3.c              |  8 ++++----
 xen/drivers/char/exynos4210-uart.c |  8 ++++----
 xen/drivers/char/ns16550.c         | 12 ++++++------
 xen/drivers/char/omap-uart.c       |  8 ++++----
 xen/drivers/char/pl011.c           |  8 ++++----
 xen/include/asm-arm/device.h       |  8 ++++++--
 xen/include/asm-arm/gic.h          | 15 +++++----------
 9 files changed, 39 insertions(+), 59 deletions(-)

diff --git a/xen/arch/arm/device.c b/xen/arch/arm/device.c
index de702ff..1993929 100644
--- a/xen/arch/arm/device.c
+++ b/xen/arch/arm/device.c
@@ -23,23 +23,6 @@
 
 extern const struct device_desc _sdevice[], _edevice[];
 
-static bool_t __init device_is_compatible(const struct device_desc *desc,
-                                          const struct dt_device_node *dev)
-{
-    const char *const *compat;
-
-    if ( !desc->compatible )
-        return 0;
-
-    for ( compat = desc->compatible; *compat; compat++ )
-    {
-        if ( dt_device_is_compatible(dev, *compat) )
-            return 1;
-    }
-
-    return 0;
-}
-
 int __init device_init(struct dt_device_node *dev, enum device_match type,
                        const void *data)
 {
@@ -55,7 +38,7 @@ int __init device_init(struct dt_device_node *dev, enum device_match type,
         if ( desc->type != type )
             continue;
 
-        if ( device_is_compatible(desc, dev) )
+        if ( dt_match_node(desc->dt_match, dev) )
         {
             ASSERT(desc->init != NULL);
 
@@ -75,7 +58,7 @@ enum device_match device_get_type(const struct dt_device_node *dev)
 
     for ( desc = _sdevice; desc != _edevice; desc++ )
     {
-        if ( device_is_compatible(desc, dev) )
+        if ( dt_match_node(desc->dt_match, dev) )
             return desc->type;
     }
 
diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c
index 048350b..db3795d 100644
--- a/xen/arch/arm/gic-v2.c
+++ b/xen/arch/arm/gic-v2.c
@@ -763,16 +763,14 @@ static int __init gicv2_init(struct dt_device_node *node, const void *data)
     return 0;
 }
 
-static const char * const gicv2_dt_compat[] __initconst =
+static const struct dt_device_match gicv2_dt_match[] __initconst =
 {
-    DT_COMPAT_GIC_CORTEX_A15,
-    DT_COMPAT_GIC_CORTEX_A7,
-    DT_COMPAT_GIC_400,
-    NULL
+    DT_MATCH_GIC_V2,
+    { /* sentinel */ },
 };
 
 DT_DEVICE_START(gicv2, "GICv2", DEVICE_GIC)
-        .compatible = gicv2_dt_compat,
+        .dt_match = gicv2_dt_match,
         .init = gicv2_init,
 DT_DEVICE_END
 
diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
index c6d1876..1191cb7 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -1278,14 +1278,14 @@ static int __init gicv3_init(struct dt_device_node *node, const void *data)
     return res;
 }
 
-static const char * const gicv3_dt_compat[] __initconst =
+static const struct dt_device_match gicv3_dt_match[] __initconst =
 {
-    DT_COMPAT_GIC_V3,
-    NULL
+    DT_MATCH_GIC_V3,
+    { /* sentinel */ },
 };
 
 DT_DEVICE_START(gicv3, "GICv3", DEVICE_GIC)
-        .compatible = gicv3_dt_compat,
+        .dt_match = gicv3_dt_match,
         .init = gicv3_init,
 DT_DEVICE_END
 
diff --git a/xen/drivers/char/exynos4210-uart.c b/xen/drivers/char/exynos4210-uart.c
index 75246e1..b59e64a 100644
--- a/xen/drivers/char/exynos4210-uart.c
+++ b/xen/drivers/char/exynos4210-uart.c
@@ -352,14 +352,14 @@ static int __init exynos4210_uart_init(struct dt_device_node *dev,
     return 0;
 }
 
-static const char * const exynos4210_dt_compat[] __initconst =
+static const struct dt_device_match exynos4210_dt_match[] __initconst =
 {
-    "samsung,exynos4210-uart",
-    NULL
+    DT_MATCH_COMPATIBLE("samsung,exynos4210-uart"),
+    { /* sentinel */ },
 };
 
 DT_DEVICE_START(exynos4210, "Exynos 4210 UART", DEVICE_SERIAL)
-        .compatible = exynos4210_dt_compat,
+        .dt_match = exynos4210_dt_match,
         .init = exynos4210_uart_init,
 DT_DEVICE_END
 
diff --git a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c
index 6df3f95..a0373a9 100644
--- a/xen/drivers/char/ns16550.c
+++ b/xen/drivers/char/ns16550.c
@@ -1185,16 +1185,16 @@ static int __init ns16550_uart_dt_init(struct dt_device_node *dev,
     return 0;
 }
 
-static const char * const ns16550_dt_compat[] __initconst =
+static const struct dt_device_match ns16550_dt_match[] __initconst =
 {
-    "ns16550",
-    "ns16550a",
-    "snps,dw-apb-uart",
-    NULL
+    DT_MATCH_COMPATIBLE("ns16550"),
+    DT_MATCH_COMPATIBLE("ns16550a"),
+    DT_MATCH_COMPATIBLE("snps,dw-apb-uart"),
+    { /* sentinel */ },
 };
 
 DT_DEVICE_START(ns16550, "NS16550 UART", DEVICE_SERIAL)
-        .compatible = ns16550_dt_compat,
+        .dt_match = ns16550_dt_match,
         .init = ns16550_uart_dt_init,
 DT_DEVICE_END
 
diff --git a/xen/drivers/char/omap-uart.c b/xen/drivers/char/omap-uart.c
index c4cd442..3df80cf 100644
--- a/xen/drivers/char/omap-uart.c
+++ b/xen/drivers/char/omap-uart.c
@@ -350,14 +350,14 @@ static int __init omap_uart_init(struct dt_device_node *dev,
     return 0;
 }
 
-static const char * const omap_uart_dt_compat[] __initconst =
+static const struct dt_device_match omap_uart_dt_match[] __initconst =
 {
-    "ti,omap4-uart",
-    NULL
+    DT_MATCH_COMPATIBLE("ti,omap4-uart"),
+    { /* sentinel */ },
 };
 
 DT_DEVICE_START(omap_uart, "OMAP UART", DEVICE_SERIAL)
-    .compatible = omap_uart_dt_compat,
+    .dt_match = omap_uart_dt_match,
     .init = omap_uart_init,
 DT_DEVICE_END
 
diff --git a/xen/drivers/char/pl011.c b/xen/drivers/char/pl011.c
index cc91224..e6ecf0b 100644
--- a/xen/drivers/char/pl011.c
+++ b/xen/drivers/char/pl011.c
@@ -269,14 +269,14 @@ static int __init pl011_uart_init(struct dt_device_node *dev,
     return 0;
 }
 
-static const char * const pl011_dt_compat[] __initconst =
+static const struct dt_device_match pl011_dt_match[] __initconst =
 {
-    "arm,pl011",
-    NULL
+    DT_MATCH_COMPATIBLE("arm,pl011"),
+    { /* sentinel */ },
 };
 
 DT_DEVICE_START(pl011, "PL011 UART", DEVICE_SERIAL)
-        .compatible = pl011_dt_compat,
+        .dt_match = pl011_dt_match,
         .init = pl011_uart_init,
 DT_DEVICE_END
 
diff --git a/xen/include/asm-arm/device.h b/xen/include/asm-arm/device.h
index fdcd097..a2711a3 100644
--- a/xen/include/asm-arm/device.h
+++ b/xen/include/asm-arm/device.h
@@ -3,6 +3,10 @@
 
 #include <xen/init.h>
 
+struct dev_archdata {
+    void *iommu;    /* IOMMU private data */
+};
+
 struct dt_device_node;
 
 enum device_match
@@ -19,8 +23,8 @@ struct device_desc {
     const char *name;
     /* Device type */
     enum device_match type;
-    /* Array of device tree 'compatible' strings */
-    const char *const *compatible;
+    /* List of devices supported by this driver */
+    const struct dt_device_match *dt_match;
     /* Device initialization */
     int (*init)(struct dt_device_node *dev, const void *data);
 };
diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h
index 187dc46..73ca3cf 100644
--- a/xen/include/asm-arm/gic.h
+++ b/xen/include/asm-arm/gic.h
@@ -152,17 +152,12 @@
 #include <xen/irq.h>
 #include <asm-arm/vgic.h>
 
-#define DT_COMPAT_GIC_400            "arm,gic-400"
-#define DT_COMPAT_GIC_CORTEX_A15     "arm,cortex-a15-gic"
-#define DT_COMPAT_GIC_CORTEX_A7      "arm,cortex-a7-gic"
+#define DT_MATCH_GIC_V2                                             \
+    DT_MATCH_COMPATIBLE("arm,cortex-a15-gic"),                      \
+    DT_MATCH_COMPATIBLE("arm,cortex-a7-gic"),                       \
+    DT_MATCH_COMPATIBLE("arm,gic-400")
 
-#define DT_MATCH_GIC_V2 DT_MATCH_COMPATIBLE(DT_COMPAT_GIC_CORTEX_A15), \
-                        DT_MATCH_COMPATIBLE(DT_COMPAT_GIC_CORTEX_A7), \
-                        DT_MATCH_COMPATIBLE(DT_COMPAT_GIC_400)
-
-#define DT_COMPAT_GIC_V3             "arm,gic-v3"
-
-#define DT_MATCH_GIC_V3 DT_MATCH_COMPATIBLE(DT_COMPAT_GIC_V3)
+#define DT_MATCH_GIC_V3 DT_MATCH_COMPATIBLE("arm,gic-v3")
 
 /*
  * GICv3 registers that needs to be saved/restored
-- 
2.1.3

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

* [PATCH for 4.6 10/13] xen/iommu: arm: Import the SMMU driver from Linux
  2014-12-16 20:08 [PATCH for 4.6 00/13] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (8 preceding siblings ...)
  2014-12-16 20:08 ` [PATCH for 4.6 09/13] xen/arm: Describe device supported by a driver with dt_match_node Julien Grall
@ 2014-12-16 20:08 ` Julien Grall
  2014-12-16 20:08 ` [PATCH for 4.6 11/13] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices Julien Grall
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 41+ messages in thread
From: Julien Grall @ 2014-12-16 20:08 UTC (permalink / raw)
  To: xen-devel
  Cc: stefano.stabellini, manish.jaggi, Julien Grall, tim, ian.campbell

Based on commit e6b5be2be4e30037eb551e0ed09dd97bd00d85d3.

It's a basic copy of the Linux SMMU drivers code. No Xen code has yet been added
and not build.

Compare to the previous drivers it gains support of PCI. Though it will
need a bit of plumbing for Xen.

Signed-of-by: Julien Grall <julien.grall@linaro.org>
---
 xen/drivers/passthrough/arm/smmu.c | 2193 ++++++++++++++++++++++++++++++++++++
 1 file changed, 2193 insertions(+)
 create mode 100644 xen/drivers/passthrough/arm/smmu.c

diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c
new file mode 100644
index 0000000..6cd47b7
--- /dev/null
+++ b/xen/drivers/passthrough/arm/smmu.c
@@ -0,0 +1,2193 @@
+/*
+ * IOMMU API for ARM architected SMMU implementations.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2013 ARM Limited
+ *
+ * Author: Will Deacon <will.deacon@arm.com>
+ *
+ * This driver currently supports:
+ *	- SMMUv1 and v2 implementations
+ *	- Stream-matching and stream-indexing
+ *	- v7/v8 long-descriptor format
+ *	- Non-secure access to the SMMU
+ *	- 4k and 64k pages, with contiguous pte hints.
+ *	- Up to 48-bit addressing (dependent on VA_BITS)
+ *	- Context fault reporting
+ */
+
+#define pr_fmt(fmt) "arm-smmu: " fmt
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iommu.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <linux/amba/bus.h>
+
+#include <asm/pgalloc.h>
+
+/* Maximum number of stream IDs assigned to a single device */
+#define MAX_MASTER_STREAMIDS		MAX_PHANDLE_ARGS
+
+/* Maximum number of context banks per SMMU */
+#define ARM_SMMU_MAX_CBS		128
+
+/* Maximum number of mapping groups per SMMU */
+#define ARM_SMMU_MAX_SMRS		128
+
+/* SMMU global address space */
+#define ARM_SMMU_GR0(smmu)		((smmu)->base)
+#define ARM_SMMU_GR1(smmu)		((smmu)->base + (1 << (smmu)->pgshift))
+
+/*
+ * SMMU global address space with conditional offset to access secure
+ * aliases of non-secure registers (e.g. nsCR0: 0x400, nsGFSR: 0x448,
+ * nsGFSYNR0: 0x450)
+ */
+#define ARM_SMMU_GR0_NS(smmu)						\
+	((smmu)->base +							\
+		((smmu->options & ARM_SMMU_OPT_SECURE_CFG_ACCESS)	\
+			? 0x400 : 0))
+
+/* Page table bits */
+#define ARM_SMMU_PTE_XN			(((pteval_t)3) << 53)
+#define ARM_SMMU_PTE_CONT		(((pteval_t)1) << 52)
+#define ARM_SMMU_PTE_AF			(((pteval_t)1) << 10)
+#define ARM_SMMU_PTE_SH_NS		(((pteval_t)0) << 8)
+#define ARM_SMMU_PTE_SH_OS		(((pteval_t)2) << 8)
+#define ARM_SMMU_PTE_SH_IS		(((pteval_t)3) << 8)
+#define ARM_SMMU_PTE_PAGE		(((pteval_t)3) << 0)
+
+#if PAGE_SIZE == SZ_4K
+#define ARM_SMMU_PTE_CONT_ENTRIES	16
+#elif PAGE_SIZE == SZ_64K
+#define ARM_SMMU_PTE_CONT_ENTRIES	32
+#else
+#define ARM_SMMU_PTE_CONT_ENTRIES	1
+#endif
+
+#define ARM_SMMU_PTE_CONT_SIZE		(PAGE_SIZE * ARM_SMMU_PTE_CONT_ENTRIES)
+#define ARM_SMMU_PTE_CONT_MASK		(~(ARM_SMMU_PTE_CONT_SIZE - 1))
+
+/* Stage-1 PTE */
+#define ARM_SMMU_PTE_AP_UNPRIV		(((pteval_t)1) << 6)
+#define ARM_SMMU_PTE_AP_RDONLY		(((pteval_t)2) << 6)
+#define ARM_SMMU_PTE_ATTRINDX_SHIFT	2
+#define ARM_SMMU_PTE_nG			(((pteval_t)1) << 11)
+
+/* Stage-2 PTE */
+#define ARM_SMMU_PTE_HAP_FAULT		(((pteval_t)0) << 6)
+#define ARM_SMMU_PTE_HAP_READ		(((pteval_t)1) << 6)
+#define ARM_SMMU_PTE_HAP_WRITE		(((pteval_t)2) << 6)
+#define ARM_SMMU_PTE_MEMATTR_OIWB	(((pteval_t)0xf) << 2)
+#define ARM_SMMU_PTE_MEMATTR_NC		(((pteval_t)0x5) << 2)
+#define ARM_SMMU_PTE_MEMATTR_DEV	(((pteval_t)0x1) << 2)
+
+/* Configuration registers */
+#define ARM_SMMU_GR0_sCR0		0x0
+#define sCR0_CLIENTPD			(1 << 0)
+#define sCR0_GFRE			(1 << 1)
+#define sCR0_GFIE			(1 << 2)
+#define sCR0_GCFGFRE			(1 << 4)
+#define sCR0_GCFGFIE			(1 << 5)
+#define sCR0_USFCFG			(1 << 10)
+#define sCR0_VMIDPNE			(1 << 11)
+#define sCR0_PTM			(1 << 12)
+#define sCR0_FB				(1 << 13)
+#define sCR0_BSU_SHIFT			14
+#define sCR0_BSU_MASK			0x3
+
+/* Identification registers */
+#define ARM_SMMU_GR0_ID0		0x20
+#define ARM_SMMU_GR0_ID1		0x24
+#define ARM_SMMU_GR0_ID2		0x28
+#define ARM_SMMU_GR0_ID3		0x2c
+#define ARM_SMMU_GR0_ID4		0x30
+#define ARM_SMMU_GR0_ID5		0x34
+#define ARM_SMMU_GR0_ID6		0x38
+#define ARM_SMMU_GR0_ID7		0x3c
+#define ARM_SMMU_GR0_sGFSR		0x48
+#define ARM_SMMU_GR0_sGFSYNR0		0x50
+#define ARM_SMMU_GR0_sGFSYNR1		0x54
+#define ARM_SMMU_GR0_sGFSYNR2		0x58
+#define ARM_SMMU_GR0_PIDR0		0xfe0
+#define ARM_SMMU_GR0_PIDR1		0xfe4
+#define ARM_SMMU_GR0_PIDR2		0xfe8
+
+#define ID0_S1TS			(1 << 30)
+#define ID0_S2TS			(1 << 29)
+#define ID0_NTS				(1 << 28)
+#define ID0_SMS				(1 << 27)
+#define ID0_PTFS_SHIFT			24
+#define ID0_PTFS_MASK			0x2
+#define ID0_PTFS_V8_ONLY		0x2
+#define ID0_CTTW			(1 << 14)
+#define ID0_NUMIRPT_SHIFT		16
+#define ID0_NUMIRPT_MASK		0xff
+#define ID0_NUMSIDB_SHIFT		9
+#define ID0_NUMSIDB_MASK		0xf
+#define ID0_NUMSMRG_SHIFT		0
+#define ID0_NUMSMRG_MASK		0xff
+
+#define ID1_PAGESIZE			(1 << 31)
+#define ID1_NUMPAGENDXB_SHIFT		28
+#define ID1_NUMPAGENDXB_MASK		7
+#define ID1_NUMS2CB_SHIFT		16
+#define ID1_NUMS2CB_MASK		0xff
+#define ID1_NUMCB_SHIFT			0
+#define ID1_NUMCB_MASK			0xff
+
+#define ID2_OAS_SHIFT			4
+#define ID2_OAS_MASK			0xf
+#define ID2_IAS_SHIFT			0
+#define ID2_IAS_MASK			0xf
+#define ID2_UBS_SHIFT			8
+#define ID2_UBS_MASK			0xf
+#define ID2_PTFS_4K			(1 << 12)
+#define ID2_PTFS_16K			(1 << 13)
+#define ID2_PTFS_64K			(1 << 14)
+
+#define PIDR2_ARCH_SHIFT		4
+#define PIDR2_ARCH_MASK			0xf
+
+/* Global TLB invalidation */
+#define ARM_SMMU_GR0_STLBIALL		0x60
+#define ARM_SMMU_GR0_TLBIVMID		0x64
+#define ARM_SMMU_GR0_TLBIALLNSNH	0x68
+#define ARM_SMMU_GR0_TLBIALLH		0x6c
+#define ARM_SMMU_GR0_sTLBGSYNC		0x70
+#define ARM_SMMU_GR0_sTLBGSTATUS	0x74
+#define sTLBGSTATUS_GSACTIVE		(1 << 0)
+#define TLB_LOOP_TIMEOUT		1000000	/* 1s! */
+
+/* Stream mapping registers */
+#define ARM_SMMU_GR0_SMR(n)		(0x800 + ((n) << 2))
+#define SMR_VALID			(1 << 31)
+#define SMR_MASK_SHIFT			16
+#define SMR_MASK_MASK			0x7fff
+#define SMR_ID_SHIFT			0
+#define SMR_ID_MASK			0x7fff
+
+#define ARM_SMMU_GR0_S2CR(n)		(0xc00 + ((n) << 2))
+#define S2CR_CBNDX_SHIFT		0
+#define S2CR_CBNDX_MASK			0xff
+#define S2CR_TYPE_SHIFT			16
+#define S2CR_TYPE_MASK			0x3
+#define S2CR_TYPE_TRANS			(0 << S2CR_TYPE_SHIFT)
+#define S2CR_TYPE_BYPASS		(1 << S2CR_TYPE_SHIFT)
+#define S2CR_TYPE_FAULT			(2 << S2CR_TYPE_SHIFT)
+
+/* Context bank attribute registers */
+#define ARM_SMMU_GR1_CBAR(n)		(0x0 + ((n) << 2))
+#define CBAR_VMID_SHIFT			0
+#define CBAR_VMID_MASK			0xff
+#define CBAR_S1_BPSHCFG_SHIFT		8
+#define CBAR_S1_BPSHCFG_MASK		3
+#define CBAR_S1_BPSHCFG_NSH		3
+#define CBAR_S1_MEMATTR_SHIFT		12
+#define CBAR_S1_MEMATTR_MASK		0xf
+#define CBAR_S1_MEMATTR_WB		0xf
+#define CBAR_TYPE_SHIFT			16
+#define CBAR_TYPE_MASK			0x3
+#define CBAR_TYPE_S2_TRANS		(0 << CBAR_TYPE_SHIFT)
+#define CBAR_TYPE_S1_TRANS_S2_BYPASS	(1 << CBAR_TYPE_SHIFT)
+#define CBAR_TYPE_S1_TRANS_S2_FAULT	(2 << CBAR_TYPE_SHIFT)
+#define CBAR_TYPE_S1_TRANS_S2_TRANS	(3 << CBAR_TYPE_SHIFT)
+#define CBAR_IRPTNDX_SHIFT		24
+#define CBAR_IRPTNDX_MASK		0xff
+
+#define ARM_SMMU_GR1_CBA2R(n)		(0x800 + ((n) << 2))
+#define CBA2R_RW64_32BIT		(0 << 0)
+#define CBA2R_RW64_64BIT		(1 << 0)
+
+/* Translation context bank */
+#define ARM_SMMU_CB_BASE(smmu)		((smmu)->base + ((smmu)->size >> 1))
+#define ARM_SMMU_CB(smmu, n)		((n) * (1 << (smmu)->pgshift))
+
+#define ARM_SMMU_CB_SCTLR		0x0
+#define ARM_SMMU_CB_RESUME		0x8
+#define ARM_SMMU_CB_TTBCR2		0x10
+#define ARM_SMMU_CB_TTBR0_LO		0x20
+#define ARM_SMMU_CB_TTBR0_HI		0x24
+#define ARM_SMMU_CB_TTBCR		0x30
+#define ARM_SMMU_CB_S1_MAIR0		0x38
+#define ARM_SMMU_CB_FSR			0x58
+#define ARM_SMMU_CB_FAR_LO		0x60
+#define ARM_SMMU_CB_FAR_HI		0x64
+#define ARM_SMMU_CB_FSYNR0		0x68
+#define ARM_SMMU_CB_S1_TLBIASID		0x610
+
+#define SCTLR_S1_ASIDPNE		(1 << 12)
+#define SCTLR_CFCFG			(1 << 7)
+#define SCTLR_CFIE			(1 << 6)
+#define SCTLR_CFRE			(1 << 5)
+#define SCTLR_E				(1 << 4)
+#define SCTLR_AFE			(1 << 2)
+#define SCTLR_TRE			(1 << 1)
+#define SCTLR_M				(1 << 0)
+#define SCTLR_EAE_SBOP			(SCTLR_AFE | SCTLR_TRE)
+
+#define RESUME_RETRY			(0 << 0)
+#define RESUME_TERMINATE		(1 << 0)
+
+#define TTBCR_EAE			(1 << 31)
+
+#define TTBCR_PASIZE_SHIFT		16
+#define TTBCR_PASIZE_MASK		0x7
+
+#define TTBCR_TG0_4K			(0 << 14)
+#define TTBCR_TG0_64K			(1 << 14)
+
+#define TTBCR_SH0_SHIFT			12
+#define TTBCR_SH0_MASK			0x3
+#define TTBCR_SH_NS			0
+#define TTBCR_SH_OS			2
+#define TTBCR_SH_IS			3
+
+#define TTBCR_ORGN0_SHIFT		10
+#define TTBCR_IRGN0_SHIFT		8
+#define TTBCR_RGN_MASK			0x3
+#define TTBCR_RGN_NC			0
+#define TTBCR_RGN_WBWA			1
+#define TTBCR_RGN_WT			2
+#define TTBCR_RGN_WB			3
+
+#define TTBCR_SL0_SHIFT			6
+#define TTBCR_SL0_MASK			0x3
+#define TTBCR_SL0_LVL_2			0
+#define TTBCR_SL0_LVL_1			1
+
+#define TTBCR_T1SZ_SHIFT		16
+#define TTBCR_T0SZ_SHIFT		0
+#define TTBCR_SZ_MASK			0xf
+
+#define TTBCR2_SEP_SHIFT		15
+#define TTBCR2_SEP_MASK			0x7
+
+#define TTBCR2_PASIZE_SHIFT		0
+#define TTBCR2_PASIZE_MASK		0x7
+
+/* Common definitions for PASize and SEP fields */
+#define TTBCR2_ADDR_32			0
+#define TTBCR2_ADDR_36			1
+#define TTBCR2_ADDR_40			2
+#define TTBCR2_ADDR_42			3
+#define TTBCR2_ADDR_44			4
+#define TTBCR2_ADDR_48			5
+
+#define TTBRn_HI_ASID_SHIFT		16
+
+#define MAIR_ATTR_SHIFT(n)		((n) << 3)
+#define MAIR_ATTR_MASK			0xff
+#define MAIR_ATTR_DEVICE		0x04
+#define MAIR_ATTR_NC			0x44
+#define MAIR_ATTR_WBRWA			0xff
+#define MAIR_ATTR_IDX_NC		0
+#define MAIR_ATTR_IDX_CACHE		1
+#define MAIR_ATTR_IDX_DEV		2
+
+#define FSR_MULTI			(1 << 31)
+#define FSR_SS				(1 << 30)
+#define FSR_UUT				(1 << 8)
+#define FSR_ASF				(1 << 7)
+#define FSR_TLBLKF			(1 << 6)
+#define FSR_TLBMCF			(1 << 5)
+#define FSR_EF				(1 << 4)
+#define FSR_PF				(1 << 3)
+#define FSR_AFF				(1 << 2)
+#define FSR_TF				(1 << 1)
+
+#define FSR_IGN				(FSR_AFF | FSR_ASF | \
+					 FSR_TLBMCF | FSR_TLBLKF)
+#define FSR_FAULT			(FSR_MULTI | FSR_SS | FSR_UUT | \
+					 FSR_EF | FSR_PF | FSR_TF | FSR_IGN)
+
+#define FSYNR0_WNR			(1 << 4)
+
+static int force_stage;
+module_param_named(force_stage, force_stage, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(force_stage,
+	"Force SMMU mappings to be installed at a particular stage of translation. A value of '1' or '2' forces the corresponding stage. All other values are ignored (i.e. no stage is forced). Note that selecting a specific stage will disable support for nested translation.");
+
+enum arm_smmu_arch_version {
+	ARM_SMMU_V1 = 1,
+	ARM_SMMU_V2,
+};
+
+struct arm_smmu_smr {
+	u8				idx;
+	u16				mask;
+	u16				id;
+};
+
+struct arm_smmu_master_cfg {
+	int				num_streamids;
+	u16				streamids[MAX_MASTER_STREAMIDS];
+	struct arm_smmu_smr		*smrs;
+};
+
+struct arm_smmu_master {
+	struct device_node		*of_node;
+	struct rb_node			node;
+	struct arm_smmu_master_cfg	cfg;
+};
+
+struct arm_smmu_device {
+	struct device			*dev;
+
+	void __iomem			*base;
+	unsigned long			size;
+	unsigned long			pgshift;
+
+#define ARM_SMMU_FEAT_COHERENT_WALK	(1 << 0)
+#define ARM_SMMU_FEAT_STREAM_MATCH	(1 << 1)
+#define ARM_SMMU_FEAT_TRANS_S1		(1 << 2)
+#define ARM_SMMU_FEAT_TRANS_S2		(1 << 3)
+#define ARM_SMMU_FEAT_TRANS_NESTED	(1 << 4)
+	u32				features;
+
+#define ARM_SMMU_OPT_SECURE_CFG_ACCESS (1 << 0)
+	u32				options;
+	enum arm_smmu_arch_version	version;
+
+	u32				num_context_banks;
+	u32				num_s2_context_banks;
+	DECLARE_BITMAP(context_map, ARM_SMMU_MAX_CBS);
+	atomic_t			irptndx;
+
+	u32				num_mapping_groups;
+	DECLARE_BITMAP(smr_map, ARM_SMMU_MAX_SMRS);
+
+	unsigned long			s1_input_size;
+	unsigned long			s1_output_size;
+	unsigned long			s2_input_size;
+	unsigned long			s2_output_size;
+
+	u32				num_global_irqs;
+	u32				num_context_irqs;
+	unsigned int			*irqs;
+
+	struct list_head		list;
+	struct rb_root			masters;
+};
+
+struct arm_smmu_cfg {
+	u8				cbndx;
+	u8				irptndx;
+	u32				cbar;
+	pgd_t				*pgd;
+};
+#define INVALID_IRPTNDX			0xff
+
+#define ARM_SMMU_CB_ASID(cfg)		((cfg)->cbndx)
+#define ARM_SMMU_CB_VMID(cfg)		((cfg)->cbndx + 1)
+
+enum arm_smmu_domain_stage {
+	ARM_SMMU_DOMAIN_S1 = 0,
+	ARM_SMMU_DOMAIN_S2,
+	ARM_SMMU_DOMAIN_NESTED,
+};
+
+struct arm_smmu_domain {
+	struct arm_smmu_device		*smmu;
+	struct arm_smmu_cfg		cfg;
+	enum arm_smmu_domain_stage	stage;
+	spinlock_t			lock;
+};
+
+static DEFINE_SPINLOCK(arm_smmu_devices_lock);
+static LIST_HEAD(arm_smmu_devices);
+
+struct arm_smmu_option_prop {
+	u32 opt;
+	const char *prop;
+};
+
+static struct arm_smmu_option_prop arm_smmu_options[] = {
+	{ ARM_SMMU_OPT_SECURE_CFG_ACCESS, "calxeda,smmu-secure-config-access" },
+	{ 0, NULL},
+};
+
+static void parse_driver_options(struct arm_smmu_device *smmu)
+{
+	int i = 0;
+
+	do {
+		if (of_property_read_bool(smmu->dev->of_node,
+						arm_smmu_options[i].prop)) {
+			smmu->options |= arm_smmu_options[i].opt;
+			dev_notice(smmu->dev, "option %s\n",
+				arm_smmu_options[i].prop);
+		}
+	} while (arm_smmu_options[++i].opt);
+}
+
+static struct device_node *dev_get_dev_node(struct device *dev)
+{
+	if (dev_is_pci(dev)) {
+		struct pci_bus *bus = to_pci_dev(dev)->bus;
+
+		while (!pci_is_root_bus(bus))
+			bus = bus->parent;
+		return bus->bridge->parent->of_node;
+	}
+
+	return dev->of_node;
+}
+
+static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu,
+						struct device_node *dev_node)
+{
+	struct rb_node *node = smmu->masters.rb_node;
+
+	while (node) {
+		struct arm_smmu_master *master;
+
+		master = container_of(node, struct arm_smmu_master, node);
+
+		if (dev_node < master->of_node)
+			node = node->rb_left;
+		else if (dev_node > master->of_node)
+			node = node->rb_right;
+		else
+			return master;
+	}
+
+	return NULL;
+}
+
+static struct arm_smmu_master_cfg *
+find_smmu_master_cfg(struct device *dev)
+{
+	struct arm_smmu_master_cfg *cfg = NULL;
+	struct iommu_group *group = iommu_group_get(dev);
+
+	if (group) {
+		cfg = iommu_group_get_iommudata(group);
+		iommu_group_put(group);
+	}
+
+	return cfg;
+}
+
+static int insert_smmu_master(struct arm_smmu_device *smmu,
+			      struct arm_smmu_master *master)
+{
+	struct rb_node **new, *parent;
+
+	new = &smmu->masters.rb_node;
+	parent = NULL;
+	while (*new) {
+		struct arm_smmu_master *this
+			= container_of(*new, struct arm_smmu_master, node);
+
+		parent = *new;
+		if (master->of_node < this->of_node)
+			new = &((*new)->rb_left);
+		else if (master->of_node > this->of_node)
+			new = &((*new)->rb_right);
+		else
+			return -EEXIST;
+	}
+
+	rb_link_node(&master->node, parent, new);
+	rb_insert_color(&master->node, &smmu->masters);
+	return 0;
+}
+
+static int register_smmu_master(struct arm_smmu_device *smmu,
+				struct device *dev,
+				struct of_phandle_args *masterspec)
+{
+	int i;
+	struct arm_smmu_master *master;
+
+	master = find_smmu_master(smmu, masterspec->np);
+	if (master) {
+		dev_err(dev,
+			"rejecting multiple registrations for master device %s\n",
+			masterspec->np->name);
+		return -EBUSY;
+	}
+
+	if (masterspec->args_count > MAX_MASTER_STREAMIDS) {
+		dev_err(dev,
+			"reached maximum number (%d) of stream IDs for master device %s\n",
+			MAX_MASTER_STREAMIDS, masterspec->np->name);
+		return -ENOSPC;
+	}
+
+	master = devm_kzalloc(dev, sizeof(*master), GFP_KERNEL);
+	if (!master)
+		return -ENOMEM;
+
+	master->of_node			= masterspec->np;
+	master->cfg.num_streamids	= masterspec->args_count;
+
+	for (i = 0; i < master->cfg.num_streamids; ++i) {
+		u16 streamid = masterspec->args[i];
+
+		if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) &&
+		     (streamid >= smmu->num_mapping_groups)) {
+			dev_err(dev,
+				"stream ID for master device %s greater than maximum allowed (%d)\n",
+				masterspec->np->name, smmu->num_mapping_groups);
+			return -ERANGE;
+		}
+		master->cfg.streamids[i] = streamid;
+	}
+	return insert_smmu_master(smmu, master);
+}
+
+static struct arm_smmu_device *find_smmu_for_device(struct device *dev)
+{
+	struct arm_smmu_device *smmu;
+	struct arm_smmu_master *master = NULL;
+	struct device_node *dev_node = dev_get_dev_node(dev);
+
+	spin_lock(&arm_smmu_devices_lock);
+	list_for_each_entry(smmu, &arm_smmu_devices, list) {
+		master = find_smmu_master(smmu, dev_node);
+		if (master)
+			break;
+	}
+	spin_unlock(&arm_smmu_devices_lock);
+
+	return master ? smmu : NULL;
+}
+
+static int __arm_smmu_alloc_bitmap(unsigned long *map, int start, int end)
+{
+	int idx;
+
+	do {
+		idx = find_next_zero_bit(map, end, start);
+		if (idx == end)
+			return -ENOSPC;
+	} while (test_and_set_bit(idx, map));
+
+	return idx;
+}
+
+static void __arm_smmu_free_bitmap(unsigned long *map, int idx)
+{
+	clear_bit(idx, map);
+}
+
+/* Wait for any pending TLB invalidations to complete */
+static void arm_smmu_tlb_sync(struct arm_smmu_device *smmu)
+{
+	int count = 0;
+	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+
+	writel_relaxed(0, gr0_base + ARM_SMMU_GR0_sTLBGSYNC);
+	while (readl_relaxed(gr0_base + ARM_SMMU_GR0_sTLBGSTATUS)
+	       & sTLBGSTATUS_GSACTIVE) {
+		cpu_relax();
+		if (++count == TLB_LOOP_TIMEOUT) {
+			dev_err_ratelimited(smmu->dev,
+			"TLB sync timed out -- SMMU may be deadlocked\n");
+			return;
+		}
+		udelay(1);
+	}
+}
+
+static void arm_smmu_tlb_inv_context(struct arm_smmu_domain *smmu_domain)
+{
+	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
+	struct arm_smmu_device *smmu = smmu_domain->smmu;
+	void __iomem *base = ARM_SMMU_GR0(smmu);
+	bool stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS;
+
+	if (stage1) {
+		base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
+		writel_relaxed(ARM_SMMU_CB_ASID(cfg),
+			       base + ARM_SMMU_CB_S1_TLBIASID);
+	} else {
+		base = ARM_SMMU_GR0(smmu);
+		writel_relaxed(ARM_SMMU_CB_VMID(cfg),
+			       base + ARM_SMMU_GR0_TLBIVMID);
+	}
+
+	arm_smmu_tlb_sync(smmu);
+}
+
+static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
+{
+	int flags, ret;
+	u32 fsr, far, fsynr, resume;
+	unsigned long iova;
+	struct iommu_domain *domain = dev;
+	struct arm_smmu_domain *smmu_domain = domain->priv;
+	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
+	struct arm_smmu_device *smmu = smmu_domain->smmu;
+	void __iomem *cb_base;
+
+	cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
+	fsr = readl_relaxed(cb_base + ARM_SMMU_CB_FSR);
+
+	if (!(fsr & FSR_FAULT))
+		return IRQ_NONE;
+
+	if (fsr & FSR_IGN)
+		dev_err_ratelimited(smmu->dev,
+				    "Unexpected context fault (fsr 0x%x)\n",
+				    fsr);
+
+	fsynr = readl_relaxed(cb_base + ARM_SMMU_CB_FSYNR0);
+	flags = fsynr & FSYNR0_WNR ? IOMMU_FAULT_WRITE : IOMMU_FAULT_READ;
+
+	far = readl_relaxed(cb_base + ARM_SMMU_CB_FAR_LO);
+	iova = far;
+#ifdef CONFIG_64BIT
+	far = readl_relaxed(cb_base + ARM_SMMU_CB_FAR_HI);
+	iova |= ((unsigned long)far << 32);
+#endif
+
+	if (!report_iommu_fault(domain, smmu->dev, iova, flags)) {
+		ret = IRQ_HANDLED;
+		resume = RESUME_RETRY;
+	} else {
+		dev_err_ratelimited(smmu->dev,
+		    "Unhandled context fault: iova=0x%08lx, fsynr=0x%x, cb=%d\n",
+		    iova, fsynr, cfg->cbndx);
+		ret = IRQ_NONE;
+		resume = RESUME_TERMINATE;
+	}
+
+	/* Clear the faulting FSR */
+	writel(fsr, cb_base + ARM_SMMU_CB_FSR);
+
+	/* Retry or terminate any stalled transactions */
+	if (fsr & FSR_SS)
+		writel_relaxed(resume, cb_base + ARM_SMMU_CB_RESUME);
+
+	return ret;
+}
+
+static irqreturn_t arm_smmu_global_fault(int irq, void *dev)
+{
+	u32 gfsr, gfsynr0, gfsynr1, gfsynr2;
+	struct arm_smmu_device *smmu = dev;
+	void __iomem *gr0_base = ARM_SMMU_GR0_NS(smmu);
+
+	gfsr = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSR);
+	gfsynr0 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR0);
+	gfsynr1 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR1);
+	gfsynr2 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR2);
+
+	if (!gfsr)
+		return IRQ_NONE;
+
+	dev_err_ratelimited(smmu->dev,
+		"Unexpected global fault, this could be serious\n");
+	dev_err_ratelimited(smmu->dev,
+		"\tGFSR 0x%08x, GFSYNR0 0x%08x, GFSYNR1 0x%08x, GFSYNR2 0x%08x\n",
+		gfsr, gfsynr0, gfsynr1, gfsynr2);
+
+	writel(gfsr, gr0_base + ARM_SMMU_GR0_sGFSR);
+	return IRQ_HANDLED;
+}
+
+static void arm_smmu_flush_pgtable(struct arm_smmu_device *smmu, void *addr,
+				   size_t size)
+{
+	unsigned long offset = (unsigned long)addr & ~PAGE_MASK;
+
+
+	/* Ensure new page tables are visible to the hardware walker */
+	if (smmu->features & ARM_SMMU_FEAT_COHERENT_WALK) {
+		dsb(ishst);
+	} else {
+		/*
+		 * If the SMMU can't walk tables in the CPU caches, treat them
+		 * like non-coherent DMA since we need to flush the new entries
+		 * all the way out to memory. There's no possibility of
+		 * recursion here as the SMMU table walker will not be wired
+		 * through another SMMU.
+		 */
+		dma_map_page(smmu->dev, virt_to_page(addr), offset, size,
+				DMA_TO_DEVICE);
+	}
+}
+
+static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
+{
+	u32 reg;
+	bool stage1;
+	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
+	struct arm_smmu_device *smmu = smmu_domain->smmu;
+	void __iomem *cb_base, *gr0_base, *gr1_base;
+
+	gr0_base = ARM_SMMU_GR0(smmu);
+	gr1_base = ARM_SMMU_GR1(smmu);
+	stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS;
+	cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
+
+	/* CBAR */
+	reg = cfg->cbar;
+	if (smmu->version == ARM_SMMU_V1)
+		reg |= cfg->irptndx << CBAR_IRPTNDX_SHIFT;
+
+	/*
+	 * Use the weakest shareability/memory types, so they are
+	 * overridden by the ttbcr/pte.
+	 */
+	if (stage1) {
+		reg |= (CBAR_S1_BPSHCFG_NSH << CBAR_S1_BPSHCFG_SHIFT) |
+			(CBAR_S1_MEMATTR_WB << CBAR_S1_MEMATTR_SHIFT);
+	} else {
+		reg |= ARM_SMMU_CB_VMID(cfg) << CBAR_VMID_SHIFT;
+	}
+	writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBAR(cfg->cbndx));
+
+	if (smmu->version > ARM_SMMU_V1) {
+		/* CBA2R */
+#ifdef CONFIG_64BIT
+		reg = CBA2R_RW64_64BIT;
+#else
+		reg = CBA2R_RW64_32BIT;
+#endif
+		writel_relaxed(reg,
+			       gr1_base + ARM_SMMU_GR1_CBA2R(cfg->cbndx));
+
+		/* TTBCR2 */
+		switch (smmu->s1_input_size) {
+		case 32:
+			reg = (TTBCR2_ADDR_32 << TTBCR2_SEP_SHIFT);
+			break;
+		case 36:
+			reg = (TTBCR2_ADDR_36 << TTBCR2_SEP_SHIFT);
+			break;
+		case 39:
+		case 40:
+			reg = (TTBCR2_ADDR_40 << TTBCR2_SEP_SHIFT);
+			break;
+		case 42:
+			reg = (TTBCR2_ADDR_42 << TTBCR2_SEP_SHIFT);
+			break;
+		case 44:
+			reg = (TTBCR2_ADDR_44 << TTBCR2_SEP_SHIFT);
+			break;
+		case 48:
+			reg = (TTBCR2_ADDR_48 << TTBCR2_SEP_SHIFT);
+			break;
+		}
+
+		switch (smmu->s1_output_size) {
+		case 32:
+			reg |= (TTBCR2_ADDR_32 << TTBCR2_PASIZE_SHIFT);
+			break;
+		case 36:
+			reg |= (TTBCR2_ADDR_36 << TTBCR2_PASIZE_SHIFT);
+			break;
+		case 39:
+		case 40:
+			reg |= (TTBCR2_ADDR_40 << TTBCR2_PASIZE_SHIFT);
+			break;
+		case 42:
+			reg |= (TTBCR2_ADDR_42 << TTBCR2_PASIZE_SHIFT);
+			break;
+		case 44:
+			reg |= (TTBCR2_ADDR_44 << TTBCR2_PASIZE_SHIFT);
+			break;
+		case 48:
+			reg |= (TTBCR2_ADDR_48 << TTBCR2_PASIZE_SHIFT);
+			break;
+		}
+
+		if (stage1)
+			writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR2);
+	}
+
+	/* TTBR0 */
+	arm_smmu_flush_pgtable(smmu, cfg->pgd,
+			       PTRS_PER_PGD * sizeof(pgd_t));
+	reg = __pa(cfg->pgd);
+	writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_LO);
+	reg = (phys_addr_t)__pa(cfg->pgd) >> 32;
+	if (stage1)
+		reg |= ARM_SMMU_CB_ASID(cfg) << TTBRn_HI_ASID_SHIFT;
+	writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_HI);
+
+	/*
+	 * TTBCR
+	 * We use long descriptor, with inner-shareable WBWA tables in TTBR0.
+	 */
+	if (smmu->version > ARM_SMMU_V1) {
+		if (PAGE_SIZE == SZ_4K)
+			reg = TTBCR_TG0_4K;
+		else
+			reg = TTBCR_TG0_64K;
+
+		if (!stage1) {
+			reg |= (64 - smmu->s2_input_size) << TTBCR_T0SZ_SHIFT;
+
+			switch (smmu->s2_output_size) {
+			case 32:
+				reg |= (TTBCR2_ADDR_32 << TTBCR_PASIZE_SHIFT);
+				break;
+			case 36:
+				reg |= (TTBCR2_ADDR_36 << TTBCR_PASIZE_SHIFT);
+				break;
+			case 40:
+				reg |= (TTBCR2_ADDR_40 << TTBCR_PASIZE_SHIFT);
+				break;
+			case 42:
+				reg |= (TTBCR2_ADDR_42 << TTBCR_PASIZE_SHIFT);
+				break;
+			case 44:
+				reg |= (TTBCR2_ADDR_44 << TTBCR_PASIZE_SHIFT);
+				break;
+			case 48:
+				reg |= (TTBCR2_ADDR_48 << TTBCR_PASIZE_SHIFT);
+				break;
+			}
+		} else {
+			reg |= (64 - smmu->s1_input_size) << TTBCR_T0SZ_SHIFT;
+		}
+	} else {
+		reg = 0;
+	}
+
+	reg |= TTBCR_EAE |
+	      (TTBCR_SH_IS << TTBCR_SH0_SHIFT) |
+	      (TTBCR_RGN_WBWA << TTBCR_ORGN0_SHIFT) |
+	      (TTBCR_RGN_WBWA << TTBCR_IRGN0_SHIFT);
+
+	if (!stage1)
+		reg |= (TTBCR_SL0_LVL_1 << TTBCR_SL0_SHIFT);
+
+	writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR);
+
+	/* MAIR0 (stage-1 only) */
+	if (stage1) {
+		reg = (MAIR_ATTR_NC << MAIR_ATTR_SHIFT(MAIR_ATTR_IDX_NC)) |
+		      (MAIR_ATTR_WBRWA << MAIR_ATTR_SHIFT(MAIR_ATTR_IDX_CACHE)) |
+		      (MAIR_ATTR_DEVICE << MAIR_ATTR_SHIFT(MAIR_ATTR_IDX_DEV));
+		writel_relaxed(reg, cb_base + ARM_SMMU_CB_S1_MAIR0);
+	}
+
+	/* SCTLR */
+	reg = SCTLR_CFCFG | SCTLR_CFIE | SCTLR_CFRE | SCTLR_M | SCTLR_EAE_SBOP;
+	if (stage1)
+		reg |= SCTLR_S1_ASIDPNE;
+#ifdef __BIG_ENDIAN
+	reg |= SCTLR_E;
+#endif
+	writel_relaxed(reg, cb_base + ARM_SMMU_CB_SCTLR);
+}
+
+static int arm_smmu_init_domain_context(struct iommu_domain *domain,
+					struct arm_smmu_device *smmu)
+{
+	int irq, start, ret = 0;
+	unsigned long flags;
+	struct arm_smmu_domain *smmu_domain = domain->priv;
+	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
+
+	spin_lock_irqsave(&smmu_domain->lock, flags);
+	if (smmu_domain->smmu)
+		goto out_unlock;
+
+	/*
+	 * Mapping the requested stage onto what we support is surprisingly
+	 * complicated, mainly because the spec allows S1+S2 SMMUs without
+	 * support for nested translation. That means we end up with the
+	 * following table:
+	 *
+	 * Requested        Supported        Actual
+	 *     S1               N              S1
+	 *     S1             S1+S2            S1
+	 *     S1               S2             S2
+	 *     S1               S1             S1
+	 *     N                N              N
+	 *     N              S1+S2            S2
+	 *     N                S2             S2
+	 *     N                S1             S1
+	 *
+	 * Note that you can't actually request stage-2 mappings.
+	 */
+	if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1))
+		smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
+	if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2))
+		smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
+
+	switch (smmu_domain->stage) {
+	case ARM_SMMU_DOMAIN_S1:
+		cfg->cbar = CBAR_TYPE_S1_TRANS_S2_BYPASS;
+		start = smmu->num_s2_context_banks;
+		break;
+	case ARM_SMMU_DOMAIN_NESTED:
+		/*
+		 * We will likely want to change this if/when KVM gets
+		 * involved.
+		 */
+	case ARM_SMMU_DOMAIN_S2:
+		cfg->cbar = CBAR_TYPE_S2_TRANS;
+		start = 0;
+		break;
+	default:
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
+	ret = __arm_smmu_alloc_bitmap(smmu->context_map, start,
+				      smmu->num_context_banks);
+	if (IS_ERR_VALUE(ret))
+		goto out_unlock;
+
+	cfg->cbndx = ret;
+	if (smmu->version == ARM_SMMU_V1) {
+		cfg->irptndx = atomic_inc_return(&smmu->irptndx);
+		cfg->irptndx %= smmu->num_context_irqs;
+	} else {
+		cfg->irptndx = cfg->cbndx;
+	}
+
+	ACCESS_ONCE(smmu_domain->smmu) = smmu;
+	arm_smmu_init_context_bank(smmu_domain);
+	spin_unlock_irqrestore(&smmu_domain->lock, flags);
+
+	irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx];
+	ret = request_irq(irq, arm_smmu_context_fault, IRQF_SHARED,
+			  "arm-smmu-context-fault", domain);
+	if (IS_ERR_VALUE(ret)) {
+		dev_err(smmu->dev, "failed to request context IRQ %d (%u)\n",
+			cfg->irptndx, irq);
+		cfg->irptndx = INVALID_IRPTNDX;
+	}
+
+	return 0;
+
+out_unlock:
+	spin_unlock_irqrestore(&smmu_domain->lock, flags);
+	return ret;
+}
+
+static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
+{
+	struct arm_smmu_domain *smmu_domain = domain->priv;
+	struct arm_smmu_device *smmu = smmu_domain->smmu;
+	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
+	void __iomem *cb_base;
+	int irq;
+
+	if (!smmu)
+		return;
+
+	/* Disable the context bank and nuke the TLB before freeing it. */
+	cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
+	writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR);
+	arm_smmu_tlb_inv_context(smmu_domain);
+
+	if (cfg->irptndx != INVALID_IRPTNDX) {
+		irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx];
+		free_irq(irq, domain);
+	}
+
+	__arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx);
+}
+
+static int arm_smmu_domain_init(struct iommu_domain *domain)
+{
+	struct arm_smmu_domain *smmu_domain;
+	pgd_t *pgd;
+
+	/*
+	 * Allocate the domain and initialise some of its data structures.
+	 * We can't really do anything meaningful until we've added a
+	 * master.
+	 */
+	smmu_domain = kzalloc(sizeof(*smmu_domain), GFP_KERNEL);
+	if (!smmu_domain)
+		return -ENOMEM;
+
+	pgd = kcalloc(PTRS_PER_PGD, sizeof(pgd_t), GFP_KERNEL);
+	if (!pgd)
+		goto out_free_domain;
+	smmu_domain->cfg.pgd = pgd;
+
+	spin_lock_init(&smmu_domain->lock);
+	domain->priv = smmu_domain;
+	return 0;
+
+out_free_domain:
+	kfree(smmu_domain);
+	return -ENOMEM;
+}
+
+static void arm_smmu_free_ptes(pmd_t *pmd)
+{
+	pgtable_t table = pmd_pgtable(*pmd);
+
+	__free_page(table);
+}
+
+static void arm_smmu_free_pmds(pud_t *pud)
+{
+	int i;
+	pmd_t *pmd, *pmd_base = pmd_offset(pud, 0);
+
+	pmd = pmd_base;
+	for (i = 0; i < PTRS_PER_PMD; ++i) {
+		if (pmd_none(*pmd))
+			continue;
+
+		arm_smmu_free_ptes(pmd);
+		pmd++;
+	}
+
+	pmd_free(NULL, pmd_base);
+}
+
+static void arm_smmu_free_puds(pgd_t *pgd)
+{
+	int i;
+	pud_t *pud, *pud_base = pud_offset(pgd, 0);
+
+	pud = pud_base;
+	for (i = 0; i < PTRS_PER_PUD; ++i) {
+		if (pud_none(*pud))
+			continue;
+
+		arm_smmu_free_pmds(pud);
+		pud++;
+	}
+
+	pud_free(NULL, pud_base);
+}
+
+static void arm_smmu_free_pgtables(struct arm_smmu_domain *smmu_domain)
+{
+	int i;
+	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
+	pgd_t *pgd, *pgd_base = cfg->pgd;
+
+	/*
+	 * Recursively free the page tables for this domain. We don't
+	 * care about speculative TLB filling because the tables should
+	 * not be active in any context bank at this point (SCTLR.M is 0).
+	 */
+	pgd = pgd_base;
+	for (i = 0; i < PTRS_PER_PGD; ++i) {
+		if (pgd_none(*pgd))
+			continue;
+		arm_smmu_free_puds(pgd);
+		pgd++;
+	}
+
+	kfree(pgd_base);
+}
+
+static void arm_smmu_domain_destroy(struct iommu_domain *domain)
+{
+	struct arm_smmu_domain *smmu_domain = domain->priv;
+
+	/*
+	 * Free the domain resources. We assume that all devices have
+	 * already been detached.
+	 */
+	arm_smmu_destroy_domain_context(domain);
+	arm_smmu_free_pgtables(smmu_domain);
+	kfree(smmu_domain);
+}
+
+static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu,
+					  struct arm_smmu_master_cfg *cfg)
+{
+	int i;
+	struct arm_smmu_smr *smrs;
+	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+
+	if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH))
+		return 0;
+
+	if (cfg->smrs)
+		return -EEXIST;
+
+	smrs = kmalloc_array(cfg->num_streamids, sizeof(*smrs), GFP_KERNEL);
+	if (!smrs) {
+		dev_err(smmu->dev, "failed to allocate %d SMRs\n",
+			cfg->num_streamids);
+		return -ENOMEM;
+	}
+
+	/* Allocate the SMRs on the SMMU */
+	for (i = 0; i < cfg->num_streamids; ++i) {
+		int idx = __arm_smmu_alloc_bitmap(smmu->smr_map, 0,
+						  smmu->num_mapping_groups);
+		if (IS_ERR_VALUE(idx)) {
+			dev_err(smmu->dev, "failed to allocate free SMR\n");
+			goto err_free_smrs;
+		}
+
+		smrs[i] = (struct arm_smmu_smr) {
+			.idx	= idx,
+			.mask	= 0, /* We don't currently share SMRs */
+			.id	= cfg->streamids[i],
+		};
+	}
+
+	/* It worked! Now, poke the actual hardware */
+	for (i = 0; i < cfg->num_streamids; ++i) {
+		u32 reg = SMR_VALID | smrs[i].id << SMR_ID_SHIFT |
+			  smrs[i].mask << SMR_MASK_SHIFT;
+		writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_SMR(smrs[i].idx));
+	}
+
+	cfg->smrs = smrs;
+	return 0;
+
+err_free_smrs:
+	while (--i >= 0)
+		__arm_smmu_free_bitmap(smmu->smr_map, smrs[i].idx);
+	kfree(smrs);
+	return -ENOSPC;
+}
+
+static void arm_smmu_master_free_smrs(struct arm_smmu_device *smmu,
+				      struct arm_smmu_master_cfg *cfg)
+{
+	int i;
+	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+	struct arm_smmu_smr *smrs = cfg->smrs;
+
+	if (!smrs)
+		return;
+
+	/* Invalidate the SMRs before freeing back to the allocator */
+	for (i = 0; i < cfg->num_streamids; ++i) {
+		u8 idx = smrs[i].idx;
+
+		writel_relaxed(~SMR_VALID, gr0_base + ARM_SMMU_GR0_SMR(idx));
+		__arm_smmu_free_bitmap(smmu->smr_map, idx);
+	}
+
+	cfg->smrs = NULL;
+	kfree(smrs);
+}
+
+static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
+				      struct arm_smmu_master_cfg *cfg)
+{
+	int i, ret;
+	struct arm_smmu_device *smmu = smmu_domain->smmu;
+	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+
+	/* Devices in an IOMMU group may already be configured */
+	ret = arm_smmu_master_configure_smrs(smmu, cfg);
+	if (ret)
+		return ret == -EEXIST ? 0 : ret;
+
+	for (i = 0; i < cfg->num_streamids; ++i) {
+		u32 idx, s2cr;
+
+		idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
+		s2cr = S2CR_TYPE_TRANS |
+		       (smmu_domain->cfg.cbndx << S2CR_CBNDX_SHIFT);
+		writel_relaxed(s2cr, gr0_base + ARM_SMMU_GR0_S2CR(idx));
+	}
+
+	return 0;
+}
+
+static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
+					  struct arm_smmu_master_cfg *cfg)
+{
+	int i;
+	struct arm_smmu_device *smmu = smmu_domain->smmu;
+	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+
+	/* An IOMMU group is torn down by the first device to be removed */
+	if ((smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) && !cfg->smrs)
+		return;
+
+	/*
+	 * We *must* clear the S2CR first, because freeing the SMR means
+	 * that it can be re-allocated immediately.
+	 */
+	for (i = 0; i < cfg->num_streamids; ++i) {
+		u32 idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
+
+		writel_relaxed(S2CR_TYPE_BYPASS,
+			       gr0_base + ARM_SMMU_GR0_S2CR(idx));
+	}
+
+	arm_smmu_master_free_smrs(smmu, cfg);
+}
+
+static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
+{
+	int ret;
+	struct arm_smmu_domain *smmu_domain = domain->priv;
+	struct arm_smmu_device *smmu, *dom_smmu;
+	struct arm_smmu_master_cfg *cfg;
+
+	smmu = find_smmu_for_device(dev);
+	if (!smmu) {
+		dev_err(dev, "cannot attach to SMMU, is it on the same bus?\n");
+		return -ENXIO;
+	}
+
+	if (dev->archdata.iommu) {
+		dev_err(dev, "already attached to IOMMU domain\n");
+		return -EEXIST;
+	}
+
+	/*
+	 * Sanity check the domain. We don't support domains across
+	 * different SMMUs.
+	 */
+	dom_smmu = ACCESS_ONCE(smmu_domain->smmu);
+	if (!dom_smmu) {
+		/* Now that we have a master, we can finalise the domain */
+		ret = arm_smmu_init_domain_context(domain, smmu);
+		if (IS_ERR_VALUE(ret))
+			return ret;
+
+		dom_smmu = smmu_domain->smmu;
+	}
+
+	if (dom_smmu != smmu) {
+		dev_err(dev,
+			"cannot attach to SMMU %s whilst already attached to domain on SMMU %s\n",
+			dev_name(smmu_domain->smmu->dev), dev_name(smmu->dev));
+		return -EINVAL;
+	}
+
+	/* Looks ok, so add the device to the domain */
+	cfg = find_smmu_master_cfg(dev);
+	if (!cfg)
+		return -ENODEV;
+
+	ret = arm_smmu_domain_add_master(smmu_domain, cfg);
+	if (!ret)
+		dev->archdata.iommu = domain;
+	return ret;
+}
+
+static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
+{
+	struct arm_smmu_domain *smmu_domain = domain->priv;
+	struct arm_smmu_master_cfg *cfg;
+
+	cfg = find_smmu_master_cfg(dev);
+	if (!cfg)
+		return;
+
+	dev->archdata.iommu = NULL;
+	arm_smmu_domain_remove_master(smmu_domain, cfg);
+}
+
+static bool arm_smmu_pte_is_contiguous_range(unsigned long addr,
+					     unsigned long end)
+{
+	return !(addr & ~ARM_SMMU_PTE_CONT_MASK) &&
+		(addr + ARM_SMMU_PTE_CONT_SIZE <= end);
+}
+
+static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd,
+				   unsigned long addr, unsigned long end,
+				   unsigned long pfn, int prot, int stage)
+{
+	pte_t *pte, *start;
+	pteval_t pteval = ARM_SMMU_PTE_PAGE | ARM_SMMU_PTE_AF;
+
+	if (pmd_none(*pmd)) {
+		/* Allocate a new set of tables */
+		pgtable_t table = alloc_page(GFP_ATOMIC|__GFP_ZERO);
+
+		if (!table)
+			return -ENOMEM;
+
+		arm_smmu_flush_pgtable(smmu, page_address(table), PAGE_SIZE);
+		pmd_populate(NULL, pmd, table);
+		arm_smmu_flush_pgtable(smmu, pmd, sizeof(*pmd));
+	}
+
+	if (stage == 1) {
+		pteval |= ARM_SMMU_PTE_AP_UNPRIV | ARM_SMMU_PTE_nG;
+		if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ))
+			pteval |= ARM_SMMU_PTE_AP_RDONLY;
+
+		if (prot & IOMMU_CACHE)
+			pteval |= (MAIR_ATTR_IDX_CACHE <<
+				   ARM_SMMU_PTE_ATTRINDX_SHIFT);
+	} else {
+		pteval |= ARM_SMMU_PTE_HAP_FAULT;
+		if (prot & IOMMU_READ)
+			pteval |= ARM_SMMU_PTE_HAP_READ;
+		if (prot & IOMMU_WRITE)
+			pteval |= ARM_SMMU_PTE_HAP_WRITE;
+		if (prot & IOMMU_CACHE)
+			pteval |= ARM_SMMU_PTE_MEMATTR_OIWB;
+		else
+			pteval |= ARM_SMMU_PTE_MEMATTR_NC;
+	}
+
+	if (prot & IOMMU_NOEXEC)
+		pteval |= ARM_SMMU_PTE_XN;
+
+	/* If no access, create a faulting entry to avoid TLB fills */
+	if (!(prot & (IOMMU_READ | IOMMU_WRITE)))
+		pteval &= ~ARM_SMMU_PTE_PAGE;
+
+	pteval |= ARM_SMMU_PTE_SH_IS;
+	start = pmd_page_vaddr(*pmd) + pte_index(addr);
+	pte = start;
+
+	/*
+	 * Install the page table entries. This is fairly complicated
+	 * since we attempt to make use of the contiguous hint in the
+	 * ptes where possible. The contiguous hint indicates a series
+	 * of ARM_SMMU_PTE_CONT_ENTRIES ptes mapping a physically
+	 * contiguous region with the following constraints:
+	 *
+	 *   - The region start is aligned to ARM_SMMU_PTE_CONT_SIZE
+	 *   - Each pte in the region has the contiguous hint bit set
+	 *
+	 * This complicates unmapping (also handled by this code, when
+	 * neither IOMMU_READ or IOMMU_WRITE are set) because it is
+	 * possible, yet highly unlikely, that a client may unmap only
+	 * part of a contiguous range. This requires clearing of the
+	 * contiguous hint bits in the range before installing the new
+	 * faulting entries.
+	 *
+	 * Note that re-mapping an address range without first unmapping
+	 * it is not supported, so TLB invalidation is not required here
+	 * and is instead performed at unmap and domain-init time.
+	 */
+	do {
+		int i = 1;
+
+		pteval &= ~ARM_SMMU_PTE_CONT;
+
+		if (arm_smmu_pte_is_contiguous_range(addr, end)) {
+			i = ARM_SMMU_PTE_CONT_ENTRIES;
+			pteval |= ARM_SMMU_PTE_CONT;
+		} else if (pte_val(*pte) &
+			   (ARM_SMMU_PTE_CONT | ARM_SMMU_PTE_PAGE)) {
+			int j;
+			pte_t *cont_start;
+			unsigned long idx = pte_index(addr);
+
+			idx &= ~(ARM_SMMU_PTE_CONT_ENTRIES - 1);
+			cont_start = pmd_page_vaddr(*pmd) + idx;
+			for (j = 0; j < ARM_SMMU_PTE_CONT_ENTRIES; ++j)
+				pte_val(*(cont_start + j)) &=
+					~ARM_SMMU_PTE_CONT;
+
+			arm_smmu_flush_pgtable(smmu, cont_start,
+					       sizeof(*pte) *
+					       ARM_SMMU_PTE_CONT_ENTRIES);
+		}
+
+		do {
+			*pte = pfn_pte(pfn, __pgprot(pteval));
+		} while (pte++, pfn++, addr += PAGE_SIZE, --i);
+	} while (addr != end);
+
+	arm_smmu_flush_pgtable(smmu, start, sizeof(*pte) * (pte - start));
+	return 0;
+}
+
+static int arm_smmu_alloc_init_pmd(struct arm_smmu_device *smmu, pud_t *pud,
+				   unsigned long addr, unsigned long end,
+				   phys_addr_t phys, int prot, int stage)
+{
+	int ret;
+	pmd_t *pmd;
+	unsigned long next, pfn = __phys_to_pfn(phys);
+
+#ifndef __PAGETABLE_PMD_FOLDED
+	if (pud_none(*pud)) {
+		pmd = (pmd_t *)get_zeroed_page(GFP_ATOMIC);
+		if (!pmd)
+			return -ENOMEM;
+
+		arm_smmu_flush_pgtable(smmu, pmd, PAGE_SIZE);
+		pud_populate(NULL, pud, pmd);
+		arm_smmu_flush_pgtable(smmu, pud, sizeof(*pud));
+
+		pmd += pmd_index(addr);
+	} else
+#endif
+		pmd = pmd_offset(pud, addr);
+
+	do {
+		next = pmd_addr_end(addr, end);
+		ret = arm_smmu_alloc_init_pte(smmu, pmd, addr, next, pfn,
+					      prot, stage);
+		phys += next - addr;
+		pfn = __phys_to_pfn(phys);
+	} while (pmd++, addr = next, addr < end);
+
+	return ret;
+}
+
+static int arm_smmu_alloc_init_pud(struct arm_smmu_device *smmu, pgd_t *pgd,
+				   unsigned long addr, unsigned long end,
+				   phys_addr_t phys, int prot, int stage)
+{
+	int ret = 0;
+	pud_t *pud;
+	unsigned long next;
+
+#ifndef __PAGETABLE_PUD_FOLDED
+	if (pgd_none(*pgd)) {
+		pud = (pud_t *)get_zeroed_page(GFP_ATOMIC);
+		if (!pud)
+			return -ENOMEM;
+
+		arm_smmu_flush_pgtable(smmu, pud, PAGE_SIZE);
+		pgd_populate(NULL, pgd, pud);
+		arm_smmu_flush_pgtable(smmu, pgd, sizeof(*pgd));
+
+		pud += pud_index(addr);
+	} else
+#endif
+		pud = pud_offset(pgd, addr);
+
+	do {
+		next = pud_addr_end(addr, end);
+		ret = arm_smmu_alloc_init_pmd(smmu, pud, addr, next, phys,
+					      prot, stage);
+		phys += next - addr;
+	} while (pud++, addr = next, addr < end);
+
+	return ret;
+}
+
+static int arm_smmu_handle_mapping(struct arm_smmu_domain *smmu_domain,
+				   unsigned long iova, phys_addr_t paddr,
+				   size_t size, int prot)
+{
+	int ret, stage;
+	unsigned long end;
+	phys_addr_t input_mask, output_mask;
+	struct arm_smmu_device *smmu = smmu_domain->smmu;
+	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
+	pgd_t *pgd = cfg->pgd;
+	unsigned long flags;
+
+	if (cfg->cbar == CBAR_TYPE_S2_TRANS) {
+		stage = 2;
+		input_mask = (1ULL << smmu->s2_input_size) - 1;
+		output_mask = (1ULL << smmu->s2_output_size) - 1;
+	} else {
+		stage = 1;
+		input_mask = (1ULL << smmu->s1_input_size) - 1;
+		output_mask = (1ULL << smmu->s1_output_size) - 1;
+	}
+
+	if (!pgd)
+		return -EINVAL;
+
+	if (size & ~PAGE_MASK)
+		return -EINVAL;
+
+	if ((phys_addr_t)iova & ~input_mask)
+		return -ERANGE;
+
+	if (paddr & ~output_mask)
+		return -ERANGE;
+
+	spin_lock_irqsave(&smmu_domain->lock, flags);
+	pgd += pgd_index(iova);
+	end = iova + size;
+	do {
+		unsigned long next = pgd_addr_end(iova, end);
+
+		ret = arm_smmu_alloc_init_pud(smmu, pgd, iova, next, paddr,
+					      prot, stage);
+		if (ret)
+			goto out_unlock;
+
+		paddr += next - iova;
+		iova = next;
+	} while (pgd++, iova != end);
+
+out_unlock:
+	spin_unlock_irqrestore(&smmu_domain->lock, flags);
+
+	return ret;
+}
+
+static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
+			phys_addr_t paddr, size_t size, int prot)
+{
+	struct arm_smmu_domain *smmu_domain = domain->priv;
+
+	if (!smmu_domain)
+		return -ENODEV;
+
+	return arm_smmu_handle_mapping(smmu_domain, iova, paddr, size, prot);
+}
+
+static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
+			     size_t size)
+{
+	int ret;
+	struct arm_smmu_domain *smmu_domain = domain->priv;
+
+	ret = arm_smmu_handle_mapping(smmu_domain, iova, 0, size, 0);
+	arm_smmu_tlb_inv_context(smmu_domain);
+	return ret ? 0 : size;
+}
+
+static phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain,
+					 dma_addr_t iova)
+{
+	pgd_t *pgdp, pgd;
+	pud_t pud;
+	pmd_t pmd;
+	pte_t pte;
+	struct arm_smmu_domain *smmu_domain = domain->priv;
+	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
+
+	pgdp = cfg->pgd;
+	if (!pgdp)
+		return 0;
+
+	pgd = *(pgdp + pgd_index(iova));
+	if (pgd_none(pgd))
+		return 0;
+
+	pud = *pud_offset(&pgd, iova);
+	if (pud_none(pud))
+		return 0;
+
+	pmd = *pmd_offset(&pud, iova);
+	if (pmd_none(pmd))
+		return 0;
+
+	pte = *(pmd_page_vaddr(pmd) + pte_index(iova));
+	if (pte_none(pte))
+		return 0;
+
+	return __pfn_to_phys(pte_pfn(pte)) | (iova & ~PAGE_MASK);
+}
+
+static bool arm_smmu_capable(enum iommu_cap cap)
+{
+	switch (cap) {
+	case IOMMU_CAP_CACHE_COHERENCY:
+		/*
+		 * Return true here as the SMMU can always send out coherent
+		 * requests.
+		 */
+		return true;
+	case IOMMU_CAP_INTR_REMAP:
+		return true; /* MSIs are just memory writes */
+	case IOMMU_CAP_NOEXEC:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data)
+{
+	*((u16 *)data) = alias;
+	return 0; /* Continue walking */
+}
+
+static void __arm_smmu_release_pci_iommudata(void *data)
+{
+	kfree(data);
+}
+
+static int arm_smmu_add_device(struct device *dev)
+{
+	struct arm_smmu_device *smmu;
+	struct arm_smmu_master_cfg *cfg;
+	struct iommu_group *group;
+	void (*releasefn)(void *) = NULL;
+	int ret;
+
+	smmu = find_smmu_for_device(dev);
+	if (!smmu)
+		return -ENODEV;
+
+	group = iommu_group_alloc();
+	if (IS_ERR(group)) {
+		dev_err(dev, "Failed to allocate IOMMU group\n");
+		return PTR_ERR(group);
+	}
+
+	if (dev_is_pci(dev)) {
+		struct pci_dev *pdev = to_pci_dev(dev);
+
+		cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+		if (!cfg) {
+			ret = -ENOMEM;
+			goto out_put_group;
+		}
+
+		cfg->num_streamids = 1;
+		/*
+		 * Assume Stream ID == Requester ID for now.
+		 * We need a way to describe the ID mappings in FDT.
+		 */
+		pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid,
+				       &cfg->streamids[0]);
+		releasefn = __arm_smmu_release_pci_iommudata;
+	} else {
+		struct arm_smmu_master *master;
+
+		master = find_smmu_master(smmu, dev->of_node);
+		if (!master) {
+			ret = -ENODEV;
+			goto out_put_group;
+		}
+
+		cfg = &master->cfg;
+	}
+
+	iommu_group_set_iommudata(group, cfg, releasefn);
+	ret = iommu_group_add_device(group, dev);
+
+out_put_group:
+	iommu_group_put(group);
+	return ret;
+}
+
+static void arm_smmu_remove_device(struct device *dev)
+{
+	iommu_group_remove_device(dev);
+}
+
+static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
+				    enum iommu_attr attr, void *data)
+{
+	struct arm_smmu_domain *smmu_domain = domain->priv;
+
+	switch (attr) {
+	case DOMAIN_ATTR_NESTING:
+		*(int *)data = (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED);
+		return 0;
+	default:
+		return -ENODEV;
+	}
+}
+
+static int arm_smmu_domain_set_attr(struct iommu_domain *domain,
+				    enum iommu_attr attr, void *data)
+{
+	struct arm_smmu_domain *smmu_domain = domain->priv;
+
+	switch (attr) {
+	case DOMAIN_ATTR_NESTING:
+		if (smmu_domain->smmu)
+			return -EPERM;
+		if (*(int *)data)
+			smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED;
+		else
+			smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
+
+		return 0;
+	default:
+		return -ENODEV;
+	}
+}
+
+static const struct iommu_ops arm_smmu_ops = {
+	.capable		= arm_smmu_capable,
+	.domain_init		= arm_smmu_domain_init,
+	.domain_destroy		= arm_smmu_domain_destroy,
+	.attach_dev		= arm_smmu_attach_dev,
+	.detach_dev		= arm_smmu_detach_dev,
+	.map			= arm_smmu_map,
+	.unmap			= arm_smmu_unmap,
+	.map_sg			= default_iommu_map_sg,
+	.iova_to_phys		= arm_smmu_iova_to_phys,
+	.add_device		= arm_smmu_add_device,
+	.remove_device		= arm_smmu_remove_device,
+	.domain_get_attr	= arm_smmu_domain_get_attr,
+	.domain_set_attr	= arm_smmu_domain_set_attr,
+	.pgsize_bitmap		= (SECTION_SIZE |
+				   ARM_SMMU_PTE_CONT_SIZE |
+				   PAGE_SIZE),
+};
+
+static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
+{
+	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+	void __iomem *cb_base;
+	int i = 0;
+	u32 reg;
+
+	/* clear global FSR */
+	reg = readl_relaxed(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR);
+	writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR);
+
+	/* Mark all SMRn as invalid and all S2CRn as bypass */
+	for (i = 0; i < smmu->num_mapping_groups; ++i) {
+		writel_relaxed(0, gr0_base + ARM_SMMU_GR0_SMR(i));
+		writel_relaxed(S2CR_TYPE_BYPASS,
+			gr0_base + ARM_SMMU_GR0_S2CR(i));
+	}
+
+	/* Make sure all context banks are disabled and clear CB_FSR  */
+	for (i = 0; i < smmu->num_context_banks; ++i) {
+		cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, i);
+		writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR);
+		writel_relaxed(FSR_FAULT, cb_base + ARM_SMMU_CB_FSR);
+	}
+
+	/* Invalidate the TLB, just in case */
+	writel_relaxed(0, gr0_base + ARM_SMMU_GR0_STLBIALL);
+	writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLH);
+	writel_relaxed(0, gr0_base + ARM_SMMU_GR0_TLBIALLNSNH);
+
+	reg = readl_relaxed(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
+
+	/* Enable fault reporting */
+	reg |= (sCR0_GFRE | sCR0_GFIE | sCR0_GCFGFRE | sCR0_GCFGFIE);
+
+	/* Disable TLB broadcasting. */
+	reg |= (sCR0_VMIDPNE | sCR0_PTM);
+
+	/* Enable client access, but bypass when no mapping is found */
+	reg &= ~(sCR0_CLIENTPD | sCR0_USFCFG);
+
+	/* Disable forced broadcasting */
+	reg &= ~sCR0_FB;
+
+	/* Don't upgrade barriers */
+	reg &= ~(sCR0_BSU_MASK << sCR0_BSU_SHIFT);
+
+	/* Push the button */
+	arm_smmu_tlb_sync(smmu);
+	writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
+}
+
+static int arm_smmu_id_size_to_bits(int size)
+{
+	switch (size) {
+	case 0:
+		return 32;
+	case 1:
+		return 36;
+	case 2:
+		return 40;
+	case 3:
+		return 42;
+	case 4:
+		return 44;
+	case 5:
+	default:
+		return 48;
+	}
+}
+
+static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
+{
+	unsigned long size;
+	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
+	u32 id;
+
+	dev_notice(smmu->dev, "probing hardware configuration...\n");
+	dev_notice(smmu->dev, "SMMUv%d with:\n", smmu->version);
+
+	/* ID0 */
+	id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID0);
+#ifndef CONFIG_64BIT
+	if (((id >> ID0_PTFS_SHIFT) & ID0_PTFS_MASK) == ID0_PTFS_V8_ONLY) {
+		dev_err(smmu->dev, "\tno v7 descriptor support!\n");
+		return -ENODEV;
+	}
+#endif
+
+	/* Restrict available stages based on module parameter */
+	if (force_stage == 1)
+		id &= ~(ID0_S2TS | ID0_NTS);
+	else if (force_stage == 2)
+		id &= ~(ID0_S1TS | ID0_NTS);
+
+	if (id & ID0_S1TS) {
+		smmu->features |= ARM_SMMU_FEAT_TRANS_S1;
+		dev_notice(smmu->dev, "\tstage 1 translation\n");
+	}
+
+	if (id & ID0_S2TS) {
+		smmu->features |= ARM_SMMU_FEAT_TRANS_S2;
+		dev_notice(smmu->dev, "\tstage 2 translation\n");
+	}
+
+	if (id & ID0_NTS) {
+		smmu->features |= ARM_SMMU_FEAT_TRANS_NESTED;
+		dev_notice(smmu->dev, "\tnested translation\n");
+	}
+
+	if (!(smmu->features &
+		(ARM_SMMU_FEAT_TRANS_S1 | ARM_SMMU_FEAT_TRANS_S2))) {
+		dev_err(smmu->dev, "\tno translation support!\n");
+		return -ENODEV;
+	}
+
+	if (id & ID0_CTTW) {
+		smmu->features |= ARM_SMMU_FEAT_COHERENT_WALK;
+		dev_notice(smmu->dev, "\tcoherent table walk\n");
+	}
+
+	if (id & ID0_SMS) {
+		u32 smr, sid, mask;
+
+		smmu->features |= ARM_SMMU_FEAT_STREAM_MATCH;
+		smmu->num_mapping_groups = (id >> ID0_NUMSMRG_SHIFT) &
+					   ID0_NUMSMRG_MASK;
+		if (smmu->num_mapping_groups == 0) {
+			dev_err(smmu->dev,
+				"stream-matching supported, but no SMRs present!\n");
+			return -ENODEV;
+		}
+
+		smr = SMR_MASK_MASK << SMR_MASK_SHIFT;
+		smr |= (SMR_ID_MASK << SMR_ID_SHIFT);
+		writel_relaxed(smr, gr0_base + ARM_SMMU_GR0_SMR(0));
+		smr = readl_relaxed(gr0_base + ARM_SMMU_GR0_SMR(0));
+
+		mask = (smr >> SMR_MASK_SHIFT) & SMR_MASK_MASK;
+		sid = (smr >> SMR_ID_SHIFT) & SMR_ID_MASK;
+		if ((mask & sid) != sid) {
+			dev_err(smmu->dev,
+				"SMR mask bits (0x%x) insufficient for ID field (0x%x)\n",
+				mask, sid);
+			return -ENODEV;
+		}
+
+		dev_notice(smmu->dev,
+			   "\tstream matching with %u register groups, mask 0x%x",
+			   smmu->num_mapping_groups, mask);
+	} else {
+		smmu->num_mapping_groups = (id >> ID0_NUMSIDB_SHIFT) &
+					   ID0_NUMSIDB_MASK;
+	}
+
+	/* ID1 */
+	id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID1);
+	smmu->pgshift = (id & ID1_PAGESIZE) ? 16 : 12;
+
+	/* Check for size mismatch of SMMU address space from mapped region */
+	size = 1 <<
+		(((id >> ID1_NUMPAGENDXB_SHIFT) & ID1_NUMPAGENDXB_MASK) + 1);
+	size *= 2 << smmu->pgshift;
+	if (smmu->size != size)
+		dev_warn(smmu->dev,
+			"SMMU address space size (0x%lx) differs from mapped region size (0x%lx)!\n",
+			size, smmu->size);
+
+	smmu->num_s2_context_banks = (id >> ID1_NUMS2CB_SHIFT) &
+				      ID1_NUMS2CB_MASK;
+	smmu->num_context_banks = (id >> ID1_NUMCB_SHIFT) & ID1_NUMCB_MASK;
+	if (smmu->num_s2_context_banks > smmu->num_context_banks) {
+		dev_err(smmu->dev, "impossible number of S2 context banks!\n");
+		return -ENODEV;
+	}
+	dev_notice(smmu->dev, "\t%u context banks (%u stage-2 only)\n",
+		   smmu->num_context_banks, smmu->num_s2_context_banks);
+
+	/* ID2 */
+	id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID2);
+	size = arm_smmu_id_size_to_bits((id >> ID2_IAS_SHIFT) & ID2_IAS_MASK);
+	smmu->s1_output_size = min_t(unsigned long, PHYS_MASK_SHIFT, size);
+
+	/* Stage-2 input size limited due to pgd allocation (PTRS_PER_PGD) */
+#ifdef CONFIG_64BIT
+	smmu->s2_input_size = min_t(unsigned long, VA_BITS, size);
+#else
+	smmu->s2_input_size = min(32UL, size);
+#endif
+
+	/* The stage-2 output mask is also applied for bypass */
+	size = arm_smmu_id_size_to_bits((id >> ID2_OAS_SHIFT) & ID2_OAS_MASK);
+	smmu->s2_output_size = min_t(unsigned long, PHYS_MASK_SHIFT, size);
+
+	if (smmu->version == ARM_SMMU_V1) {
+		smmu->s1_input_size = 32;
+	} else {
+#ifdef CONFIG_64BIT
+		size = (id >> ID2_UBS_SHIFT) & ID2_UBS_MASK;
+		size = min(VA_BITS, arm_smmu_id_size_to_bits(size));
+#else
+		size = 32;
+#endif
+		smmu->s1_input_size = size;
+
+		if ((PAGE_SIZE == SZ_4K && !(id & ID2_PTFS_4K)) ||
+		    (PAGE_SIZE == SZ_64K && !(id & ID2_PTFS_64K)) ||
+		    (PAGE_SIZE != SZ_4K && PAGE_SIZE != SZ_64K)) {
+			dev_err(smmu->dev, "CPU page size 0x%lx unsupported\n",
+				PAGE_SIZE);
+			return -ENODEV;
+		}
+	}
+
+	if (smmu->features & ARM_SMMU_FEAT_TRANS_S1)
+		dev_notice(smmu->dev, "\tStage-1: %lu-bit VA -> %lu-bit IPA\n",
+			   smmu->s1_input_size, smmu->s1_output_size);
+
+	if (smmu->features & ARM_SMMU_FEAT_TRANS_S2)
+		dev_notice(smmu->dev, "\tStage-2: %lu-bit IPA -> %lu-bit PA\n",
+			   smmu->s2_input_size, smmu->s2_output_size);
+
+	return 0;
+}
+
+static const struct of_device_id arm_smmu_of_match[] = {
+	{ .compatible = "arm,smmu-v1", .data = (void *)ARM_SMMU_V1 },
+	{ .compatible = "arm,smmu-v2", .data = (void *)ARM_SMMU_V2 },
+	{ .compatible = "arm,mmu-400", .data = (void *)ARM_SMMU_V1 },
+	{ .compatible = "arm,mmu-401", .data = (void *)ARM_SMMU_V1 },
+	{ .compatible = "arm,mmu-500", .data = (void *)ARM_SMMU_V2 },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
+
+static int arm_smmu_device_dt_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *of_id;
+	struct resource *res;
+	struct arm_smmu_device *smmu;
+	struct device *dev = &pdev->dev;
+	struct rb_node *node;
+	struct of_phandle_args masterspec;
+	int num_irqs, i, err;
+
+	smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
+	if (!smmu) {
+		dev_err(dev, "failed to allocate arm_smmu_device\n");
+		return -ENOMEM;
+	}
+	smmu->dev = dev;
+
+	of_id = of_match_node(arm_smmu_of_match, dev->of_node);
+	smmu->version = (enum arm_smmu_arch_version)of_id->data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	smmu->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(smmu->base))
+		return PTR_ERR(smmu->base);
+	smmu->size = resource_size(res);
+
+	if (of_property_read_u32(dev->of_node, "#global-interrupts",
+				 &smmu->num_global_irqs)) {
+		dev_err(dev, "missing #global-interrupts property\n");
+		return -ENODEV;
+	}
+
+	num_irqs = 0;
+	while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, num_irqs))) {
+		num_irqs++;
+		if (num_irqs > smmu->num_global_irqs)
+			smmu->num_context_irqs++;
+	}
+
+	if (!smmu->num_context_irqs) {
+		dev_err(dev, "found %d interrupts but expected at least %d\n",
+			num_irqs, smmu->num_global_irqs + 1);
+		return -ENODEV;
+	}
+
+	smmu->irqs = devm_kzalloc(dev, sizeof(*smmu->irqs) * num_irqs,
+				  GFP_KERNEL);
+	if (!smmu->irqs) {
+		dev_err(dev, "failed to allocate %d irqs\n", num_irqs);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < num_irqs; ++i) {
+		int irq = platform_get_irq(pdev, i);
+
+		if (irq < 0) {
+			dev_err(dev, "failed to get irq index %d\n", i);
+			return -ENODEV;
+		}
+		smmu->irqs[i] = irq;
+	}
+
+	err = arm_smmu_device_cfg_probe(smmu);
+	if (err)
+		return err;
+
+	i = 0;
+	smmu->masters = RB_ROOT;
+	while (!of_parse_phandle_with_args(dev->of_node, "mmu-masters",
+					   "#stream-id-cells", i,
+					   &masterspec)) {
+		err = register_smmu_master(smmu, dev, &masterspec);
+		if (err) {
+			dev_err(dev, "failed to add master %s\n",
+				masterspec.np->name);
+			goto out_put_masters;
+		}
+
+		i++;
+	}
+	dev_notice(dev, "registered %d master devices\n", i);
+
+	parse_driver_options(smmu);
+
+	if (smmu->version > ARM_SMMU_V1 &&
+	    smmu->num_context_banks != smmu->num_context_irqs) {
+		dev_err(dev,
+			"found only %d context interrupt(s) but %d required\n",
+			smmu->num_context_irqs, smmu->num_context_banks);
+		err = -ENODEV;
+		goto out_put_masters;
+	}
+
+	for (i = 0; i < smmu->num_global_irqs; ++i) {
+		err = request_irq(smmu->irqs[i],
+				  arm_smmu_global_fault,
+				  IRQF_SHARED,
+				  "arm-smmu global fault",
+				  smmu);
+		if (err) {
+			dev_err(dev, "failed to request global IRQ %d (%u)\n",
+				i, smmu->irqs[i]);
+			goto out_free_irqs;
+		}
+	}
+
+	INIT_LIST_HEAD(&smmu->list);
+	spin_lock(&arm_smmu_devices_lock);
+	list_add(&smmu->list, &arm_smmu_devices);
+	spin_unlock(&arm_smmu_devices_lock);
+
+	arm_smmu_device_reset(smmu);
+	return 0;
+
+out_free_irqs:
+	while (i--)
+		free_irq(smmu->irqs[i], smmu);
+
+out_put_masters:
+	for (node = rb_first(&smmu->masters); node; node = rb_next(node)) {
+		struct arm_smmu_master *master
+			= container_of(node, struct arm_smmu_master, node);
+		of_node_put(master->of_node);
+	}
+
+	return err;
+}
+
+static int arm_smmu_device_remove(struct platform_device *pdev)
+{
+	int i;
+	struct device *dev = &pdev->dev;
+	struct arm_smmu_device *curr, *smmu = NULL;
+	struct rb_node *node;
+
+	spin_lock(&arm_smmu_devices_lock);
+	list_for_each_entry(curr, &arm_smmu_devices, list) {
+		if (curr->dev == dev) {
+			smmu = curr;
+			list_del(&smmu->list);
+			break;
+		}
+	}
+	spin_unlock(&arm_smmu_devices_lock);
+
+	if (!smmu)
+		return -ENODEV;
+
+	for (node = rb_first(&smmu->masters); node; node = rb_next(node)) {
+		struct arm_smmu_master *master
+			= container_of(node, struct arm_smmu_master, node);
+		of_node_put(master->of_node);
+	}
+
+	if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS))
+		dev_err(dev, "removing device with active domains!\n");
+
+	for (i = 0; i < smmu->num_global_irqs; ++i)
+		free_irq(smmu->irqs[i], smmu);
+
+	/* Turn the thing off */
+	writel(sCR0_CLIENTPD, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
+	return 0;
+}
+
+static struct platform_driver arm_smmu_driver = {
+	.driver	= {
+		.name		= "arm-smmu",
+		.of_match_table	= of_match_ptr(arm_smmu_of_match),
+	},
+	.probe	= arm_smmu_device_dt_probe,
+	.remove	= arm_smmu_device_remove,
+};
+
+static int __init arm_smmu_init(void)
+{
+	struct device_node *np;
+	int ret;
+
+	/*
+	 * Play nice with systems that don't have an ARM SMMU by checking that
+	 * an ARM SMMU exists in the system before proceeding with the driver
+	 * and IOMMU bus operation registration.
+	 */
+	np = of_find_matching_node(NULL, arm_smmu_of_match);
+	if (!np)
+		return 0;
+
+	of_node_put(np);
+
+	ret = platform_driver_register(&arm_smmu_driver);
+	if (ret)
+		return ret;
+
+	/* Oh, for a proper bus abstraction */
+	if (!iommu_present(&platform_bus_type))
+		bus_set_iommu(&platform_bus_type, &arm_smmu_ops);
+
+#ifdef CONFIG_ARM_AMBA
+	if (!iommu_present(&amba_bustype))
+		bus_set_iommu(&amba_bustype, &arm_smmu_ops);
+#endif
+
+#ifdef CONFIG_PCI
+	if (!iommu_present(&pci_bus_type))
+		bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
+#endif
+
+	return 0;
+}
+
+static void __exit arm_smmu_exit(void)
+{
+	return platform_driver_unregister(&arm_smmu_driver);
+}
+
+subsys_initcall(arm_smmu_init);
+module_exit(arm_smmu_exit);
+
+MODULE_DESCRIPTION("IOMMU API for ARM architected SMMU implementations");
+MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.1.3

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

* [PATCH for 4.6 11/13] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices
  2014-12-16 20:08 [PATCH for 4.6 00/13] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (9 preceding siblings ...)
  2014-12-16 20:08 ` [PATCH for 4.6 10/13] xen/iommu: arm: Import the SMMU driver from Linux Julien Grall
@ 2014-12-16 20:08 ` Julien Grall
  2014-12-16 20:08 ` [PATCH for 4.6 12/13] xen/iommu: smmu: Introduce automatic stream-id-masking Julien Grall
  2014-12-16 20:08 ` [PATCH for 4.6 13/13] xen/iommu: smmu: Add Xen specific code to be able to use the driver Julien Grall
  12 siblings, 0 replies; 41+ messages in thread
From: Julien Grall @ 2014-12-16 20:08 UTC (permalink / raw)
  To: xen-devel
  Cc: ian.campbell, Andreas Herrmann, manish.jaggi, Julien Grall, tim,
	stefano.stabellini, Andreas Herrmann

From: Andreas Herrmann <andreas.herrmann@calxeda.com>

If DT information lists one stream ID twice for the master devices of
an SMMU this can cause a multi match when stream ID matching is used.
For stream ID indexing this might trigger an overwrite of an S2CR that
is already in use.

So better check for duplicates when DT information is parsed.

Taken from the linux ML:
http://lists.infradead.org/pipermail/linux-arm-kernel/2014-January/226099.html

Cc: Andreas Herrmann <herrmann.der.user@googlemail.com>
Signed-off-by: Andreas Herrmann <andreas.herrmann@calxeda.com>
Signed-off-by: Julien Grall <julien.grall@linaro.org>
---
 xen/drivers/passthrough/arm/smmu.c | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c
index 6cd47b7..bfc1069 100644
--- a/xen/drivers/passthrough/arm/smmu.c
+++ b/xen/drivers/passthrough/arm/smmu.c
@@ -51,6 +51,9 @@
 /* Maximum number of stream IDs assigned to a single device */
 #define MAX_MASTER_STREAMIDS		MAX_PHANDLE_ARGS
 
+/* Maximum stream ID */
+#define ARM_SMMU_MAX_STREAMID		(SZ_64K - 1)
+
 /* Maximum number of context banks per SMMU */
 #define ARM_SMMU_MAX_CBS		128
 
@@ -519,7 +522,8 @@ static int insert_smmu_master(struct arm_smmu_device *smmu,
 
 static int register_smmu_master(struct arm_smmu_device *smmu,
 				struct device *dev,
-				struct of_phandle_args *masterspec)
+				struct of_phandle_args *masterspec,
+				unsigned long *smmu_sids)
 {
 	int i;
 	struct arm_smmu_master *master;
@@ -556,6 +560,12 @@ static int register_smmu_master(struct arm_smmu_device *smmu,
 				masterspec->np->name, smmu->num_mapping_groups);
 			return -ERANGE;
 		}
+
+		if (test_and_set_bit(streamid, smmu_sids)) {
+			dev_err(dev, "duplicate stream ID (%d)\n", streamid);
+			return -EEXIST;
+		}
+
 		master->cfg.streamids[i] = streamid;
 	}
 	return insert_smmu_master(smmu, master);
@@ -1977,6 +1987,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	struct device *dev = &pdev->dev;
 	struct rb_node *node;
 	struct of_phandle_args masterspec;
+	unsigned long *smmu_sids;
 	int num_irqs, i, err;
 
 	smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
@@ -2035,20 +2046,30 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
+	smmu_sids = kzalloc(BITS_TO_LONGS(ARM_SMMU_MAX_STREAMID) *
+			sizeof(long), GFP_KERNEL);
+	if (!smmu_sids) {
+		dev_err(dev,
+			"failed to allocate bitmap for stream ID tracking\n");
+		return -ENOMEM;
+	}
+
 	i = 0;
 	smmu->masters = RB_ROOT;
 	while (!of_parse_phandle_with_args(dev->of_node, "mmu-masters",
 					   "#stream-id-cells", i,
 					   &masterspec)) {
-		err = register_smmu_master(smmu, dev, &masterspec);
+		err = register_smmu_master(smmu, dev, &masterspec, smmu_sids);
 		if (err) {
 			dev_err(dev, "failed to add master %s\n",
 				masterspec.np->name);
+			kfree(smmu_sids);
 			goto out_put_masters;
 		}
 
 		i++;
 	}
+	kfree(smmu_sids);
 	dev_notice(dev, "registered %d master devices\n", i);
 
 	parse_driver_options(smmu);
-- 
2.1.3

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

* [PATCH for 4.6 12/13] xen/iommu: smmu: Introduce automatic stream-id-masking
  2014-12-16 20:08 [PATCH for 4.6 00/13] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (10 preceding siblings ...)
  2014-12-16 20:08 ` [PATCH for 4.6 11/13] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices Julien Grall
@ 2014-12-16 20:08 ` Julien Grall
  2014-12-16 20:08 ` [PATCH for 4.6 13/13] xen/iommu: smmu: Add Xen specific code to be able to use the driver Julien Grall
  12 siblings, 0 replies; 41+ messages in thread
From: Julien Grall @ 2014-12-16 20:08 UTC (permalink / raw)
  To: xen-devel
  Cc: ian.campbell, Andreas Herrmann, manish.jaggi, Julien Grall, tim,
	stefano.stabellini, Andreas Herrmann

From: Andreas Herrmann <andreas.herrmann@calxeda.com>

Try to determine mask/id values that match several stream IDs of a
master device when doing Stream ID matching. Thus the number of used
SMR groups that are required to map all stream IDs of a master device
to a context should be less than the number of SMR groups used so far
(currently one SMR group is used for one stream ID).

Taken from the Linux ML:
http://lists.infradead.org/pipermail/linux-arm-kernel/2014-January/226100.html

Changes compare to the Linux ML version:
    - _fls doesn't exist on Xen so use fls
    - Use num_s2crs rather than num_streamids in the arm_smmu_free_smrs.
    This former is the field used to configure SRMS

Cc: Andreas Herrmann <herrmann.der.user@googlemail.com>
Signed-off-by: Andreas Herrmann <andreas.herrmann@calxeda.com>
Signed-off-by: Julien Grall <julien.grall@linaro.org>
---
 xen/drivers/passthrough/arm/smmu.c | 177 +++++++++++++++++++++++++++++++++----
 1 file changed, 162 insertions(+), 15 deletions(-)

diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c
index bfc1069..8a6514f 100644
--- a/xen/drivers/passthrough/arm/smmu.c
+++ b/xen/drivers/passthrough/arm/smmu.c
@@ -43,6 +43,7 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
+#include <linux/bitops.h>
 
 #include <linux/amba/bus.h>
 
@@ -346,8 +347,10 @@ struct arm_smmu_smr {
 };
 
 struct arm_smmu_master_cfg {
-	int				num_streamids;
+	u32				num_streamids;
 	u16				streamids[MAX_MASTER_STREAMIDS];
+	int				num_s2crs;
+
 	struct arm_smmu_smr		*smrs;
 };
 
@@ -392,6 +395,9 @@ struct arm_smmu_device {
 	u32				num_context_irqs;
 	unsigned int			*irqs;
 
+	u32				smr_mask_mask;
+	u32				smr_id_mask;
+
 	struct list_head		list;
 	struct rb_root			masters;
 };
@@ -1113,6 +1119,137 @@ static void arm_smmu_free_pgtables(struct arm_smmu_domain *smmu_domain)
 	kfree(pgd_base);
 }
 
+/*
+ * For a given set N of 2**order different stream IDs (no duplicates
+ * please!) we determine values mask and id such that
+ *
+ * (1)          (x & mask) == id
+ *
+ * for each stream ID x from the given set N.
+ *
+ * If the number of bits that are set in mask equals n, then there
+ * exist 2**n different values y for which
+ *
+ * (2)          (y & mask) == id
+ *
+ * Thus if n equals order we know that for the calculated mask and id
+ * values there are exactly 2**order == 2**n stream IDs for which (1)
+ * is true. And we finally can use mask and id to configure an SMR to
+ * match all stream IDs in the set N.
+ */
+static int determine_smr_mask(struct arm_smmu_device *smmu,
+			      struct arm_smmu_master_cfg *cfg,
+			      struct arm_smmu_smr *smr, int start, int order)
+{
+	u16 i, zero_bits_mask, one_bits_mask, const_mask;
+	int nr;
+
+	nr = 1 << order;
+
+	if (nr == 1) {
+		/* no mask, use streamid to match and be done with it */
+		smr->mask = 0;
+		smr->id = cfg->streamids[start];
+		return 0;
+	}
+
+	zero_bits_mask = 0;
+	one_bits_mask = 0xffff;
+	for (i = start; i < start + nr; i++) {
+		zero_bits_mask |= cfg->streamids[i];	/* const 0 bits */
+		one_bits_mask &= cfg->streamids[i];	/* const 1 bits */
+	}
+	zero_bits_mask = ~zero_bits_mask;
+
+	/* bits having constant values (either 0 or 1) */
+	const_mask = zero_bits_mask | one_bits_mask;
+
+	i = hweight16(~const_mask);
+	if (i == order) {
+		/*
+		 * We have found a mask/id pair that matches exactly
+		 * nr = 2**order stream IDs which we used for its
+		 * calculation.
+		 */
+		smr->mask = ~const_mask;
+		smr->id = one_bits_mask;
+	} else {
+		/*
+		 * No usable mask/id pair for this set of streamids.
+		 * If i > order then mask/id would match more than nr
+		 * streamids.
+		 * If i < order then mask/id would match less than nr
+		 * streamids. (In this case we potentially have used
+		 * some duplicate streamids for the calculation.)
+		 */
+		return 1;
+	}
+
+	if (((smr->mask & smmu->smr_mask_mask) != smr->mask) ||
+		((smr->id & smmu->smr_id_mask) != smr->id))
+		/* insufficient number of mask/id bits */
+		return 1;
+
+	return 0;
+}
+
+static int determine_smr_mapping(struct arm_smmu_device *smmu,
+				 struct arm_smmu_master_cfg *cfg,
+				 struct arm_smmu_smr *smrs, int max_smrs)
+{
+	int nr_sid, nr, i, bit, start;
+
+	/*
+	 * This function is called only once -- when a master is added
+	 * to a domain. If cfg->num_s2crs != 0 then this master
+	 * was already added to a domain.
+	 */
+	if (cfg->num_s2crs)
+		return -EINVAL;
+
+	start = nr = 0;
+	nr_sid = cfg->num_streamids;
+	do {
+		/*
+		 * largest power-of-2 number of streamids for which to
+		 * determine a usable mask/id pair for stream matching
+		 */
+		bit = fls(nr_sid) - 1;
+		if (bit < 0)
+			return 0;
+
+		/*
+		 * iterate over power-of-2 numbers to determine
+		 * largest possible mask/id pair for stream matching
+		 * of next 2**i streamids
+		 */
+		for (i = bit; i >= 0; i--) {
+			if (!determine_smr_mask(smmu, cfg,
+						&smrs[cfg->num_s2crs],
+						start, i))
+				break;
+		}
+
+		if (i < 0)
+			goto out;
+
+		nr = 1 << i;
+		nr_sid -= nr;
+		start += nr;
+		cfg->num_s2crs++;
+	} while (cfg->num_s2crs <= max_smrs);
+
+out:
+	if (nr_sid) {
+		/* not enough mapping groups available */
+		cfg->num_s2crs = 0;
+		return -ENOSPC;
+	}
+
+	return 0;
+}
+
+
 static void arm_smmu_domain_destroy(struct iommu_domain *domain)
 {
 	struct arm_smmu_domain *smmu_domain = domain->priv;
@@ -1129,7 +1266,7 @@ static void arm_smmu_domain_destroy(struct iommu_domain *domain)
 static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu,
 					  struct arm_smmu_master_cfg *cfg)
 {
-	int i;
+	int i, max_smrs, ret;
 	struct arm_smmu_smr *smrs;
 	void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
 
@@ -1139,31 +1276,32 @@ static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu,
 	if (cfg->smrs)
 		return -EEXIST;
 
-	smrs = kmalloc_array(cfg->num_streamids, sizeof(*smrs), GFP_KERNEL);
+	max_smrs = min(smmu->num_mapping_groups, cfg->num_streamids);
+	smrs = kmalloc(sizeof(*smrs) * max_smrs, GFP_KERNEL);
 	if (!smrs) {
 		dev_err(smmu->dev, "failed to allocate %d SMRs\n",
-			cfg->num_streamids);
+			max_smrs);
 		return -ENOMEM;
 	}
 
+	ret = determine_smr_mapping(smmu, cfg, smrs, max_smrs);
+	if (ret)
+		goto err_free_smrs;
+
 	/* Allocate the SMRs on the SMMU */
-	for (i = 0; i < cfg->num_streamids; ++i) {
+	for (i = 0; i < cfg->num_s2crs; ++i) {
 		int idx = __arm_smmu_alloc_bitmap(smmu->smr_map, 0,
 						  smmu->num_mapping_groups);
 		if (IS_ERR_VALUE(idx)) {
 			dev_err(smmu->dev, "failed to allocate free SMR\n");
-			goto err_free_smrs;
+			goto err_free_bitmap;
 		}
 
-		smrs[i] = (struct arm_smmu_smr) {
-			.idx	= idx,
-			.mask	= 0, /* We don't currently share SMRs */
-			.id	= cfg->streamids[i],
-		};
+		smrs[i].idx = idx;
 	}
 
 	/* It worked! Now, poke the actual hardware */
-	for (i = 0; i < cfg->num_streamids; ++i) {
+	for (i = 0; i < cfg->num_s2crs; ++i) {
 		u32 reg = SMR_VALID | smrs[i].id << SMR_ID_SHIFT |
 			  smrs[i].mask << SMR_MASK_SHIFT;
 		writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_SMR(smrs[i].idx));
@@ -1172,9 +1310,11 @@ static int arm_smmu_master_configure_smrs(struct arm_smmu_device *smmu,
 	cfg->smrs = smrs;
 	return 0;
 
-err_free_smrs:
+err_free_bitmap:
 	while (--i >= 0)
 		__arm_smmu_free_bitmap(smmu->smr_map, smrs[i].idx);
+	cfg->num_s2crs = 0;
+err_free_smrs:
 	kfree(smrs);
 	return -ENOSPC;
 }
@@ -1190,13 +1330,15 @@ static void arm_smmu_master_free_smrs(struct arm_smmu_device *smmu,
 		return;
 
 	/* Invalidate the SMRs before freeing back to the allocator */
-	for (i = 0; i < cfg->num_streamids; ++i) {
+	for (i = 0; i < cfg->num_s2crs; ++i) {
 		u8 idx = smrs[i].idx;
 
 		writel_relaxed(~SMR_VALID, gr0_base + ARM_SMMU_GR0_SMR(idx));
 		__arm_smmu_free_bitmap(smmu->smr_map, idx);
 	}
 
+	cfg->num_s2crs = 0;
+
 	cfg->smrs = NULL;
 	kfree(smrs);
 }
@@ -1213,12 +1355,15 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
 	if (ret)
 		return ret == -EEXIST ? 0 : ret;
 
-	for (i = 0; i < cfg->num_streamids; ++i) {
+	if (!cfg->num_s2crs)
+		cfg->num_s2crs = cfg->num_streamids;
+	for (i = 0; i < cfg->num_s2crs; ++i) {
 		u32 idx, s2cr;
 
 		idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
 		s2cr = S2CR_TYPE_TRANS |
 		       (smmu_domain->cfg.cbndx << S2CR_CBNDX_SHIFT);
+		dev_dbg(smmu->dev, "S2CR%d: 0x%x\n", idx, s2cr);
 		writel_relaxed(s2cr, gr0_base + ARM_SMMU_GR0_S2CR(idx));
 	}
 
@@ -1890,6 +2035,8 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 				mask, sid);
 			return -ENODEV;
 		}
+		smmu->smr_mask_mask = mask;
+		smmu->smr_id_mask = sid;
 
 		dev_notice(smmu->dev,
 			   "\tstream matching with %u register groups, mask 0x%x",
-- 
2.1.3

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

* [PATCH for 4.6 13/13] xen/iommu: smmu: Add Xen specific code to be able to use the driver
  2014-12-16 20:08 [PATCH for 4.6 00/13] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (11 preceding siblings ...)
  2014-12-16 20:08 ` [PATCH for 4.6 12/13] xen/iommu: smmu: Introduce automatic stream-id-masking Julien Grall
@ 2014-12-16 20:08 ` Julien Grall
  2015-02-18  1:02   ` Manish
  12 siblings, 1 reply; 41+ messages in thread
From: Julien Grall @ 2014-12-16 20:08 UTC (permalink / raw)
  To: xen-devel
  Cc: stefano.stabellini, manish.jaggi, Julien Grall, tim, ian.campbell

The main goal is to modify as little the Linux code to be able to port
easily new feature added in Linux repo for the driver.

To achieve that we:
    - Add helpers to Linux function not implemented on Xen
    - Add callbacks used by Xen to do our own stuff and call Linux ones
    - Only modify when required the code which comes from Linux. If so a
    comment has been added with /* Xen: ... */ explaining why it's
    necessary.

The support for PCI has been commented because it's not yet supported by
Xen ARM and therefore won't compile.

Signed-off-by: Julien Grall <julien.grall@linaro.org>
---
 xen/drivers/passthrough/arm/Makefile |   1 +
 xen/drivers/passthrough/arm/smmu.c   | 668 +++++++++++++++++++++++++++++++----
 2 files changed, 602 insertions(+), 67 deletions(-)

diff --git a/xen/drivers/passthrough/arm/Makefile b/xen/drivers/passthrough/arm/Makefile
index 0484b79..f4cd26e 100644
--- a/xen/drivers/passthrough/arm/Makefile
+++ b/xen/drivers/passthrough/arm/Makefile
@@ -1 +1,2 @@
 obj-y += iommu.o
+obj-y += smmu.o
diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c
index 8a6514f..3cf1773 100644
--- a/xen/drivers/passthrough/arm/smmu.c
+++ b/xen/drivers/passthrough/arm/smmu.c
@@ -18,6 +18,13 @@
  *
  * Author: Will Deacon <will.deacon@arm.com>
  *
+ * Based on Linux drivers/iommu/arm-smmu.c
+ *	=> commit e6b5be2be4e30037eb551e0ed09dd97bd00d85d3
+ *
+ * Xen modification:
+ * Julien Grall <julien.grall@linaro.org>
+ * Copyright (C) 2014 Linaro Limited.
+ *
  * This driver currently supports:
  *	- SMMUv1 and v2 implementations
  *	- Stream-matching and stream-indexing
@@ -28,26 +35,154 @@
  *	- Context fault reporting
  */
 
-#define pr_fmt(fmt) "arm-smmu: " fmt
 
-#include <linux/delay.h>
-#include <linux/dma-mapping.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/iommu.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/bitops.h>
+#include <xen/config.h>
+#include <xen/delay.h>
+#include <xen/device.h>
+#include <xen/errno.h>
+#include <xen/err.h>
+#include <xen/irq.h>
+#include <xen/lib.h>
+#include <xen/list.h>
+#include <xen/mm.h>
+#include <xen/vmap.h>
+#include <xen/rbtree.h>
+#include <xen/sched.h>
+#include <xen/sizes.h>
+#include <asm/atomic.h>
+#include <asm/io.h>
+#include <asm/platform.h>
+
+/* Xen: The below defines are redefined within the file. Undef it */
+#undef SCTLR_AFE
+#undef SCTLR_TRE
+#undef SCTLR_M
+#undef TTBCR_EAE
+
+/* Alias to Xen device tree helpers */
+#define device_node dt_device_node
+#define of_phandle_args dt_phandle_args
+#define of_device_id dt_device_match
+#define of_match_node dt_match_node
+#define of_property_read_u32(np, pname, out) (!dt_property_read_u32(np, pname, out))
+#define of_property_read_bool dt_property_read_bool
+#define of_parse_phandle_with_args dt_parse_phandle_with_args
+
+/* Xen: Helpers to get device MMIO and IRQs */
+struct resource
+{
+	u64 addr;
+	u64 size;
+	unsigned int type;
+};
+
+#define resource_size(res) (res)->size;
+
+#define platform_device dt_device_node
+
+#define IORESOURCE_MEM 0
+#define IORESOURCE_IRQ 1
+
+static struct resource *platform_get_resource(struct platform_device *pdev,
+					      unsigned int type,
+					      unsigned int num)
+{
+	/*
+	 * The resource is only used between 2 calls of platform_get_resource.
+	 * It's quite ugly but it's avoid to add too much code in the part
+	 * imported from Linux
+	 */
+	static struct resource res;
+	int ret = 0;
+
+	res.type = type;
+
+	switch (type) {
+	case IORESOURCE_MEM:
+		ret = dt_device_get_address(pdev, num, &res.addr, &res.size);
+
+		return ((ret) ? NULL : &res);
+
+	case IORESOURCE_IRQ:
+		ret = platform_get_irq(pdev, num);
+		if (ret < 0)
+			return NULL;
+
+		res.addr = ret;
+		res.size = 1;
+
+		return &res;
+
+	default:
+		return NULL;
+	}
+}
+
+/* Alias to Xen IRQ functions */
+#define request_irq(irq, func, flags, name, dev) request_irq(irq, flags, func, name, dev)
+#define free_irq release_irq
+
+/*
+ * Device logger functions
+ * TODO: Handle PCI
+ */
+#define dev_print(dev, lvl, fmt, ...)						\
+	 printk(lvl "smmu: %s: " fmt, dt_node_full_name(dev_to_dt(dev)), ## __VA_ARGS__)
+
+#define dev_dbg(dev, fmt, ...) dev_print(dev, XENLOG_DEBUG, fmt, ## __VA_ARGS__)
+#define dev_notice(dev, fmt, ...) dev_print(dev, XENLOG_INFO, fmt, ## __VA_ARGS__)
+#define dev_warn(dev, fmt, ...) dev_print(dev, XENLOG_WARNING, fmt, ## __VA_ARGS__)
+#define dev_err(dev, fmt, ...) dev_print(dev, XENLOG_ERR, fmt, ## __VA_ARGS__)
+
+#define dev_err_ratelimited(dev, fmt, ...)					\
+	 dev_print(dev, XENLOG_ERR, fmt, ## __VA_ARGS__)
 
-#include <linux/amba/bus.h>
+#define dev_name(dev) dt_node_full_name(dev_to_dt(dev))
 
-#include <asm/pgalloc.h>
+/* Alias to Xen allocation helpers */
+#define kfree xfree
+#define kmalloc(size, flags)		_xmalloc(size, sizeof(void *))
+#define kzalloc(size, flags)		_xzalloc(size, sizeof(void *))
+#define devm_kzalloc(dev, size, flags)	_xzalloc(size, sizeof(void *))
+#define kmalloc_array(size, n, flags)	_xmalloc_array(size, sizeof(void *), n)
+
+static void __iomem *devm_ioremap_resource(struct device *dev,
+					   struct resource *res)
+{
+	void __iomem *ptr;
+
+	if (!res || res->type != IORESOURCE_MEM) {
+		dev_err(dev, "Invalid resource\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	ptr = ioremap_nocache(res->addr, res->size);
+	if (!ptr) {
+		dev_err(dev,
+			"ioremap failed (addr 0x%"PRIx64" size 0x%"PRIx64")\n",
+			res->addr, res->size);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	return ptr;
+}
+
+/* Xen doesn't handle IOMMU fault */
+#define report_iommu_fault(...)	1
+
+#define IOMMU_FAULT_READ	0
+#define IOMMU_FAULT_WRITE	1
+
+/* Xen: misc */
+#define PHYS_MASK_SHIFT		PADDR_BITS
+
+#ifdef CONFIG_ARM_64
+# define CONFIG_64BIT
+#endif
+
+#define VA_BITS		0	/* Only used for configuring stage-1 input size */
+
+/***** Start of SMMU definitions *****/
 
 /* Maximum number of stream IDs assigned to a single device */
 #define MAX_MASTER_STREAMIDS		MAX_PHANDLE_ARGS
@@ -330,10 +465,14 @@
 
 #define FSYNR0_WNR			(1 << 4)
 
-static int force_stage;
-module_param_named(force_stage, force_stage, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(force_stage,
-	"Force SMMU mappings to be installed at a particular stage of translation. A value of '1' or '2' forces the corresponding stage. All other values are ignored (i.e. no stage is forced). Note that selecting a specific stage will disable support for nested translation.");
+/* Force SMMU mapping to be installed at a particular stage of translation.
+ * A value of '1' or '2' forces the corresponding state. All other values
+ * are ignored (i.e no stage is forced). Note that selecting a specific stage
+ * will disable support for nested translation.
+ *
+ * Xen is only supported stage-2 translation, so force the value to 2.
+ */
+static const int force_stage = 2;
 
 enum arm_smmu_arch_version {
 	ARM_SMMU_V1 = 1,
@@ -406,7 +545,9 @@ struct arm_smmu_cfg {
 	u8				cbndx;
 	u8				irptndx;
 	u32				cbar;
-	pgd_t				*pgd;
+
+	/* Xen: Domain associated to this configuration */
+	struct domain			*domain;
 };
 #define INVALID_IRPTNDX			0xff
 
@@ -426,6 +567,90 @@ struct arm_smmu_domain {
 	spinlock_t			lock;
 };
 
+/* Xen: Dummy iommu_domain */
+struct iommu_domain
+{
+	struct arm_smmu_domain		*priv;
+
+	/* Used to link domain contexts for a same domain */
+	struct list_head		list;
+};
+
+/* Xen: Describes informations required for a Xen domain */
+struct arm_smmu_xen_domain {
+	spinlock_t			lock;
+	/* List of context (i.e iommu_domain) associated to this domain */
+	struct list_head		contexts;
+};
+
+/* Xen: Information about each device stored in dev->archdata.iommu */
+struct arm_smmu_xen_device {
+	struct iommu_domain *domain;
+	struct iommu_group *group;
+};
+
+#define dev_archdata(dev) ((struct arm_smmu_xen_device *)dev->archdata.iommu)
+#define dev_iommu_domain(dev) (dev_archdata(dev)->domain)
+#define dev_iommu_group(dev) (dev_archdata(dev)->group)
+
+/* Xen: Dummy iommu_group */
+struct iommu_group
+{
+	struct arm_smmu_master_cfg *cfg;
+
+	atomic_t ref;
+};
+
+static struct iommu_group *iommu_group_alloc(void)
+{
+	struct iommu_group *group = xzalloc(struct iommu_group);
+
+	if (!group)
+		return ERR_PTR(-ENOMEM);
+
+	atomic_set(&group->ref, 1);
+
+	return group;
+}
+
+static void iommu_group_put(struct iommu_group *group)
+{
+	if (atomic_dec_and_test(&group->ref))
+		xfree(group);
+}
+
+static void iommu_group_set_iommudata(struct iommu_group *group,
+				      struct arm_smmu_master_cfg *cfg,
+				      void (*releasefn)(void *))
+{
+	/* TODO: Store the releasefn for the PCI */
+	ASSERT(releasefn == NULL);
+
+	group->cfg = cfg;
+}
+
+static int iommu_group_add_device(struct iommu_group *group,
+				  struct device *dev)
+{
+	dev_iommu_group(dev) = group;
+
+	atomic_inc(&group->ref);
+
+	return 0;
+}
+
+static struct iommu_group *iommu_group_get(struct device *dev)
+{
+	struct iommu_group *group = dev_iommu_group(dev);
+
+	if (group)
+		atomic_inc(&group->ref);
+
+	return group;
+}
+
+#define iommu_group_get_iommudata(group) (group)->cfg
+
 static DEFINE_SPINLOCK(arm_smmu_devices_lock);
 static LIST_HEAD(arm_smmu_devices);
 
@@ -455,6 +680,8 @@ static void parse_driver_options(struct arm_smmu_device *smmu)
 
 static struct device_node *dev_get_dev_node(struct device *dev)
 {
+	/* Xen: TODO: Add support for PCI */
+#if 0
 	if (dev_is_pci(dev)) {
 		struct pci_bus *bus = to_pci_dev(dev)->bus;
 
@@ -462,7 +689,7 @@ static struct device_node *dev_get_dev_node(struct device *dev)
 			bus = bus->parent;
 		return bus->bridge->parent->of_node;
 	}
-
+#endif
 	return dev->of_node;
 }
 
@@ -556,6 +783,9 @@ static int register_smmu_master(struct arm_smmu_device *smmu,
 	master->of_node			= masterspec->np;
 	master->cfg.num_streamids	= masterspec->args_count;
 
+	/* Xen: Let Xen knows that the device is protected by an SMMU */
+	dt_device_set_protected(masterspec->np);
+
 	for (i = 0; i < master->cfg.num_streamids; ++i) {
 		u16 streamid = masterspec->args[i];
 
@@ -651,11 +881,12 @@ static void arm_smmu_tlb_inv_context(struct arm_smmu_domain *smmu_domain)
 	arm_smmu_tlb_sync(smmu);
 }
 
-static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
+static void arm_smmu_context_fault(int irq, void *dev,
+				   struct cpu_user_regs *regs)
 {
-	int flags, ret;
+	int flags;
 	u32 fsr, far, fsynr, resume;
-	unsigned long iova;
+	paddr_t iova;
 	struct iommu_domain *domain = dev;
 	struct arm_smmu_domain *smmu_domain = domain->priv;
 	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
@@ -666,7 +897,7 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
 	fsr = readl_relaxed(cb_base + ARM_SMMU_CB_FSR);
 
 	if (!(fsr & FSR_FAULT))
-		return IRQ_NONE;
+		return;
 
 	if (fsr & FSR_IGN)
 		dev_err_ratelimited(smmu->dev,
@@ -678,19 +909,16 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
 
 	far = readl_relaxed(cb_base + ARM_SMMU_CB_FAR_LO);
 	iova = far;
-#ifdef CONFIG_64BIT
+	/* Xen: The fault address maybe higher than 32 bits on arm32 */
 	far = readl_relaxed(cb_base + ARM_SMMU_CB_FAR_HI);
-	iova |= ((unsigned long)far << 32);
-#endif
+	iova |= ((paddr_t)far << 32);
 
 	if (!report_iommu_fault(domain, smmu->dev, iova, flags)) {
-		ret = IRQ_HANDLED;
 		resume = RESUME_RETRY;
 	} else {
 		dev_err_ratelimited(smmu->dev,
-		    "Unhandled context fault: iova=0x%08lx, fsynr=0x%x, cb=%d\n",
+		    "Unhandled context fault: iova=0x%"PRIpaddr", fsynr=0x%x, cb=%d\n",
 		    iova, fsynr, cfg->cbndx);
-		ret = IRQ_NONE;
 		resume = RESUME_TERMINATE;
 	}
 
@@ -700,11 +928,10 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
 	/* Retry or terminate any stalled transactions */
 	if (fsr & FSR_SS)
 		writel_relaxed(resume, cb_base + ARM_SMMU_CB_RESUME);
-
-	return ret;
 }
 
-static irqreturn_t arm_smmu_global_fault(int irq, void *dev)
+static void arm_smmu_global_fault(int irq, void *dev,
+                                  struct cpu_user_regs *regs)
 {
 	u32 gfsr, gfsynr0, gfsynr1, gfsynr2;
 	struct arm_smmu_device *smmu = dev;
@@ -716,7 +943,7 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev)
 	gfsynr2 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR2);
 
 	if (!gfsr)
-		return IRQ_NONE;
+		return;
 
 	dev_err_ratelimited(smmu->dev,
 		"Unexpected global fault, this could be serious\n");
@@ -725,9 +952,10 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev)
 		gfsr, gfsynr0, gfsynr1, gfsynr2);
 
 	writel(gfsr, gr0_base + ARM_SMMU_GR0_sGFSR);
-	return IRQ_HANDLED;
 }
 
+/* Xen: Page tables are shared with the processor */
+#if 0
 static void arm_smmu_flush_pgtable(struct arm_smmu_device *smmu, void *addr,
 				   size_t size)
 {
@@ -749,6 +977,7 @@ static void arm_smmu_flush_pgtable(struct arm_smmu_device *smmu, void *addr,
 				DMA_TO_DEVICE);
 	}
 }
+#endif
 
 static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
 {
@@ -757,6 +986,7 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
 	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
 	struct arm_smmu_device *smmu = smmu_domain->smmu;
 	void __iomem *cb_base, *gr0_base, *gr1_base;
+	paddr_t p2maddr;
 
 	gr0_base = ARM_SMMU_GR0(smmu);
 	gr1_base = ARM_SMMU_GR1(smmu);
@@ -840,11 +1070,16 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
 	}
 
 	/* TTBR0 */
-	arm_smmu_flush_pgtable(smmu, cfg->pgd,
-			       PTRS_PER_PGD * sizeof(pgd_t));
-	reg = __pa(cfg->pgd);
+	/* Xen: The page table is shared with the P2M code */
+	ASSERT(smmu_domain->cfg.domain != NULL);
+	p2maddr = page_to_maddr(smmu_domain->cfg.domain->arch.p2m.root);
+
+	dev_notice(smmu->dev, "d%u: p2maddr 0x%"PRIpaddr"\n",
+		   smmu_domain->cfg.domain->domain_id, p2maddr);
+
+	reg = (p2maddr & ((1ULL << 32) - 1));
 	writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_LO);
-	reg = (phys_addr_t)__pa(cfg->pgd) >> 32;
+	reg = (p2maddr >> 32);
 	if (stage1)
 		reg |= ARM_SMMU_CB_ASID(cfg) << TTBRn_HI_ASID_SHIFT;
 	writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_HI);
@@ -886,9 +1121,21 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
 			reg |= (64 - smmu->s1_input_size) << TTBCR_T0SZ_SHIFT;
 		}
 	} else {
+#if CONFIG_ARM_32
+		/* Xen: Midway is using 40-bit address space. Though it may
+		 * not work on other ARM32 platform with SMMU-v1.
+		 * TODO: A quirk may be necessary if we have to support
+		 * other ARM32 platform with SMMU-v1.
+		 */
+		reg = 0x18 << TTBCR_T0SZ_SHIFT;
+#else
 		reg = 0;
+#endif
 	}
 
+	/* Xen: The attributes to walk the page table should be the same as
+	 * VTCR_EL2. Currently doesn't differ from Linux ones.
+	 */
 	reg |= TTBCR_EAE |
 	      (TTBCR_SH_IS << TTBCR_SH0_SHIFT) |
 	      (TTBCR_RGN_WBWA << TTBCR_ORGN0_SHIFT) |
@@ -1031,7 +1278,6 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
 static int arm_smmu_domain_init(struct iommu_domain *domain)
 {
 	struct arm_smmu_domain *smmu_domain;
-	pgd_t *pgd;
 
 	/*
 	 * Allocate the domain and initialise some of its data structures.
@@ -1042,20 +1288,12 @@ static int arm_smmu_domain_init(struct iommu_domain *domain)
 	if (!smmu_domain)
 		return -ENOMEM;
 
-	pgd = kcalloc(PTRS_PER_PGD, sizeof(pgd_t), GFP_KERNEL);
-	if (!pgd)
-		goto out_free_domain;
-	smmu_domain->cfg.pgd = pgd;
-
 	spin_lock_init(&smmu_domain->lock);
 	domain->priv = smmu_domain;
 	return 0;
-
-out_free_domain:
-	kfree(smmu_domain);
-	return -ENOMEM;
 }
 
+#if 0
 static void arm_smmu_free_ptes(pmd_t *pmd)
 {
 	pgtable_t table = pmd_pgtable(*pmd);
@@ -1118,6 +1356,7 @@ static void arm_smmu_free_pgtables(struct arm_smmu_domain *smmu_domain)
 
 	kfree(pgd_base);
 }
+#endif
 
 /*
  * For a given set N of 2**order different stream IDs (no duplicates
@@ -1259,7 +1498,6 @@ static void arm_smmu_domain_destroy(struct iommu_domain *domain)
 	 * already been detached.
 	 */
 	arm_smmu_destroy_domain_context(domain);
-	arm_smmu_free_pgtables(smmu_domain);
 	kfree(smmu_domain);
 }
 
@@ -1384,11 +1622,12 @@ static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
 	/*
 	 * We *must* clear the S2CR first, because freeing the SMR means
 	 * that it can be re-allocated immediately.
+	 * Xen: Unlike Linux, any access to non-configured stream will fault.
 	 */
 	for (i = 0; i < cfg->num_streamids; ++i) {
 		u32 idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
 
-		writel_relaxed(S2CR_TYPE_BYPASS,
+		writel_relaxed(S2CR_TYPE_FAULT,
 			       gr0_base + ARM_SMMU_GR0_S2CR(idx));
 	}
 
@@ -1408,7 +1647,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 		return -ENXIO;
 	}
 
-	if (dev->archdata.iommu) {
+	if (dev_iommu_domain(dev)) {
 		dev_err(dev, "already attached to IOMMU domain\n");
 		return -EEXIST;
 	}
@@ -1440,8 +1679,9 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 		return -ENODEV;
 
 	ret = arm_smmu_domain_add_master(smmu_domain, cfg);
+
 	if (!ret)
-		dev->archdata.iommu = domain;
+		dev_iommu_domain(dev) = domain;
 	return ret;
 }
 
@@ -1454,10 +1694,14 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
 	if (!cfg)
 		return;
 
-	dev->archdata.iommu = NULL;
+	dev_iommu_domain(dev) = NULL;
 	arm_smmu_domain_remove_master(smmu_domain, cfg);
 }
 
+/* Xen: the page table is shared with the processor, therefore helpers to
+ * implement separate is not necessary.
+ */
+#if 0
 static bool arm_smmu_pte_is_contiguous_range(unsigned long addr,
 					     unsigned long end)
 {
@@ -1746,7 +1990,10 @@ static phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain,
 
 	return __pfn_to_phys(pte_pfn(pte)) | (iova & ~PAGE_MASK);
 }
+#endif
 
+/* Xen: Functions are not used at the moment */
+#if 0
 static bool arm_smmu_capable(enum iommu_cap cap)
 {
 	switch (cap) {
@@ -1775,6 +2022,7 @@ static void __arm_smmu_release_pci_iommudata(void *data)
 {
 	kfree(data);
 }
+#endif
 
 static int arm_smmu_add_device(struct device *dev)
 {
@@ -1784,6 +2032,10 @@ static int arm_smmu_add_device(struct device *dev)
 	void (*releasefn)(void *) = NULL;
 	int ret;
 
+	/* Xen: Check if the device has already been added */
+	if (dev_iommu_group(dev))
+		return -EBUSY;
+
 	smmu = find_smmu_for_device(dev);
 	if (!smmu)
 		return -ENODEV;
@@ -1795,6 +2047,9 @@ static int arm_smmu_add_device(struct device *dev)
 	}
 
 	if (dev_is_pci(dev)) {
+		/* Xen: TODO: Add PCI support */
+		BUG();
+#if 0
 		struct pci_dev *pdev = to_pci_dev(dev);
 
 		cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
@@ -1811,6 +2066,7 @@ static int arm_smmu_add_device(struct device *dev)
 		pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid,
 				       &cfg->streamids[0]);
 		releasefn = __arm_smmu_release_pci_iommudata;
+#endif
 	} else {
 		struct arm_smmu_master *master;
 
@@ -1831,6 +2087,8 @@ out_put_group:
 	return ret;
 }
 
+/* Xen: We don't support remove device for now. Will be useful for PCI */
+#if 0
 static void arm_smmu_remove_device(struct device *dev)
 {
 	iommu_group_remove_device(dev);
@@ -1888,6 +2146,7 @@ static const struct iommu_ops arm_smmu_ops = {
 				   ARM_SMMU_PTE_CONT_SIZE |
 				   PAGE_SIZE),
 };
+#endif
 
 static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
 {
@@ -1903,7 +2162,11 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
 	/* Mark all SMRn as invalid and all S2CRn as bypass */
 	for (i = 0; i < smmu->num_mapping_groups; ++i) {
 		writel_relaxed(0, gr0_base + ARM_SMMU_GR0_SMR(i));
-		writel_relaxed(S2CR_TYPE_BYPASS,
+		/*
+		 * Xen: Unlike Linux, any access to a non-configure stream
+		 * will fault by default.
+		 */
+		writel_relaxed(S2CR_TYPE_FAULT,
 			gr0_base + ARM_SMMU_GR0_S2CR(i));
 	}
 
@@ -1929,6 +2192,8 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
 
 	/* Enable client access, but bypass when no mapping is found */
 	reg &= ~(sCR0_CLIENTPD | sCR0_USFCFG);
+	/* Xen: Unlike Linux, generate a fault when no mapping is found */
+	reg |= sCR0_USFCFG;
 
 	/* Disable forced broadcasting */
 	reg &= ~sCR0_FB;
@@ -2039,7 +2304,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 		smmu->smr_id_mask = sid;
 
 		dev_notice(smmu->dev,
-			   "\tstream matching with %u register groups, mask 0x%x",
+			   "\tstream matching with %u register groups, mask 0x%x\n",
 			   smmu->num_mapping_groups, mask);
 	} else {
 		smmu->num_mapping_groups = (id >> ID0_NUMSIDB_SHIFT) &
@@ -2074,12 +2339,30 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
 	size = arm_smmu_id_size_to_bits((id >> ID2_IAS_SHIFT) & ID2_IAS_MASK);
 	smmu->s1_output_size = min_t(unsigned long, PHYS_MASK_SHIFT, size);
 
+	/* Xen: Stage-2 input size is not restricted */
+	smmu->s2_input_size = size;
+#if 0
 	/* Stage-2 input size limited due to pgd allocation (PTRS_PER_PGD) */
 #ifdef CONFIG_64BIT
 	smmu->s2_input_size = min_t(unsigned long, VA_BITS, size);
 #else
 	smmu->s2_input_size = min(32UL, size);
 #endif
+#endif
+
+	/*
+	 * Xen: SMMU v1: Only 40 bits input address size is supported for
+ 	 * arm32. See arm_smmu_init_context_bank
+	 */
+#ifdef CONFIG_ARM_32
+	if ( smmu->version == ARM_SMMU_V1 && smmu->s2_input_size != 40 )
+	{
+		dev_err(smmu->dev,
+			"Stage-2 Input size %ld not supported for SMMUv1\n",
+			smmu->s2_input_size);
+		return -ENODEV;;
+	}
+#endif
 
 	/* The stage-2 output mask is also applied for bypass */
 	size = arm_smmu_id_size_to_bits((id >> ID2_OAS_SHIFT) & ID2_OAS_MASK);
@@ -2124,8 +2407,11 @@ static const struct of_device_id arm_smmu_of_match[] = {
 	{ .compatible = "arm,mmu-500", .data = (void *)ARM_SMMU_V2 },
 	{ },
 };
-MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
 
+/*
+ * Xen: We don't have refcount allocated memory so manually free memory when
+ * an error occured.
+ */
 static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *of_id;
@@ -2149,14 +2435,17 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	smmu->base = devm_ioremap_resource(dev, res);
-	if (IS_ERR(smmu->base))
-		return PTR_ERR(smmu->base);
+	if (IS_ERR(smmu->base)) {
+		err = PTR_ERR(smmu->base);
+		goto out_free;
+	}
 	smmu->size = resource_size(res);
 
 	if (of_property_read_u32(dev->of_node, "#global-interrupts",
 				 &smmu->num_global_irqs)) {
 		dev_err(dev, "missing #global-interrupts property\n");
-		return -ENODEV;
+		err = -ENODEV;
+		goto out_free;
 	}
 
 	num_irqs = 0;
@@ -2169,14 +2458,16 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	if (!smmu->num_context_irqs) {
 		dev_err(dev, "found %d interrupts but expected at least %d\n",
 			num_irqs, smmu->num_global_irqs + 1);
-		return -ENODEV;
+		err = -ENODEV;
+		goto out_free;
 	}
 
 	smmu->irqs = devm_kzalloc(dev, sizeof(*smmu->irqs) * num_irqs,
 				  GFP_KERNEL);
 	if (!smmu->irqs) {
 		dev_err(dev, "failed to allocate %d irqs\n", num_irqs);
-		return -ENOMEM;
+		err = -ENOMEM;
+		goto out_free;
 	}
 
 	for (i = 0; i < num_irqs; ++i) {
@@ -2184,7 +2475,8 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 
 		if (irq < 0) {
 			dev_err(dev, "failed to get irq index %d\n", i);
-			return -ENODEV;
+			err = -ENODEV;
+			goto out_free;
 		}
 		smmu->irqs[i] = irq;
 	}
@@ -2259,12 +2551,20 @@ out_put_masters:
 	for (node = rb_first(&smmu->masters); node; node = rb_next(node)) {
 		struct arm_smmu_master *master
 			= container_of(node, struct arm_smmu_master, node);
-		of_node_put(master->of_node);
+		kfree(master);
 	}
 
+out_free:
+	kfree(smmu->irqs);
+	if (!IS_ERR(smmu->base))
+		iounmap(smmu->base);
+	kfree(smmu);
+
 	return err;
 }
 
+/* Xen: We never remove SMMU */
+#if 0
 static int arm_smmu_device_remove(struct platform_device *pdev)
 {
 	int i;
@@ -2359,3 +2659,237 @@ module_exit(arm_smmu_exit);
 MODULE_DESCRIPTION("IOMMU API for ARM architected SMMU implementations");
 MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>");
 MODULE_LICENSE("GPL v2");
+#endif
+
+/* Xen specific function */
+
+static void arm_smmu_iotlb_flush_all(struct domain *d)
+{
+	struct arm_smmu_xen_domain *smmu_domain = domain_hvm_iommu(d)->arch.priv;
+	struct iommu_domain *cfg;
+
+	spin_lock(&smmu_domain->lock);
+	list_for_each_entry(cfg, &smmu_domain->contexts, list) {
+		/*
+		 * Only invalidate the context when SMMU is present.
+		 * This is because the context initialization is delayed
+		 * until a master has been added.
+		 */
+		if (unlikely(!ACCESS_ONCE(cfg->priv->smmu)))
+			continue;
+		arm_smmu_tlb_inv_context(cfg->priv);
+	}
+	spin_unlock(&smmu_domain->lock);
+}
+
+static void arm_smmu_iotlb_flush(struct domain *d, unsigned long gfn,
+                                 unsigned int page_count)
+{
+    /* ARM SMMU v1 doesn't have flush by VMA and VMID */
+    arm_smmu_iotlb_flush_all(d);
+}
+
+static int arm_smmu_assign_dev(struct domain *d, u8 devfn,
+			       struct device *dev)
+{
+	struct iommu_domain *domain;
+	struct arm_smmu_xen_domain *xen_domain;
+	int ret;
+
+	xen_domain = domain_hvm_iommu(d)->arch.priv;
+
+	if (!dev->archdata.iommu) {
+		dev->archdata.iommu = xzalloc(struct arm_smmu_xen_device);
+		if (!dev->archdata.iommu)
+			return -ENOMEM;
+	}
+
+	if (!dev_iommu_group(dev)) {
+		ret = arm_smmu_add_device(dev);
+		if (ret)
+			return ret;
+	}
+
+	/*
+	 * TODO: Share the context bank (i.e iommu_domain) when the device is
+	 * under the same SMMU as another device assigned to this domain.
+	 * Would it useful for PCI
+	 */
+	domain = xzalloc(struct iommu_domain);
+	if (!domain)
+		return -ENOMEM;
+
+	ret = arm_smmu_domain_init(domain);
+	if (ret)
+		goto err_dom_init;
+
+	domain->priv->cfg.domain = d;
+
+	ret = arm_smmu_attach_dev(domain, dev);
+	if (ret)
+		goto err_attach_dev;
+
+	spin_lock(&xen_domain->lock);
+	/* Chain the new context to the domain */
+	list_add(&domain->list, &xen_domain->contexts);
+	spin_unlock(&xen_domain->lock);
+
+	return 0;
+
+err_attach_dev:
+	arm_smmu_domain_destroy(domain);
+err_dom_init:
+	xfree(domain);
+
+	return ret;
+}
+
+static int arm_smmu_deassign_dev(struct domain *d, struct device *dev)
+{
+	struct iommu_domain *domain = dev_iommu_domain(dev);
+	struct arm_smmu_xen_domain *xen_domain;
+
+	xen_domain = domain_hvm_iommu(d)->arch.priv;
+
+	if (!domain || domain->priv->cfg.domain != d) {
+		dev_err(dev, " not attached to domain %d\n", d->domain_id);
+		return -ESRCH;
+	}
+
+	arm_smmu_detach_dev(domain, dev);
+
+	spin_lock(&xen_domain->lock);
+	list_del(&domain->list);
+	spin_unlock(&xen_domain->lock);
+
+	arm_smmu_domain_destroy(domain);
+	xfree(domain);
+
+	return 0;
+}
+
+static int arm_smmu_reassign_dev(struct domain *s, struct domain *t,
+				 u8 devfn,  struct device *dev)
+{
+	int ret = 0;
+
+	/* Don't allow remapping on other domain than hwdom */
+	if (t != hardware_domain)
+		return -EPERM;
+
+	if (t == s)
+		return 0;
+
+	ret = arm_smmu_deassign_dev(s, dev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int arm_smmu_iommu_domain_init(struct domain *d)
+{
+	struct arm_smmu_xen_domain *xen_domain;
+
+	xen_domain = xzalloc(struct arm_smmu_xen_domain);
+	if ( !xen_domain )
+		return -ENOMEM;
+
+	spin_lock_init(&xen_domain->lock);
+	INIT_LIST_HEAD(&xen_domain->contexts);
+
+	domain_hvm_iommu(d)->arch.priv = xen_domain;
+
+	return 0;
+}
+
+static void __hwdom_init arm_smmu_iommu_hwdom_init(struct domain *d)
+{
+}
+
+static void arm_smmu_iommu_domain_teardown(struct domain *d)
+{
+	struct arm_smmu_xen_domain *xen_domain = domain_hvm_iommu(d)->arch.priv;
+
+	ASSERT(list_empty(&xen_domain->contexts));
+	xfree(xen_domain);
+}
+
+
+static int arm_smmu_map_page(struct domain *d, unsigned long gfn,
+			     unsigned long mfn, unsigned int flags)
+{
+	p2m_type_t t;
+
+	/*
+	 * Grant mappings can be used for DMA requests. The dev_bus_addr
+	 * returned by the hypercall is the MFN (not the IPA). For device
+	 * protected by an IOMMU, Xen needs to add a 1:1 mapping in the domain
+	 * p2m to allow DMA request to work.
+	 * This is only valid when the domain is directed mapped. Hence this
+	 * function should only be used by gnttab code with gfn == mfn.
+	 */
+	BUG_ON(!is_domain_direct_mapped(d));
+	BUG_ON(mfn != gfn);
+
+	/* We only support readable and writable flags */
+	if (!(flags & (IOMMUF_readable | IOMMUF_writable)))
+		return -EINVAL;
+
+	t = (flags & IOMMUF_writable) ? p2m_iommu_map_rw : p2m_iommu_map_ro;
+
+	/*
+	 * The function guest_physmap_add_entry replaces the current mapping
+	 * if there is already one...
+	 */
+	return guest_physmap_add_entry(d, gfn, mfn, 0, t);
+}
+
+static int arm_smmu_unmap_page(struct domain *d, unsigned long gfn)
+{
+	/*
+	 * This function should only be used by gnttab code when the domain
+	 * is direct mapped
+	 */
+	if ( !is_domain_direct_mapped(d) )
+		return -EINVAL;
+
+	guest_physmap_remove_page(d, gfn, gfn, 0);
+
+	return 0;
+}
+
+static const struct iommu_ops arm_smmu_iommu_ops = {
+    .init = arm_smmu_iommu_domain_init,
+    .hwdom_init = arm_smmu_iommu_hwdom_init,
+    .teardown = arm_smmu_iommu_domain_teardown,
+    .iotlb_flush = arm_smmu_iotlb_flush,
+    .iotlb_flush_all = arm_smmu_iotlb_flush_all,
+    .assign_device = arm_smmu_assign_dev,
+    .reassign_device = arm_smmu_reassign_dev,
+    .map_page = arm_smmu_map_page,
+    .unmap_page = arm_smmu_unmap_page,
+};
+
+static __init int arm_smmu_dt_init(struct dt_device_node *dev,
+				   const void *data)
+{
+	int rc;
+
+	/*
+	 * Even if the device can't be initialized, we don't want to
+	 * give the SMMU device to dom0.
+	 */
+	dt_device_set_used_by(dev, DOMID_XEN);
+
+	rc = arm_smmu_device_dt_probe(dev);
+	if ( !rc )
+		iommu_set_ops(&arm_smmu_iommu_ops);
+
+	return rc;
+}
+
+DT_DEVICE_START(smmu, "ARM SMMU", DEVICE_IOMMU)
+	.dt_match = arm_smmu_of_match,
+	.init = arm_smmu_dt_init,
+DT_DEVICE_END
-- 
2.1.3

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

* Re: [PATCH for 4.6 03/13] xen: Introduce ACCESS_ONCE macro
  2014-12-16 20:08 ` [PATCH for 4.6 03/13] xen: Introduce ACCESS_ONCE macro Julien Grall
@ 2014-12-17 10:05   ` Jan Beulich
  2014-12-17 12:54     ` Julien Grall
  0 siblings, 1 reply; 41+ messages in thread
From: Jan Beulich @ 2014-12-17 10:05 UTC (permalink / raw)
  To: Julien Grall
  Cc: Keir Fraser, ian.campbell, tim, manish.jaggi, Ian Jackson,
	stefano.stabellini, xen-devel

>>> On 16.12.14 at 21:08, <julien.grall@linaro.org> wrote:
> --- a/xen/include/xen/compiler.h
> +++ b/xen/include/xen/compiler.h
> @@ -90,4 +90,18 @@
>      __asm__ ("" : "=r"(__ptr) : "0"(ptr));      \
>      (typeof(ptr)) (__ptr + (off)); })
>  
> +/*
> + * Prevent the compiler from merging or refetching accesses.  The compiler
> + * is also forbidden from reordering successive instances of ACCESS_ONCE(),
> + * but only when the compiler is aware of some particular ordering.  One way
> + * to make the compiler aware of ordering is to put the two invocations of
> + * ACCESS_ONCE() in different C statements.
> + *
> + * This macro does absolutely -nothing- to prevent the CPU from reordering,
> + * merging, or refetching absolutely anything at any time.  Its main intended
> + * use is to mediate communication between process-level code and irq/NMI
> + * handlers, all running on the same CPU.
> + */
> +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))

Any reason not to simply use {read,write}_atomic() instead, which we
already have?

Jan

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

* Re: [PATCH for 4.6 07/13] xen: Introduce a generic way to describe device
  2014-12-16 20:08 ` [PATCH for 4.6 07/13] xen: Introduce a generic way to describe device Julien Grall
@ 2014-12-17 10:16   ` Jan Beulich
  2014-12-17 10:30     ` Julien Grall
  2014-12-18  1:12   ` Zhang, Yang Z
  1 sibling, 1 reply; 41+ messages in thread
From: Jan Beulich @ 2014-12-17 10:16 UTC (permalink / raw)
  To: Julien Grall
  Cc: Keir Fraser, ian.campbell, manish.jaggi, tim, stefano.stabellini,
	xen-devel

>>> On 16.12.14 at 21:08, <julien.grall@linaro.org> wrote:
> --- a/xen/common/Makefile
> +++ b/xen/common/Makefile
> @@ -2,6 +2,7 @@ obj-y += bitmap.o
>  obj-y += core_parking.o
>  obj-y += cpu.o
>  obj-y += cpupool.o
> +obj-y += device.o

Shouldn't this instead be two lines, one using HAS_PCI and the second
HAS_DEVICE_TREE?

> @@ -75,8 +76,19 @@ struct pci_dev {
>  #define PT_FAULT_THRESHOLD 10
>      } fault;
>      u64 vf_rlen[6];
> +
> +    struct device dev;
>  };

I'm not convinced yet that growing this structure (of which we have
quite many instances on some systems) is really worth it, in particular
on x86 where we (so far) only have one device type anyway.

> +#define pci_to_dev(pcidev)  (&(pcidev)->dev)
> +
> +static inline struct pci_dev *dev_to_pci(struct device *dev)
> +{
> +    ASSERT(dev->type == DEV_PCI);
> +
> +    return container_of(dev, struct pci_dev, dev);
> +}

While the former is const-correct, I dislike the inability of passing
pointers to const into helper functions like the latter. I can't think
of a good solution other than introducing a second const variant
of it, but I suppose we should try to find alternatives before
adding such a construct that moves us in a direction opposite to
getting our code more const-correct.

Jan

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

* Re: [PATCH for 4.6 08/13] xen/iommu: Consolidate device assignment ops into a single set
  2014-12-16 20:08 ` [PATCH for 4.6 08/13] xen/iommu: Consolidate device assignment ops into a single set Julien Grall
@ 2014-12-17 10:20   ` Jan Beulich
  0 siblings, 0 replies; 41+ messages in thread
From: Jan Beulich @ 2014-12-17 10:20 UTC (permalink / raw)
  To: Julien Grall
  Cc: Kevin Tian, ian.campbell, manish.jaggi, tim, stefano.stabellini,
	Suravee Suthikulpanit, xen-devel, Yang Zhang,
	Aravind Gopalakrishnan

>>> On 16.12.14 at 21:08, <julien.grall@linaro.org> wrote:
> @@ -123,22 +124,17 @@ struct page_info;
>  struct iommu_ops {
>      int (*init)(struct domain *d);
>      void (*hwdom_init)(struct domain *d);
> -#ifdef HAS_PCI
> -    int (*add_device)(u8 devfn, struct pci_dev *);
> -    int (*enable_device)(struct pci_dev *pdev);
> -    int (*remove_device)(u8 devfn, struct pci_dev *);
> -    int (*assign_device)(struct domain *, u8 devfn, struct pci_dev *);
> +    int (*add_device)(u8 devfn, struct device *);
> +    int (*enable_device)(struct device *dev);
> +    int (*remove_device)(u8 devfn, struct device *);
> +    int (*assign_device)(struct domain *, u8 devfn, struct device *);
>      int (*reassign_device)(struct domain *s, struct domain *t,
> -			   u8 devfn, struct pci_dev *);
> +                           u8 devfn, struct device *);

Please be consistent and either drop the parameter name from all
struct device * instances (my preference), or add one everywhere
(apparently the preference of a some other folks).

Jan

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

* Re: [PATCH for 4.6 07/13] xen: Introduce a generic way to describe device
  2014-12-17 10:16   ` Jan Beulich
@ 2014-12-17 10:30     ` Julien Grall
  2014-12-17 10:46       ` Jan Beulich
  0 siblings, 1 reply; 41+ messages in thread
From: Julien Grall @ 2014-12-17 10:30 UTC (permalink / raw)
  To: Jan Beulich
  Cc: Keir Fraser, ian.campbell, manish.jaggi, tim, stefano.stabellini,
	xen-devel

Hi Jan,

On 17/12/2014 10:16, Jan Beulich wrote:
>>>> On 16.12.14 at 21:08, <julien.grall@linaro.org> wrote:
>> --- a/xen/common/Makefile
>> +++ b/xen/common/Makefile
>> @@ -2,6 +2,7 @@ obj-y += bitmap.o
>>   obj-y += core_parking.o
>>   obj-y += cpu.o
>>   obj-y += cpupool.o
>> +obj-y += device.o
>
> Shouldn't this instead be two lines, one using HAS_PCI and the second
> HAS_DEVICE_TREE?

When ARM will gain PCI will, it will fail to compile because device.o is 
included twice.

>
>> @@ -75,8 +76,19 @@ struct pci_dev {
>>   #define PT_FAULT_THRESHOLD 10
>>       } fault;
>>       u64 vf_rlen[6];
>> +
>> +    struct device dev;
>>   };
>
> I'm not convinced yet that growing this structure (of which we have
> quite many instances on some systems) is really worth it, in particular
> on x86 where we (so far) only have one device type anyway.

Actually this will growing by only sizeof (enum type) on x86.

Having a generic way to describe device will really help ARM code (see 
IOMMU).

If we don't have a such thing, we may need to duplicate quite a lots of 
code. Which will make hard to maintain.

>> +#define pci_to_dev(pcidev)  (&(pcidev)->dev)
>> +
>> +static inline struct pci_dev *dev_to_pci(struct device *dev)
>> +{
>> +    ASSERT(dev->type == DEV_PCI);
>> +
>> +    return container_of(dev, struct pci_dev, dev);
>> +}
>
> While the former is const-correct, I dislike the inability of passing
> pointers to const into helper functions like the latter. I can't think
> of a good solution other than introducing a second const variant
> of it, but I suppose we should try to find alternatives before
> adding such a construct that moves us in a direction opposite to
> getting our code more const-correct.

Oh right. I didn't though about that case. I will turn this inline 
function into a macro.

Regards,

-- 
Julien Grall

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

* Re: [PATCH for 4.6 07/13] xen: Introduce a generic way to describe device
  2014-12-17 10:30     ` Julien Grall
@ 2014-12-17 10:46       ` Jan Beulich
  2014-12-17 13:03         ` Julien Grall
  0 siblings, 1 reply; 41+ messages in thread
From: Jan Beulich @ 2014-12-17 10:46 UTC (permalink / raw)
  To: Julien Grall
  Cc: Keir Fraser, ian.campbell, manish.jaggi, tim, stefano.stabellini,
	xen-devel

>>> On 17.12.14 at 11:30, <julien.grall@linaro.org> wrote:
> On 17/12/2014 10:16, Jan Beulich wrote:
>>>>> On 16.12.14 at 21:08, <julien.grall@linaro.org> wrote:
>>> --- a/xen/common/Makefile
>>> +++ b/xen/common/Makefile
>>> @@ -2,6 +2,7 @@ obj-y += bitmap.o
>>>   obj-y += core_parking.o
>>>   obj-y += cpu.o
>>>   obj-y += cpupool.o
>>> +obj-y += device.o
>>
>> Shouldn't this instead be two lines, one using HAS_PCI and the second
>> HAS_DEVICE_TREE?
> 
> When ARM will gain PCI will, it will fail to compile because device.o is 
> included twice.

Not necessarily: If we don't do this already, we should eliminate
duplicates from $(obj-y) just like Linux does.

>>> @@ -75,8 +76,19 @@ struct pci_dev {
>>>   #define PT_FAULT_THRESHOLD 10
>>>       } fault;
>>>       u64 vf_rlen[6];
>>> +
>>> +    struct device dev;
>>>   };
>>
>> I'm not convinced yet that growing this structure (of which we have
>> quite many instances on some systems) is really worth it, in particular
>> on x86 where we (so far) only have one device type anyway.
> 
> Actually this will growing by only sizeof (enum type) on x86.

No, by 8 bytes (due to padding).

> Having a generic way to describe device will really help ARM code (see 
> IOMMU).
> 
> If we don't have a such thing, we may need to duplicate quite a lots of 
> code. Which will make hard to maintain.

Not really, if e.g. "device" was simply an alias of "pci_dev" on x86.

>>> +#define pci_to_dev(pcidev)  (&(pcidev)->dev)
>>> +
>>> +static inline struct pci_dev *dev_to_pci(struct device *dev)
>>> +{
>>> +    ASSERT(dev->type == DEV_PCI);
>>> +
>>> +    return container_of(dev, struct pci_dev, dev);
>>> +}
>>
>> While the former is const-correct, I dislike the inability of passing
>> pointers to const into helper functions like the latter. I can't think
>> of a good solution other than introducing a second const variant
>> of it, but I suppose we should try to find alternatives before
>> adding such a construct that moves us in a direction opposite to
>> getting our code more const-correct.
> 
> Oh right. I didn't though about that case. I will turn this inline 
> function into a macro.

I'm afraid that won't help, as you still need to specify a type as
2nd argument to container_of(), and that type can't be both
const and non-const at the same time, i.e. you can't easily
inherit the const-ness of the passed in pointer.

Jan

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

* Re: [PATCH for 4.6 03/13] xen: Introduce ACCESS_ONCE macro
  2014-12-17 10:05   ` Jan Beulich
@ 2014-12-17 12:54     ` Julien Grall
  2014-12-17 17:10       ` Jan Beulich
  0 siblings, 1 reply; 41+ messages in thread
From: Julien Grall @ 2014-12-17 12:54 UTC (permalink / raw)
  To: Jan Beulich
  Cc: Keir Fraser, ian.campbell, tim, manish.jaggi, Ian Jackson,
	stefano.stabellini, xen-devel

Hi Jan,

On 17/12/14 10:05, Jan Beulich wrote:
>>>> On 16.12.14 at 21:08, <julien.grall@linaro.org> wrote:
>> --- a/xen/include/xen/compiler.h
>> +++ b/xen/include/xen/compiler.h
>> @@ -90,4 +90,18 @@
>>      __asm__ ("" : "=r"(__ptr) : "0"(ptr));      \
>>      (typeof(ptr)) (__ptr + (off)); })
>>  
>> +/*
>> + * Prevent the compiler from merging or refetching accesses.  The compiler
>> + * is also forbidden from reordering successive instances of ACCESS_ONCE(),
>> + * but only when the compiler is aware of some particular ordering.  One way
>> + * to make the compiler aware of ordering is to put the two invocations of
>> + * ACCESS_ONCE() in different C statements.
>> + *
>> + * This macro does absolutely -nothing- to prevent the CPU from reordering,
>> + * merging, or refetching absolutely anything at any time.  Its main intended
>> + * use is to mediate communication between process-level code and irq/NMI
>> + * handlers, all running on the same CPU.
>> + */
>> +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
> 
> Any reason not to simply use {read,write}_atomic() instead, which we
> already have?

To avoid modifying Linux drivers when it's not necessary and doesn't harm.

Regards,

-- 
Julien Grall

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

* Re: [PATCH for 4.6 07/13] xen: Introduce a generic way to describe device
  2014-12-17 10:46       ` Jan Beulich
@ 2014-12-17 13:03         ` Julien Grall
  2014-12-17 17:17           ` Jan Beulich
  0 siblings, 1 reply; 41+ messages in thread
From: Julien Grall @ 2014-12-17 13:03 UTC (permalink / raw)
  To: Jan Beulich
  Cc: Keir Fraser, ian.campbell, manish.jaggi, tim, stefano.stabellini,
	xen-devel

Hi Jan,

On 17/12/14 10:46, Jan Beulich wrote:
>>>> On 17.12.14 at 11:30, <julien.grall@linaro.org> wrote:
>> On 17/12/2014 10:16, Jan Beulich wrote:
>>>>>> On 16.12.14 at 21:08, <julien.grall@linaro.org> wrote:
>>>> --- a/xen/common/Makefile
>>>> +++ b/xen/common/Makefile
>>>> @@ -2,6 +2,7 @@ obj-y += bitmap.o
>>>>   obj-y += core_parking.o
>>>>   obj-y += cpu.o
>>>>   obj-y += cpupool.o
>>>> +obj-y += device.o
>>>
>>> Shouldn't this instead be two lines, one using HAS_PCI and the second
>>> HAS_DEVICE_TREE?
>>
>> When ARM will gain PCI will, it will fail to compile because device.o is 
>> included twice.
> 
> Not necessarily: If we don't do this already, we should eliminate
> duplicates from $(obj-y) just like Linux does.

I will give a look.

>>>> @@ -75,8 +76,19 @@ struct pci_dev {
>>>>   #define PT_FAULT_THRESHOLD 10
>>>>       } fault;
>>>>       u64 vf_rlen[6];
>>>> +
>>>> +    struct device dev;
>>>>   };
>>>
>>> I'm not convinced yet that growing this structure (of which we have
>>> quite many instances on some systems) is really worth it, in particular
>>> on x86 where we (so far) only have one device type anyway.
>>
>> Actually this will growing by only sizeof (enum type) on x86.
> 
> No, by 8 bytes (due to padding).

>> Having a generic way to describe device will really help ARM code (see 
>> IOMMU).
>>
>> If we don't have a such thing, we may need to duplicate quite a lots of 
>> code. Which will make hard to maintain.
> 
> Not really, if e.g. "device" was simply an alias of "pci_dev" on x86.

How many pci_dev instance you could have on a platform? 1000? Though it
might be a high value but that mean we use 2k more of RAM.

It doesn't seem to bad for the benefit to have a clear code.

>>>> +#define pci_to_dev(pcidev)  (&(pcidev)->dev)
>>>> +
>>>> +static inline struct pci_dev *dev_to_pci(struct device *dev)
>>>> +{
>>>> +    ASSERT(dev->type == DEV_PCI);
>>>> +
>>>> +    return container_of(dev, struct pci_dev, dev);
>>>> +}
>>>
>>> While the former is const-correct, I dislike the inability of passing
>>> pointers to const into helper functions like the latter. I can't think
>>> of a good solution other than introducing a second const variant
>>> of it, but I suppose we should try to find alternatives before
>>> adding such a construct that moves us in a direction opposite to
>>> getting our code more const-correct.
>>
>> Oh right. I didn't though about that case. I will turn this inline 
>> function into a macro.
> 
> I'm afraid that won't help, as you still need to specify a type as
> 2nd argument to container_of(), and that type can't be both
> const and non-const at the same time, i.e. you can't easily
> inherit the const-ness of the passed in pointer.

I agree that we will drop the const-ness. But is it really an issue?

We won't have many place where we don't want to modify the pci_dev.

Regards,

-- 
Julien Grall

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

* Re: [PATCH for 4.6 03/13] xen: Introduce ACCESS_ONCE macro
  2014-12-17 12:54     ` Julien Grall
@ 2014-12-17 17:10       ` Jan Beulich
  2014-12-17 17:52         ` Andrew Cooper
  0 siblings, 1 reply; 41+ messages in thread
From: Jan Beulich @ 2014-12-17 17:10 UTC (permalink / raw)
  To: julien.grall
  Cc: keir, ian.campbell, tim, manish.jaggi, ian.jackson,
	stefano.stabellini, xen-devel

>>> Julien Grall <julien.grall@linaro.org> 12/17/14 1:55 PM >>>
>On 17/12/14 10:05, Jan Beulich wrote:
>>>> On 16.12.14 at 21:08, <julien.grall@linaro.org> wrote:
>>> +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
>> 
>> Any reason not to simply use {read,write}_atomic() instead, which we
>> already have?
>
>To avoid modifying Linux drivers when it's not necessary and doesn't harm.

I realize that's the motivation, but I also view it as problematic to have two
different constructs doing the same thing. Defining the new one in terms of
the existing ones doesn't seem possible (or else I would suggest that in
order for the connection to be obvious). We'll see what other maintainers
think...

Jan

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

* Re: [PATCH for 4.6 07/13] xen: Introduce a generic way to describe device
  2014-12-17 13:03         ` Julien Grall
@ 2014-12-17 17:17           ` Jan Beulich
  2014-12-18 15:56             ` Julien Grall
  0 siblings, 1 reply; 41+ messages in thread
From: Jan Beulich @ 2014-12-17 17:17 UTC (permalink / raw)
  To: julien.grall
  Cc: keir, ian.campbell, manish.jaggi, tim, stefano.stabellini, xen-devel

>>> Julien Grall <julien.grall@linaro.org> 12/17/14 2:04 PM >>>
>On 17/12/14 10:46, Jan Beulich wrote:
>>>>> On 17.12.14 at 11:30, <julien.grall@linaro.org> wrote:
>>> Having a generic way to describe device will really help ARM code (see 
>>> IOMMU).
>>>
>>> If we don't have a such thing, we may need to duplicate quite a lots of 
>>> code. Which will make hard to maintain.
>> 
>> Not really, if e.g. "device" was simply an alias of "pci_dev" on x86.
>
>How many pci_dev instance you could have on a platform? 1000? Though it
>might be a high value but that mean we use 2k more of RAM.

Sure the total amount isn't big. But these days everyone thinks that way, and
data size gets grown without much consideration. And you shouldn't just think
about RAM cache utilization.

>It doesn't seem to bad for the benefit to have a clear code.

Aliasing device and pci_dev for x86 would yield similar clarity afaict.

>>>>> +#define pci_to_dev(pcidev)  (&(pcidev)->dev)
>>>>> +
>>>>> +static inline struct pci_dev *dev_to_pci(struct device *dev)
>>>>> +{
>>>>> +    ASSERT(dev->type == DEV_PCI);
>>>>> +
>>>>> +    return container_of(dev, struct pci_dev, dev);
>>>>> +}
>>>>
>>>> While the former is const-correct, I dislike the inability of passing
>>>> pointers to const into helper functions like the latter. I can't think
>>>> of a good solution other than introducing a second const variant
>>>> of it, but I suppose we should try to find alternatives before
>>>> adding such a construct that moves us in a direction opposite to
>>>> getting our code more const-correct.
>>>
>>> Oh right. I didn't though about that case. I will turn this inline 
>>> function into a macro.
>> 
>> I'm afraid that won't help, as you still need to specify a type as
>> 2nd argument to container_of(), and that type can't be both
>> const and non-const at the same time, i.e. you can't easily
>> inherit the const-ness of the passed in pointer.
>
>I agree that we will drop the const-ness. But is it really an issue?
>
>We won't have many place where we don't want to modify the pci_dev.

Did you check (including places where const could be added)? But at least
you didn't have to drop and const-s, so I'm not heavily objecting the change
you propose.

Jan

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

* Re: [PATCH for 4.6 03/13] xen: Introduce ACCESS_ONCE macro
  2014-12-17 17:10       ` Jan Beulich
@ 2014-12-17 17:52         ` Andrew Cooper
  2014-12-18 15:58           ` Julien Grall
                             ` (2 more replies)
  0 siblings, 3 replies; 41+ messages in thread
From: Andrew Cooper @ 2014-12-17 17:52 UTC (permalink / raw)
  To: Jan Beulich, julien.grall
  Cc: keir, ian.campbell, tim, manish.jaggi, ian.jackson,
	stefano.stabellini, xen-devel

On 17/12/14 17:10, Jan Beulich wrote:
>>>> Julien Grall <julien.grall@linaro.org> 12/17/14 1:55 PM >>>
>> On 17/12/14 10:05, Jan Beulich wrote:
>>>>> On 16.12.14 at 21:08, <julien.grall@linaro.org> wrote:
>>>> +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
>>> Any reason not to simply use {read,write}_atomic() instead, which we
>>> already have?
>> To avoid modifying Linux drivers when it's not necessary and doesn't harm.
> I realize that's the motivation, but I also view it as problematic to have two
> different constructs doing the same thing. Defining the new one in terms of
> the existing ones doesn't seem possible (or else I would suggest that in
> order for the connection to be obvious). We'll see what other maintainers
> think...

Personally, I find the semantics of ACCESS_ONCE() more intuitive than
read/write_atomic(), and it is certainly more familiar to Linux developers.

Furthermore, ACCESS_ONCE() doesn't force an mov instruction if the
compiler can identify a better instruction to use.

There are only a handful of user users of read/write_atomic().  It would
not be hard to make a blanket switch, if we chose to go in that direction.

~Andrew

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

* Re: [PATCH for 4.6 07/13] xen: Introduce a generic way to describe device
  2014-12-16 20:08 ` [PATCH for 4.6 07/13] xen: Introduce a generic way to describe device Julien Grall
  2014-12-17 10:16   ` Jan Beulich
@ 2014-12-18  1:12   ` Zhang, Yang Z
  2014-12-18  8:52     ` Julien Grall
  1 sibling, 1 reply; 41+ messages in thread
From: Zhang, Yang Z @ 2014-12-18  1:12 UTC (permalink / raw)
  To: Julien Grall, xen-devel
  Cc: Keir Fraser, ian.campbell, manish.jaggi, tim, stefano.stabellini,
	Jan Beulich

Julien Grall wrote on 2014-12-17:
> diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h index
> 5f295f3..6ace79d 100644
> --- a/xen/include/xen/pci.h
> +++ b/xen/include/xen/pci.h
> @@ -13,6 +13,7 @@
>  #include <xen/irq.h> #include <xen/pci_regs.h> #include <xen/pfn.h>
>  +#include <xen/device.h> #include <asm/pci.h>
>  
>  /* @@ -75,8 +76,19 @@ struct pci_dev { #define PT_FAULT_THRESHOLD 10
>      } fault;
>      u64 vf_rlen[6];
> +
> +    struct device dev;
>  };
> +#define pci_to_dev(pcidev)  (&(pcidev)->dev)
> +
> +static inline struct pci_dev *dev_to_pci(struct device *dev) {
> +    ASSERT(dev->type == DEV_PCI);
> +
> +    return container_of(dev, struct pci_dev, dev); }
> +
>  #define for_each_pdev(domain, pdev) \
>      list_for_each_entry(pdev, &(domain->arch.pdev_list), domain_list)

I'd suggest splitting the changes to common code to a separate patch and also CC the VT-d/AMD maintainers. Because I didn't find those definitions when reviewing the 8th patch and I need to search the whole patch set to find them.

Best regards,
Yang

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

* Re: [PATCH for 4.6 07/13] xen: Introduce a generic way to describe device
  2014-12-18  1:12   ` Zhang, Yang Z
@ 2014-12-18  8:52     ` Julien Grall
  0 siblings, 0 replies; 41+ messages in thread
From: Julien Grall @ 2014-12-18  8:52 UTC (permalink / raw)
  To: Zhang, Yang Z, xen-devel
  Cc: Keir Fraser, ian.campbell, manish.jaggi, tim, stefano.stabellini,
	Jan Beulich

Hello Zhang,

Please respect the netiquette and avoid lines over 80 characters.


On 18/12/2014 01:12, Zhang, Yang Z wrote:
> I'd suggest splitting the changes to common code to a separate patch and also CC the VT-d/AMD maintainers.

This patch is already common code. Though, there was some changes in 
arch/arm because of interdependency. Splitting more won't make more sense.

 > Because I didn't find those definitions when reviewing the 8th patch 
and I need to search the whole patch set to find them.

it's not a new problem. A patch may have a dependency on another patch 
who has dependency on another one... This would end up to be CCed on 
every patch and spam your inbox.

I usually provide a git repo with my patch series in the cover letter. 
So if you miss anything you can look to the code and found the relevant 
patch.

Anyway... I will CC next time

Sincerely yours,

-- 
Julien Grall

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

* Re: [PATCH for 4.6 07/13] xen: Introduce a generic way to describe device
  2014-12-17 17:17           ` Jan Beulich
@ 2014-12-18 15:56             ` Julien Grall
  2014-12-18 16:02               ` Jan Beulich
  0 siblings, 1 reply; 41+ messages in thread
From: Julien Grall @ 2014-12-18 15:56 UTC (permalink / raw)
  To: Jan Beulich
  Cc: keir, ian.campbell, manish.jaggi, tim, stefano.stabellini, xen-devel

Hi Jan,

On 17/12/2014 17:17, Jan Beulich wrote:
>>>> Julien Grall <julien.grall@linaro.org> 12/17/14 2:04 PM >>>
>> On 17/12/14 10:46, Jan Beulich wrote:
>>>>>> On 17.12.14 at 11:30, <julien.grall@linaro.org> wrote:
>>>> Having a generic way to describe device will really help ARM code (see
>>>> IOMMU).
>>>>
>>>> If we don't have a such thing, we may need to duplicate quite a lots of
>>>> code. Which will make hard to maintain.
>>>
>>> Not really, if e.g. "device" was simply an alias of "pci_dev" on x86.
>>
>> How many pci_dev instance you could have on a platform? 1000? Though it
>> might be a high value but that mean we use 2k more of RAM.
>
> Sure the total amount isn't big. But these days everyone thinks that way, and
> data size gets grown without much consideration. And you shouldn't just think
> about RAM cache utilization.

I will go ahead with the aliasing.

>> It doesn't seem to bad for the benefit to have a clear code.
>
> Aliasing device and pci_dev for x86 would yield similar clarity afaict.

To be sure, by aliasing you mean creating a typedef?

For x86:
typedef struct pci_dev device_t;

And for ARM:
typedef struct device device_t;

>
>>>>>> +#define pci_to_dev(pcidev)  (&(pcidev)->dev)
>>>>>> +
>>>>>> +static inline struct pci_dev *dev_to_pci(struct device *dev)
>>>>>> +{
>>>>>> +    ASSERT(dev->type == DEV_PCI);
>>>>>> +
>>>>>> +    return container_of(dev, struct pci_dev, dev);
>>>>>> +}
>>>>>
>>>>> While the former is const-correct, I dislike the inability of passing
>>>>> pointers to const into helper functions like the latter. I can't think
>>>>> of a good solution other than introducing a second const variant
>>>>> of it, but I suppose we should try to find alternatives before
>>>>> adding such a construct that moves us in a direction opposite to
>>>>> getting our code more const-correct.
>>>>
>>>> Oh right. I didn't though about that case. I will turn this inline
>>>> function into a macro.
>>>
>>> I'm afraid that won't help, as you still need to specify a type as
>>> 2nd argument to container_of(), and that type can't be both
>>> const and non-const at the same time, i.e. you can't easily
>>> inherit the const-ness of the passed in pointer.
>>
>> I agree that we will drop the const-ness. But is it really an issue?
>>
>> We won't have many place where we don't want to modify the pci_dev.
>
> Did you check (including places where const could be added)? But at least
> you didn't have to drop and const-s, so I'm not heavily objecting the change
> you propose.

The only usage will be in the IOMMU code where most of the time we 
require a non const version.

At least on the SMMU driver, we have to store data per-device. This is 
to know what is the SMMU master for this device.

-- 
Julien Grall

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

* Re: [PATCH for 4.6 03/13] xen: Introduce ACCESS_ONCE macro
  2014-12-17 17:52         ` Andrew Cooper
@ 2014-12-18 15:58           ` Julien Grall
  2014-12-18 15:58           ` Julien Grall
  2015-01-15 13:39           ` Julien Grall
  2 siblings, 0 replies; 41+ messages in thread
From: Julien Grall @ 2014-12-18 15:58 UTC (permalink / raw)
  To: Andrew Cooper, Jan Beulich
  Cc: keir, ian.campbell, tim, manish.jaggi, ian.jackson,
	stefano.stabellini, xen-devel

Hi Andrew Cooper,

On 17/12/2014 17:52, Andrew Cooper wrote:
> On 17/12/14 17:10, Jan Beulich wrote:
>>>>> Julien Grall <julien.grall@linaro.org> 12/17/14 1:55 PM >>>
>>> On 17/12/14 10:05, Jan Beulich wrote:
>>>>>> On 16.12.14 at 21:08, <julien.grall@linaro.org> wrote:
>>>>> +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
>>>> Any reason not to simply use {read,write}_atomic() instead, which we
>>>> already have?
>>> To avoid modifying Linux drivers when it's not necessary and doesn't harm.
>> I realize that's the motivation, but I also view it as problematic to have two
>> different constructs doing the same thing. Defining the new one in terms of
>> the existing ones doesn't seem possible (or else I would suggest that in
>> order for the connection to be obvious). We'll see what other maintainers
>> think...
>
> Personally, I find the semantics of ACCESS_ONCE() more intuitive than
> read/write_atomic(), and it is certainly more familiar to Linux developers.
>
> Furthermore, ACCESS_ONCE() doesn't force an mov instruction if the
> compiler can identify a better instruction to use.
>
> There are only a handful of user users of read/write_atomic().  It would
> not be hard to make a blanket switch, if we chose to go in that direction.

Do you mean replacing read/write_atomic() by

#define read_atomic(p) ACCESS_ONCE(p)
#define write_atomic(p, x) (ACCESS_ONCE(p) = x)

Regards,

-- 
Julien Grall

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

* Re: [PATCH for 4.6 03/13] xen: Introduce ACCESS_ONCE macro
  2014-12-17 17:52         ` Andrew Cooper
  2014-12-18 15:58           ` Julien Grall
@ 2014-12-18 15:58           ` Julien Grall
  2015-01-15 13:39           ` Julien Grall
  2 siblings, 0 replies; 41+ messages in thread
From: Julien Grall @ 2014-12-18 15:58 UTC (permalink / raw)
  To: Andrew Cooper, Jan Beulich
  Cc: keir, ian.campbell, tim, manish.jaggi, ian.jackson,
	stefano.stabellini, xen-devel

Hi Andrew,

On 17/12/2014 17:52, Andrew Cooper wrote:
> On 17/12/14 17:10, Jan Beulich wrote:
>>>>> Julien Grall <julien.grall@linaro.org> 12/17/14 1:55 PM >>>
>>> On 17/12/14 10:05, Jan Beulich wrote:
>>>>>> On 16.12.14 at 21:08, <julien.grall@linaro.org> wrote:
>>>>> +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
>>>> Any reason not to simply use {read,write}_atomic() instead, which we
>>>> already have?
>>> To avoid modifying Linux drivers when it's not necessary and doesn't harm.
>> I realize that's the motivation, but I also view it as problematic to have two
>> different constructs doing the same thing. Defining the new one in terms of
>> the existing ones doesn't seem possible (or else I would suggest that in
>> order for the connection to be obvious). We'll see what other maintainers
>> think...
>
> Personally, I find the semantics of ACCESS_ONCE() more intuitive than
> read/write_atomic(), and it is certainly more familiar to Linux developers.
>
> Furthermore, ACCESS_ONCE() doesn't force an mov instruction if the
> compiler can identify a better instruction to use.
>
> There are only a handful of user users of read/write_atomic().  It would
> not be hard to make a blanket switch, if we chose to go in that direction.

Do you mean replacing read/write_atomic() by

#define read_atomic(p) ACCESS_ONCE(p)
#define write_atomic(p, x) (ACCESS_ONCE(p) = x)

Regards,

-- 
Julien Grall

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

* Re: [PATCH for 4.6 07/13] xen: Introduce a generic way to describe device
  2014-12-18 15:56             ` Julien Grall
@ 2014-12-18 16:02               ` Jan Beulich
  2014-12-18 16:07                 ` Julien Grall
  0 siblings, 1 reply; 41+ messages in thread
From: Jan Beulich @ 2014-12-18 16:02 UTC (permalink / raw)
  To: Julien Grall
  Cc: keir, ian.campbell, manish.jaggi, tim, stefano.stabellini, xen-devel

>>> On 18.12.14 at 16:56, <julien.grall@linaro.org> wrote:
> On 17/12/2014 17:17, Jan Beulich wrote:
>> Aliasing device and pci_dev for x86 would yield similar clarity afaict.
> 
> To be sure, by aliasing you mean creating a typedef?
> 
> For x86:
> typedef struct pci_dev device_t;
> 
> And for ARM:
> typedef struct device device_t;

Yes, I think that's the only reasonable thing. Using a #define would
seem ugly no matter which direction you did it.

Jan

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

* Re: [PATCH for 4.6 07/13] xen: Introduce a generic way to describe device
  2014-12-18 16:02               ` Jan Beulich
@ 2014-12-18 16:07                 ` Julien Grall
  0 siblings, 0 replies; 41+ messages in thread
From: Julien Grall @ 2014-12-18 16:07 UTC (permalink / raw)
  To: Jan Beulich
  Cc: keir, ian.campbell, manish.jaggi, tim, stefano.stabellini, xen-devel

On 18/12/2014 16:02, Jan Beulich wrote:
>>>> On 18.12.14 at 16:56, <julien.grall@linaro.org> wrote:
>> On 17/12/2014 17:17, Jan Beulich wrote:
>>> Aliasing device and pci_dev for x86 would yield similar clarity afaict.
>>
>> To be sure, by aliasing you mean creating a typedef?
>>
>> For x86:
>> typedef struct pci_dev device_t;
>>
>> And for ARM:
>> typedef struct device device_t;
>
> Yes, I think that's the only reasonable thing. Using a #define would
> seem ugly no matter which direction you did it.

Right. We have some place where device and pci_device are used as 
variable. It would have introduced some strange compilation error.

I will go ahead with this solution.

Regards,

-- 
Julien Grall

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

* Re: [PATCH for 4.6 03/13] xen: Introduce ACCESS_ONCE macro
  2014-12-17 17:52         ` Andrew Cooper
  2014-12-18 15:58           ` Julien Grall
  2014-12-18 15:58           ` Julien Grall
@ 2015-01-15 13:39           ` Julien Grall
  2 siblings, 0 replies; 41+ messages in thread
From: Julien Grall @ 2015-01-15 13:39 UTC (permalink / raw)
  To: Andrew Cooper, Jan Beulich
  Cc: keir, ian.campbell, tim, manish.jaggi, ian.jackson,
	stefano.stabellini, xen-devel

Hi,

On 17/12/14 17:52, Andrew Cooper wrote:
> On 17/12/14 17:10, Jan Beulich wrote:
>>>>> Julien Grall <julien.grall@linaro.org> 12/17/14 1:55 PM >>>
>>> On 17/12/14 10:05, Jan Beulich wrote:
>>>>>> On 16.12.14 at 21:08, <julien.grall@linaro.org> wrote:
>>>>> +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
>>>> Any reason not to simply use {read,write}_atomic() instead, which we
>>>> already have?
>>> To avoid modifying Linux drivers when it's not necessary and doesn't harm.
>> I realize that's the motivation, but I also view it as problematic to have two
>> different constructs doing the same thing. Defining the new one in terms of
>> the existing ones doesn't seem possible (or else I would suggest that in
>> order for the connection to be obvious). We'll see what other maintainers
>> think...
> 
> Personally, I find the semantics of ACCESS_ONCE() more intuitive than
> read/write_atomic(), and it is certainly more familiar to Linux developers.
> 
> Furthermore, ACCESS_ONCE() doesn't force an mov instruction if the
> compiler can identify a better instruction to use.
> 
> There are only a handful of user users of read/write_atomic().  It would
> not be hard to make a blanket switch, if we chose to go in that direction.

It seems the next version of Linux will start to replace ACCESS_ONCE by
READ_ONCE/WRITE_ONCE.

So I will drop this patch and declare ACCESS_ONCE in the SMMU driver. I
will keep it as long as Linux is still using ACCESS_ONCE in this driver.

Regards,

-- 
Julien Grall

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

* Re: [PATCH for 4.6 13/13] xen/iommu: smmu: Add Xen specific code to be able to use the driver
  2014-12-16 20:08 ` [PATCH for 4.6 13/13] xen/iommu: smmu: Add Xen specific code to be able to use the driver Julien Grall
@ 2015-02-18  1:02   ` Manish
  2015-02-18 11:47     ` Julien Grall
  0 siblings, 1 reply; 41+ messages in thread
From: Manish @ 2015-02-18  1:02 UTC (permalink / raw)
  To: Julien Grall, xen-devel
  Cc: tim, manish.jaggi, will.daecon, ian.campbell, stefano.stabellini


On 17/12/14 1:38 am, Julien Grall wrote:
> The main goal is to modify as little the Linux code to be able to port
> easily new feature added in Linux repo for the driver.
>
> To achieve that we:
>      - Add helpers to Linux function not implemented on Xen
>      - Add callbacks used by Xen to do our own stuff and call Linux ones
>      - Only modify when required the code which comes from Linux. If so a
>      comment has been added with /* Xen: ... */ explaining why it's
>      necessary.
>
> The support for PCI has been commented because it's not yet supported by
> Xen ARM and therefore won't compile.
>
> Signed-off-by: Julien Grall <julien.grall@linaro.org>
> ---
>   xen/drivers/passthrough/arm/Makefile |   1 +
>   xen/drivers/passthrough/arm/smmu.c   | 668 +++++++++++++++++++++++++++++++----
>   2 files changed, 602 insertions(+), 67 deletions(-)
>
> diff --git a/xen/drivers/passthrough/arm/Makefile b/xen/drivers/passthrough/arm/Makefile
> index 0484b79..f4cd26e 100644
> --- a/xen/drivers/passthrough/arm/Makefile
> +++ b/xen/drivers/passthrough/arm/Makefile
> @@ -1 +1,2 @@
>   obj-y += iommu.o
> +obj-y += smmu.o
> diff --git a/xen/drivers/passthrough/arm/smmu.c b/xen/drivers/passthrough/arm/smmu.c
> index 8a6514f..3cf1773 100644
> --- a/xen/drivers/passthrough/arm/smmu.c
> +++ b/xen/drivers/passthrough/arm/smmu.c
> @@ -18,6 +18,13 @@
>    *
>    * Author: Will Deacon <will.deacon@arm.com>
>    *
> + * Based on Linux drivers/iommu/arm-smmu.c
> + *	=> commit e6b5be2be4e30037eb551e0ed09dd97bd00d85d3
> + *
> + * Xen modification:
> + * Julien Grall <julien.grall@linaro.org>
> + * Copyright (C) 2014 Linaro Limited.
> + *
>    * This driver currently supports:
>    *	- SMMUv1 and v2 implementations
>    *	- Stream-matching and stream-indexing
> @@ -28,26 +35,154 @@
>    *	- Context fault reporting
>    */

<<<snip >>>

> +/* Xen: Dummy iommu_domain */
> +struct iommu_domain
> +{
> +	struct arm_smmu_domain		*priv;
> +
> +	/* Used to link domain contexts for a same domain */
> +	struct list_head		list;
> +};
> +
> +/* Xen: Describes informations required for a Xen domain */
> +struct arm_smmu_xen_domain {
> +	spinlock_t			lock;
> +	/* List of context (i.e iommu_domain) associated to this domain */
> +	struct list_head		contexts;
> +};
> +
> +/* Xen: Information about each device stored in dev->archdata.iommu */
> +struct arm_smmu_xen_device {
> +	struct iommu_domain *domain;
> +	struct iommu_group *group;
> +};
> +
> +#define dev_archdata(dev) ((struct arm_smmu_xen_device *)dev->archdata.iommu)
> +#define dev_iommu_domain(dev) (dev_archdata(dev)->domain)
> +#define dev_iommu_group(dev) (dev_archdata(dev)->group)
> +
> +/* Xen: Dummy iommu_group */
> +struct iommu_group
> +{
> +	struct arm_smmu_master_cfg *cfg;
> +
> +	atomic_t ref;
> +};
> +
The naming needs to be revisited in this patch. Original driver from 
Will has arm_smmu_domain. This patch adds  iommu_domain, 
arm_smmu_xen_domain, iommu_group.
Could you please add some description about the relation and hierarchy 
of these data structures.

> +static struct iommu_group *iommu_group_alloc(void)
> +{
> +	struct iommu_group *group = xzalloc(struct iommu_group);
> +
> +	if (!group)
> +		return ERR_PTR(-ENOMEM);
> +
> +	atomic_set(&group->ref, 1);
> +
> +	return group;
> +}
> +
> +static void iommu_group_put(struct iommu_group *group)
> +{
> +	if (atomic_dec_and_test(&group->ref))
> +		xfree(group);
> +}
> +
> +static void iommu_group_set_iommudata(struct iommu_group *group,
> +				      struct arm_smmu_master_cfg *cfg,
> +				      void (*releasefn)(void *))
> +{
> +	/* TODO: Store the releasefn for the PCI */
> +	ASSERT(releasefn == NULL);
> +
> +	group->cfg = cfg;
> +}
> +
> +static int iommu_group_add_device(struct iommu_group *group,
> +				  struct device *dev)
> +{
> +	dev_iommu_group(dev) = group;
> +
> +	atomic_inc(&group->ref);
> +
> +	return 0;
> +}
> +
> +static struct iommu_group *iommu_group_get(struct device *dev)
> +{
> +	struct iommu_group *group = dev_iommu_group(dev);
> +
> +	if (group)
> +		atomic_inc(&group->ref);
> +
> +	return group;
> +}
> +
> +#define iommu_group_get_iommudata(group) (group)->cfg
> +
>   static DEFINE_SPINLOCK(arm_smmu_devices_lock);
>   static LIST_HEAD(arm_smmu_devices);
>   
> @@ -455,6 +680,8 @@ static void parse_driver_options(struct arm_smmu_device *smmu)
>   
>   static struct device_node *dev_get_dev_node(struct device *dev)
>   {
> +	/* Xen: TODO: Add support for PCI */
> +#if 0
>   	if (dev_is_pci(dev)) {
>   		struct pci_bus *bus = to_pci_dev(dev)->bus;
>   
> @@ -462,7 +689,7 @@ static struct device_node *dev_get_dev_node(struct device *dev)
>   			bus = bus->parent;
>   		return bus->bridge->parent->of_node;
>   	}
> -
> +#endif
>   	return dev->of_node;
>   }
>   
> @@ -556,6 +783,9 @@ static int register_smmu_master(struct arm_smmu_device *smmu,
>   	master->of_node			= masterspec->np;
>   	master->cfg.num_streamids	= masterspec->args_count;
>   
> +	/* Xen: Let Xen knows that the device is protected by an SMMU */
> +	dt_device_set_protected(masterspec->np);
> +
>   	for (i = 0; i < master->cfg.num_streamids; ++i) {
>   		u16 streamid = masterspec->args[i];
>   
> @@ -651,11 +881,12 @@ static void arm_smmu_tlb_inv_context(struct arm_smmu_domain *smmu_domain)
>   	arm_smmu_tlb_sync(smmu);
>   }
>   
> -static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
> +static void arm_smmu_context_fault(int irq, void *dev,
> +				   struct cpu_user_regs *regs)
>   {
> -	int flags, ret;
> +	int flags;
>   	u32 fsr, far, fsynr, resume;
> -	unsigned long iova;
> +	paddr_t iova;
>   	struct iommu_domain *domain = dev;
>   	struct arm_smmu_domain *smmu_domain = domain->priv;
>   	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
> @@ -666,7 +897,7 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
>   	fsr = readl_relaxed(cb_base + ARM_SMMU_CB_FSR);
>   
>   	if (!(fsr & FSR_FAULT))
> -		return IRQ_NONE;
> +		return;
>   
>   	if (fsr & FSR_IGN)
>   		dev_err_ratelimited(smmu->dev,
> @@ -678,19 +909,16 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
>   
>   	far = readl_relaxed(cb_base + ARM_SMMU_CB_FAR_LO);
>   	iova = far;
> -#ifdef CONFIG_64BIT
> +	/* Xen: The fault address maybe higher than 32 bits on arm32 */
>   	far = readl_relaxed(cb_base + ARM_SMMU_CB_FAR_HI);
> -	iova |= ((unsigned long)far << 32);
> -#endif
> +	iova |= ((paddr_t)far << 32);
>   
>   	if (!report_iommu_fault(domain, smmu->dev, iova, flags)) {
> -		ret = IRQ_HANDLED;
>   		resume = RESUME_RETRY;
>   	} else {
>   		dev_err_ratelimited(smmu->dev,
> -		    "Unhandled context fault: iova=0x%08lx, fsynr=0x%x, cb=%d\n",
> +		    "Unhandled context fault: iova=0x%"PRIpaddr", fsynr=0x%x, cb=%d\n",
>   		    iova, fsynr, cfg->cbndx);
> -		ret = IRQ_NONE;
>   		resume = RESUME_TERMINATE;
>   	}
>   
> @@ -700,11 +928,10 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
>   	/* Retry or terminate any stalled transactions */
>   	if (fsr & FSR_SS)
>   		writel_relaxed(resume, cb_base + ARM_SMMU_CB_RESUME);
> -
> -	return ret;
>   }
>   
> -static irqreturn_t arm_smmu_global_fault(int irq, void *dev)
> +static void arm_smmu_global_fault(int irq, void *dev,
> +                                  struct cpu_user_regs *regs)
>   {
>   	u32 gfsr, gfsynr0, gfsynr1, gfsynr2;
>   	struct arm_smmu_device *smmu = dev;
> @@ -716,7 +943,7 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev)
>   	gfsynr2 = readl_relaxed(gr0_base + ARM_SMMU_GR0_sGFSYNR2);
>   
>   	if (!gfsr)
> -		return IRQ_NONE;
> +		return;
>   
>   	dev_err_ratelimited(smmu->dev,
>   		"Unexpected global fault, this could be serious\n");
> @@ -725,9 +952,10 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev)
>   		gfsr, gfsynr0, gfsynr1, gfsynr2);
>   
>   	writel(gfsr, gr0_base + ARM_SMMU_GR0_sGFSR);
> -	return IRQ_HANDLED;
>   }
>   
> +/* Xen: Page tables are shared with the processor */
> +#if 0
>   static void arm_smmu_flush_pgtable(struct arm_smmu_device *smmu, void *addr,
>   				   size_t size)
>   {
> @@ -749,6 +977,7 @@ static void arm_smmu_flush_pgtable(struct arm_smmu_device *smmu, void *addr,
>   				DMA_TO_DEVICE);
>   	}
>   }
> +#endif
>   
>   static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
>   {
> @@ -757,6 +986,7 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
>   	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
>   	struct arm_smmu_device *smmu = smmu_domain->smmu;
>   	void __iomem *cb_base, *gr0_base, *gr1_base;
> +	paddr_t p2maddr;
>   
>   	gr0_base = ARM_SMMU_GR0(smmu);
>   	gr1_base = ARM_SMMU_GR1(smmu);
> @@ -840,11 +1070,16 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
>   	}
>   
>   	/* TTBR0 */
> -	arm_smmu_flush_pgtable(smmu, cfg->pgd,
> -			       PTRS_PER_PGD * sizeof(pgd_t));
> -	reg = __pa(cfg->pgd);
> +	/* Xen: The page table is shared with the P2M code */
> +	ASSERT(smmu_domain->cfg.domain != NULL);
> +	p2maddr = page_to_maddr(smmu_domain->cfg.domain->arch.p2m.root);
> +
> +	dev_notice(smmu->dev, "d%u: p2maddr 0x%"PRIpaddr"\n",
> +		   smmu_domain->cfg.domain->domain_id, p2maddr);
> +
> +	reg = (p2maddr & ((1ULL << 32) - 1));
>   	writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_LO);
> -	reg = (phys_addr_t)__pa(cfg->pgd) >> 32;
> +	reg = (p2maddr >> 32);
>   	if (stage1)
>   		reg |= ARM_SMMU_CB_ASID(cfg) << TTBRn_HI_ASID_SHIFT;
>   	writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0_HI);
> @@ -886,9 +1121,21 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
>   			reg |= (64 - smmu->s1_input_size) << TTBCR_T0SZ_SHIFT;
>   		}
>   	} else {
> +#if CONFIG_ARM_32
> +		/* Xen: Midway is using 40-bit address space. Though it may
> +		 * not work on other ARM32 platform with SMMU-v1.
> +		 * TODO: A quirk may be necessary if we have to support
> +		 * other ARM32 platform with SMMU-v1.
> +		 */
> +		reg = 0x18 << TTBCR_T0SZ_SHIFT;
> +#else
>   		reg = 0;
> +#endif
>   	}
>   
> +	/* Xen: The attributes to walk the page table should be the same as
> +	 * VTCR_EL2. Currently doesn't differ from Linux ones.
> +	 */
>   	reg |= TTBCR_EAE |
>   	      (TTBCR_SH_IS << TTBCR_SH0_SHIFT) |
>   	      (TTBCR_RGN_WBWA << TTBCR_ORGN0_SHIFT) |
> @@ -1031,7 +1278,6 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
>   static int arm_smmu_domain_init(struct iommu_domain *domain)
>   {
>   	struct arm_smmu_domain *smmu_domain;
> -	pgd_t *pgd;
>   
>   	/*
>   	 * Allocate the domain and initialise some of its data structures.
> @@ -1042,20 +1288,12 @@ static int arm_smmu_domain_init(struct iommu_domain *domain)
>   	if (!smmu_domain)
>   		return -ENOMEM;
>   
> -	pgd = kcalloc(PTRS_PER_PGD, sizeof(pgd_t), GFP_KERNEL);
> -	if (!pgd)
> -		goto out_free_domain;
> -	smmu_domain->cfg.pgd = pgd;
> -
>   	spin_lock_init(&smmu_domain->lock);
>   	domain->priv = smmu_domain;
>   	return 0;
> -
> -out_free_domain:
> -	kfree(smmu_domain);
> -	return -ENOMEM;
>   }
>   
> +#if 0
>   static void arm_smmu_free_ptes(pmd_t *pmd)
>   {
>   	pgtable_t table = pmd_pgtable(*pmd);
> @@ -1118,6 +1356,7 @@ static void arm_smmu_free_pgtables(struct arm_smmu_domain *smmu_domain)
>   
>   	kfree(pgd_base);
>   }
> +#endif
>   
>   /*
>    * For a given set N of 2**order different stream IDs (no duplicates
> @@ -1259,7 +1498,6 @@ static void arm_smmu_domain_destroy(struct iommu_domain *domain)
>   	 * already been detached.
>   	 */
>   	arm_smmu_destroy_domain_context(domain);
> -	arm_smmu_free_pgtables(smmu_domain);
>   	kfree(smmu_domain);
>   }
>   
> @@ -1384,11 +1622,12 @@ static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
>   	/*
>   	 * We *must* clear the S2CR first, because freeing the SMR means
>   	 * that it can be re-allocated immediately.
> +	 * Xen: Unlike Linux, any access to non-configured stream will fault.
>   	 */
>   	for (i = 0; i < cfg->num_streamids; ++i) {
>   		u32 idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
>   
> -		writel_relaxed(S2CR_TYPE_BYPASS,
> +		writel_relaxed(S2CR_TYPE_FAULT,
>   			       gr0_base + ARM_SMMU_GR0_S2CR(idx));
>   	}
>   
> @@ -1408,7 +1647,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
>   		return -ENXIO;
>   	}
>   
> -	if (dev->archdata.iommu) {
> +	if (dev_iommu_domain(dev)) {
>   		dev_err(dev, "already attached to IOMMU domain\n");
>   		return -EEXIST;
>   	}
> @@ -1440,8 +1679,9 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
>   		return -ENODEV;
>   
>   	ret = arm_smmu_domain_add_master(smmu_domain, cfg);
> +
>   	if (!ret)
> -		dev->archdata.iommu = domain;
> +		dev_iommu_domain(dev) = domain;
>   	return ret;
>   }
>   
> @@ -1454,10 +1694,14 @@ static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
>   	if (!cfg)
>   		return;
>   
> -	dev->archdata.iommu = NULL;
> +	dev_iommu_domain(dev) = NULL;
>   	arm_smmu_domain_remove_master(smmu_domain, cfg);
>   }
>   
> +/* Xen: the page table is shared with the processor, therefore helpers to
> + * implement separate is not necessary.
> + */
> +#if 0
>   static bool arm_smmu_pte_is_contiguous_range(unsigned long addr,
>   					     unsigned long end)
>   {
> @@ -1746,7 +1990,10 @@ static phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain,
>   
>   	return __pfn_to_phys(pte_pfn(pte)) | (iova & ~PAGE_MASK);
>   }
> +#endif
>   
> +/* Xen: Functions are not used at the moment */
> +#if 0
>   static bool arm_smmu_capable(enum iommu_cap cap)
>   {
>   	switch (cap) {
> @@ -1775,6 +2022,7 @@ static void __arm_smmu_release_pci_iommudata(void *data)
>   {
>   	kfree(data);
>   }
> +#endif
>   
>   static int arm_smmu_add_device(struct device *dev)
>   {
> @@ -1784,6 +2032,10 @@ static int arm_smmu_add_device(struct device *dev)
>   	void (*releasefn)(void *) = NULL;
>   	int ret;
>   
> +	/* Xen: Check if the device has already been added */
> +	if (dev_iommu_group(dev))
> +		return -EBUSY;
> +
>   	smmu = find_smmu_for_device(dev);
>   	if (!smmu)
>   		return -ENODEV;
> @@ -1795,6 +2047,9 @@ static int arm_smmu_add_device(struct device *dev)
>   	}
>   
>   	if (dev_is_pci(dev)) {
> +		/* Xen: TODO: Add PCI support */
> +		BUG();
> +#if 0
>   		struct pci_dev *pdev = to_pci_dev(dev);
>   
>   		cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
> @@ -1811,6 +2066,7 @@ static int arm_smmu_add_device(struct device *dev)
>   		pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid,
>   				       &cfg->streamids[0]);
>   		releasefn = __arm_smmu_release_pci_iommudata;
> +#endif
>   	} else {
>   		struct arm_smmu_master *master;
>   
> @@ -1831,6 +2087,8 @@ out_put_group:
>   	return ret;
>   }
>   
> +/* Xen: We don't support remove device for now. Will be useful for PCI */
> +#if 0
>   static void arm_smmu_remove_device(struct device *dev)
>   {
>   	iommu_group_remove_device(dev);
> @@ -1888,6 +2146,7 @@ static const struct iommu_ops arm_smmu_ops = {
>   				   ARM_SMMU_PTE_CONT_SIZE |
>   				   PAGE_SIZE),
>   };
> +#endif
>   
>   static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
>   {
> @@ -1903,7 +2162,11 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
>   	/* Mark all SMRn as invalid and all S2CRn as bypass */
>   	for (i = 0; i < smmu->num_mapping_groups; ++i) {
>   		writel_relaxed(0, gr0_base + ARM_SMMU_GR0_SMR(i));
> -		writel_relaxed(S2CR_TYPE_BYPASS,
> +		/*
> +		 * Xen: Unlike Linux, any access to a non-configure stream
> +		 * will fault by default.
> +		 */
> +		writel_relaxed(S2CR_TYPE_FAULT,
>   			gr0_base + ARM_SMMU_GR0_S2CR(i));
>   	}
>   
> @@ -1929,6 +2192,8 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
>   
>   	/* Enable client access, but bypass when no mapping is found */
>   	reg &= ~(sCR0_CLIENTPD | sCR0_USFCFG);
> +	/* Xen: Unlike Linux, generate a fault when no mapping is found */
> +	reg |= sCR0_USFCFG;
>   
>   	/* Disable forced broadcasting */
>   	reg &= ~sCR0_FB;
> @@ -2039,7 +2304,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
>   		smmu->smr_id_mask = sid;
>   
>   		dev_notice(smmu->dev,
> -			   "\tstream matching with %u register groups, mask 0x%x",
> +			   "\tstream matching with %u register groups, mask 0x%x\n",
>   			   smmu->num_mapping_groups, mask);
>   	} else {
>   		smmu->num_mapping_groups = (id >> ID0_NUMSIDB_SHIFT) &
> @@ -2074,12 +2339,30 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
>   	size = arm_smmu_id_size_to_bits((id >> ID2_IAS_SHIFT) & ID2_IAS_MASK);
>   	smmu->s1_output_size = min_t(unsigned long, PHYS_MASK_SHIFT, size);
>   
> +	/* Xen: Stage-2 input size is not restricted */
> +	smmu->s2_input_size = size;
> +#if 0
>   	/* Stage-2 input size limited due to pgd allocation (PTRS_PER_PGD) */
>   #ifdef CONFIG_64BIT
>   	smmu->s2_input_size = min_t(unsigned long, VA_BITS, size);
>   #else
>   	smmu->s2_input_size = min(32UL, size);
>   #endif
> +#endif
> +
> +	/*
> +	 * Xen: SMMU v1: Only 40 bits input address size is supported for
> + 	 * arm32. See arm_smmu_init_context_bank
> +	 */
> +#ifdef CONFIG_ARM_32
> +	if ( smmu->version == ARM_SMMU_V1 && smmu->s2_input_size != 40 )
> +	{
> +		dev_err(smmu->dev,
> +			"Stage-2 Input size %ld not supported for SMMUv1\n",
> +			smmu->s2_input_size);
> +		return -ENODEV;;
> +	}
> +#endif
>   
>   	/* The stage-2 output mask is also applied for bypass */
>   	size = arm_smmu_id_size_to_bits((id >> ID2_OAS_SHIFT) & ID2_OAS_MASK);
> @@ -2124,8 +2407,11 @@ static const struct of_device_id arm_smmu_of_match[] = {
>   	{ .compatible = "arm,mmu-500", .data = (void *)ARM_SMMU_V2 },
>   	{ },
>   };
> -MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
>   
> +/*
> + * Xen: We don't have refcount allocated memory so manually free memory when
> + * an error occured.
> + */
>   static int arm_smmu_device_dt_probe(struct platform_device *pdev)
>   {
>   	const struct of_device_id *of_id;
> @@ -2149,14 +2435,17 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
>   
>   	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>   	smmu->base = devm_ioremap_resource(dev, res);
> -	if (IS_ERR(smmu->base))
> -		return PTR_ERR(smmu->base);
> +	if (IS_ERR(smmu->base)) {
> +		err = PTR_ERR(smmu->base);
> +		goto out_free;
> +	}
>   	smmu->size = resource_size(res);
>   
>   	if (of_property_read_u32(dev->of_node, "#global-interrupts",
>   				 &smmu->num_global_irqs)) {
>   		dev_err(dev, "missing #global-interrupts property\n");
> -		return -ENODEV;
> +		err = -ENODEV;
> +		goto out_free;
>   	}
>   
>   	num_irqs = 0;
> @@ -2169,14 +2458,16 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
>   	if (!smmu->num_context_irqs) {
>   		dev_err(dev, "found %d interrupts but expected at least %d\n",
>   			num_irqs, smmu->num_global_irqs + 1);
> -		return -ENODEV;
> +		err = -ENODEV;
> +		goto out_free;
>   	}
>   
>   	smmu->irqs = devm_kzalloc(dev, sizeof(*smmu->irqs) * num_irqs,
>   				  GFP_KERNEL);
>   	if (!smmu->irqs) {
>   		dev_err(dev, "failed to allocate %d irqs\n", num_irqs);
> -		return -ENOMEM;
> +		err = -ENOMEM;
> +		goto out_free;
>   	}
>   
>   	for (i = 0; i < num_irqs; ++i) {
> @@ -2184,7 +2475,8 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
>   
>   		if (irq < 0) {
>   			dev_err(dev, "failed to get irq index %d\n", i);
> -			return -ENODEV;
> +			err = -ENODEV;
> +			goto out_free;
>   		}
>   		smmu->irqs[i] = irq;
>   	}
> @@ -2259,12 +2551,20 @@ out_put_masters:
>   	for (node = rb_first(&smmu->masters); node; node = rb_next(node)) {
>   		struct arm_smmu_master *master
>   			= container_of(node, struct arm_smmu_master, node);
> -		of_node_put(master->of_node);
> +		kfree(master);
>   	}
>   
> +out_free:
> +	kfree(smmu->irqs);
> +	if (!IS_ERR(smmu->base))
> +		iounmap(smmu->base);
> +	kfree(smmu);
> +
>   	return err;
>   }
>   
> +/* Xen: We never remove SMMU */
> +#if 0
>   static int arm_smmu_device_remove(struct platform_device *pdev)
>   {
>   	int i;
> @@ -2359,3 +2659,237 @@ module_exit(arm_smmu_exit);
>   MODULE_DESCRIPTION("IOMMU API for ARM architected SMMU implementations");
>   MODULE_AUTHOR("Will Deacon <will.deacon@arm.com>");
>   MODULE_LICENSE("GPL v2");
> +#endif
> +
> +/* Xen specific function */
> +
> +static void arm_smmu_iotlb_flush_all(struct domain *d)
> +{
> +	struct arm_smmu_xen_domain *smmu_domain = domain_hvm_iommu(d)->arch.priv;
> +	struct iommu_domain *cfg;
> +
> +	spin_lock(&smmu_domain->lock);
> +	list_for_each_entry(cfg, &smmu_domain->contexts, list) {
> +		/*
> +		 * Only invalidate the context when SMMU is present.
> +		 * This is because the context initialization is delayed
> +		 * until a master has been added.
> +		 */
> +		if (unlikely(!ACCESS_ONCE(cfg->priv->smmu)))
> +			continue;
> +		arm_smmu_tlb_inv_context(cfg->priv);
> +	}
> +	spin_unlock(&smmu_domain->lock);
> +}
> +
> +static void arm_smmu_iotlb_flush(struct domain *d, unsigned long gfn,
> +                                 unsigned int page_count)
> +{
> +    /* ARM SMMU v1 doesn't have flush by VMA and VMID */
> +    arm_smmu_iotlb_flush_all(d);
> +}
> +
> +static int arm_smmu_assign_dev(struct domain *d, u8 devfn,
> +			       struct device *dev)
> +{
> +	struct iommu_domain *domain;
> +	struct arm_smmu_xen_domain *xen_domain;
> +	int ret;
> +
> +	xen_domain = domain_hvm_iommu(d)->arch.priv;
> +
> +	if (!dev->archdata.iommu) {
> +		dev->archdata.iommu = xzalloc(struct arm_smmu_xen_device);
> +		if (!dev->archdata.iommu)
> +			return -ENOMEM;
> +	}
> +
> +	if (!dev_iommu_group(dev)) {
> +		ret = arm_smmu_add_device(dev);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	/*
> +	 * TODO: Share the context bank (i.e iommu_domain) when the device is
> +	 * under the same SMMU as another device assigned to this domain.
> +	 * Would it useful for PCI
> +	 */
> +	domain = xzalloc(struct iommu_domain);
> +	if (!domain)
> +		return -ENOMEM;
> +
> +	ret = arm_smmu_domain_init(domain);
> +	if (ret)
> +		goto err_dom_init;
> +
> +	domain->priv->cfg.domain = d;
> +
> +	ret = arm_smmu_attach_dev(domain, dev);
> +	if (ret)
> +		goto err_attach_dev;
> +
> +	spin_lock(&xen_domain->lock);
> +	/* Chain the new context to the domain */
> +	list_add(&domain->list, &xen_domain->contexts);
> +	spin_unlock(&xen_domain->lock);
> +
> +	return 0;
> +
> +err_attach_dev:
> +	arm_smmu_domain_destroy(domain);
> +err_dom_init:
> +	xfree(domain);
> +
> +	return ret;
> +}
> +
> +static int arm_smmu_deassign_dev(struct domain *d, struct device *dev)
> +{
> +	struct iommu_domain *domain = dev_iommu_domain(dev);
> +	struct arm_smmu_xen_domain *xen_domain;
> +
> +	xen_domain = domain_hvm_iommu(d)->arch.priv;
> +
> +	if (!domain || domain->priv->cfg.domain != d) {
> +		dev_err(dev, " not attached to domain %d\n", d->domain_id);
> +		return -ESRCH;
> +	}
> +
> +	arm_smmu_detach_dev(domain, dev);
> +
> +	spin_lock(&xen_domain->lock);
> +	list_del(&domain->list);
> +	spin_unlock(&xen_domain->lock);
> +
> +	arm_smmu_domain_destroy(domain);
> +	xfree(domain);
> +
> +	return 0;
> +}
> +
> +static int arm_smmu_reassign_dev(struct domain *s, struct domain *t,
> +				 u8 devfn,  struct device *dev)
> +{
> +	int ret = 0;
> +
> +	/* Don't allow remapping on other domain than hwdom */
> +	if (t != hardware_domain)
> +		return -EPERM;
> +
> +	if (t == s)
> +		return 0;
> +
> +	ret = arm_smmu_deassign_dev(s, dev);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int arm_smmu_iommu_domain_init(struct domain *d)
> +{
> +	struct arm_smmu_xen_domain *xen_domain;
> +
> +	xen_domain = xzalloc(struct arm_smmu_xen_domain);
> +	if ( !xen_domain )
> +		return -ENOMEM;
> +
> +	spin_lock_init(&xen_domain->lock);
> +	INIT_LIST_HEAD(&xen_domain->contexts);
> +
> +	domain_hvm_iommu(d)->arch.priv = xen_domain;
> +
> +	return 0;
> +}
> +
> +static void __hwdom_init arm_smmu_iommu_hwdom_init(struct domain *d)
> +{
> +}
> +
> +static void arm_smmu_iommu_domain_teardown(struct domain *d)
> +{
> +	struct arm_smmu_xen_domain *xen_domain = domain_hvm_iommu(d)->arch.priv;
> +
> +	ASSERT(list_empty(&xen_domain->contexts));
> +	xfree(xen_domain);
> +}
> +
> +
> +static int arm_smmu_map_page(struct domain *d, unsigned long gfn,
> +			     unsigned long mfn, unsigned int flags)
> +{
> +	p2m_type_t t;
> +
> +	/*
> +	 * Grant mappings can be used for DMA requests. The dev_bus_addr
> +	 * returned by the hypercall is the MFN (not the IPA). For device
> +	 * protected by an IOMMU, Xen needs to add a 1:1 mapping in the domain
> +	 * p2m to allow DMA request to work.
> +	 * This is only valid when the domain is directed mapped. Hence this
> +	 * function should only be used by gnttab code with gfn == mfn.
> +	 */
> +	BUG_ON(!is_domain_direct_mapped(d));
> +	BUG_ON(mfn != gfn);
> +
> +	/* We only support readable and writable flags */
> +	if (!(flags & (IOMMUF_readable | IOMMUF_writable)))
> +		return -EINVAL;
> +
> +	t = (flags & IOMMUF_writable) ? p2m_iommu_map_rw : p2m_iommu_map_ro;
> +
> +	/*
> +	 * The function guest_physmap_add_entry replaces the current mapping
> +	 * if there is already one...
> +	 */
> +	return guest_physmap_add_entry(d, gfn, mfn, 0, t);
> +}
> +
> +static int arm_smmu_unmap_page(struct domain *d, unsigned long gfn)
> +{
> +	/*
> +	 * This function should only be used by gnttab code when the domain
> +	 * is direct mapped
> +	 */
> +	if ( !is_domain_direct_mapped(d) )
> +		return -EINVAL;
> +
> +	guest_physmap_remove_page(d, gfn, gfn, 0);
> +
> +	return 0;
> +}
> +
> +static const struct iommu_ops arm_smmu_iommu_ops = {
> +    .init = arm_smmu_iommu_domain_init,
> +    .hwdom_init = arm_smmu_iommu_hwdom_init,
> +    .teardown = arm_smmu_iommu_domain_teardown,
> +    .iotlb_flush = arm_smmu_iotlb_flush,
> +    .iotlb_flush_all = arm_smmu_iotlb_flush_all,
> +    .assign_device = arm_smmu_assign_dev,
> +    .reassign_device = arm_smmu_reassign_dev,
> +    .map_page = arm_smmu_map_page,
> +    .unmap_page = arm_smmu_unmap_page,
> +};
> +
> +static __init int arm_smmu_dt_init(struct dt_device_node *dev,
> +				   const void *data)
> +{
> +	int rc;
> +
> +	/*
> +	 * Even if the device can't be initialized, we don't want to
> +	 * give the SMMU device to dom0.
> +	 */
> +	dt_device_set_used_by(dev, DOMID_XEN);
> +
> +	rc = arm_smmu_device_dt_probe(dev);
> +	if ( !rc )
> +		iommu_set_ops(&arm_smmu_iommu_ops);
> +
> +	return rc;
> +}
> +
> +DT_DEVICE_START(smmu, "ARM SMMU", DEVICE_IOMMU)
> +	.dt_match = arm_smmu_of_match,
> +	.init = arm_smmu_dt_init,
> +DT_DEVICE_END

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

* Re: [PATCH for 4.6 13/13] xen/iommu: smmu: Add Xen specific code to be able to use the driver
  2015-02-18  1:02   ` Manish
@ 2015-02-18 11:47     ` Julien Grall
  2015-02-18 11:54       ` Julien Grall
  0 siblings, 1 reply; 41+ messages in thread
From: Julien Grall @ 2015-02-18 11:47 UTC (permalink / raw)
  To: Manish, xen-devel
  Cc: tim, manish.jaggi, will.daecon, ian.campbell, stefano.stabellini

Hi Manish,

On 18/02/2015 01:02, Manish wrote:
>
> On 17/12/14 1:38 am, Julien Grall wrote:
>> The main goal is to modify as little the Linux code to be able to port
>> easily new feature added in Linux repo for the driver.
>>
>> To achieve that we:
>>      - Add helpers to Linux function not implemented on Xen
>>      - Add callbacks used by Xen to do our own stuff and call Linux ones
>>      - Only modify when required the code which comes from Linux. If so a
>>      comment has been added with /* Xen: ... */ explaining why it's
>>      necessary.
>>
>> The support for PCI has been commented because it's not yet supported by
>> Xen ARM and therefore won't compile.
>>
>> Signed-off-by: Julien Grall <julien.grall@linaro.org>
>> ---
>>   xen/drivers/passthrough/arm/Makefile |   1 +
>>   xen/drivers/passthrough/arm/smmu.c   | 668
>> +++++++++++++++++++++++++++++++----
>>   2 files changed, 602 insertions(+), 67 deletions(-)
>>
>> diff --git a/xen/drivers/passthrough/arm/Makefile
>> b/xen/drivers/passthrough/arm/Makefile
>> index 0484b79..f4cd26e 100644
>> --- a/xen/drivers/passthrough/arm/Makefile
>> +++ b/xen/drivers/passthrough/arm/Makefile
>> @@ -1 +1,2 @@
>>   obj-y += iommu.o
>> +obj-y += smmu.o
>> diff --git a/xen/drivers/passthrough/arm/smmu.c
>> b/xen/drivers/passthrough/arm/smmu.c
>> index 8a6514f..3cf1773 100644
>> --- a/xen/drivers/passthrough/arm/smmu.c
>> +++ b/xen/drivers/passthrough/arm/smmu.c
>> @@ -18,6 +18,13 @@
>>    *
>>    * Author: Will Deacon <will.deacon@arm.com>
>>    *
>> + * Based on Linux drivers/iommu/arm-smmu.c
>> + *    => commit e6b5be2be4e30037eb551e0ed09dd97bd00d85d3
>> + *
>> + * Xen modification:
>> + * Julien Grall <julien.grall@linaro.org>
>> + * Copyright (C) 2014 Linaro Limited.
>> + *
>>    * This driver currently supports:
>>    *    - SMMUv1 and v2 implementations
>>    *    - Stream-matching and stream-indexing
>> @@ -28,26 +35,154 @@
>>    *    - Context fault reporting
>>    */
>
> <<<snip >>>
>
>> +/* Xen: Dummy iommu_domain */
>> +struct iommu_domain
>> +{
>> +    struct arm_smmu_domain        *priv;
>> +
>> +    /* Used to link domain contexts for a same domain */
>> +    struct list_head        list;
>> +};
>> +
>> +/* Xen: Describes informations required for a Xen domain */
>> +struct arm_smmu_xen_domain {
>> +    spinlock_t            lock;
>> +    /* List of context (i.e iommu_domain) associated to this domain */
>> +    struct list_head        contexts;
>> +};
>> +
>> +/* Xen: Information about each device stored in dev->archdata.iommu */
>> +struct arm_smmu_xen_device {
>> +    struct iommu_domain *domain;
>> +    struct iommu_group *group;
>> +};
>> +
>> +#define dev_archdata(dev) ((struct arm_smmu_xen_device
>> *)dev->archdata.iommu)
>> +#define dev_iommu_domain(dev) (dev_archdata(dev)->domain)
>> +#define dev_iommu_group(dev) (dev_archdata(dev)->group)
>> +
>> +/* Xen: Dummy iommu_group */
>> +struct iommu_group
>> +{
>> +    struct arm_smmu_master_cfg *cfg;
>> +
>> +    atomic_t ref;
>> +};
>> +
> The naming needs to be revisited in this patch. Original driver from
> Will has arm_smmu_domain. This patch adds  iommu_domain,
> arm_smmu_xen_domain, iommu_group.

I can't change the naming of the structure. iommu_domain and iommu_group 
are from Linux. As we don't have it on Xen, I have to add dummy 
structure for it.

> Could you please add some description about the relation and hierarchy
> of these data structures.

Good point, I will try to add more comment and explain why we have to do it.

Regards,

-- 
Julien Grall

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

* Re: [PATCH for 4.6 13/13] xen/iommu: smmu: Add Xen specific code to be able to use the driver
  2015-02-18 11:47     ` Julien Grall
@ 2015-02-18 11:54       ` Julien Grall
  2015-02-18 17:30         ` Jaggi, Manish
  0 siblings, 1 reply; 41+ messages in thread
From: Julien Grall @ 2015-02-18 11:54 UTC (permalink / raw)
  To: Manish, xen-devel@lists.xenproject.org >> xen-devel
  Cc: stefano.stabellini, manish.jaggi, tim, ian.campbell

BTW, I have sent few versions of this series since then. Please comment 
on the latest series as the code may have change.

Nonetheless, you are comment is still valid for the v3 :).

Regards,

On 18/02/2015 11:47, Julien Grall wrote:
> Hi Manish,
>
> On 18/02/2015 01:02, Manish wrote:
>>
>> On 17/12/14 1:38 am, Julien Grall wrote:
>>> The main goal is to modify as little the Linux code to be able to port
>>> easily new feature added in Linux repo for the driver.
>>>
>>> To achieve that we:
>>>      - Add helpers to Linux function not implemented on Xen
>>>      - Add callbacks used by Xen to do our own stuff and call Linux ones
>>>      - Only modify when required the code which comes from Linux. If
>>> so a
>>>      comment has been added with /* Xen: ... */ explaining why it's
>>>      necessary.
>>>
>>> The support for PCI has been commented because it's not yet supported by
>>> Xen ARM and therefore won't compile.
>>>
>>> Signed-off-by: Julien Grall <julien.grall@linaro.org>
>>> ---
>>>   xen/drivers/passthrough/arm/Makefile |   1 +
>>>   xen/drivers/passthrough/arm/smmu.c   | 668
>>> +++++++++++++++++++++++++++++++----
>>>   2 files changed, 602 insertions(+), 67 deletions(-)
>>>
>>> diff --git a/xen/drivers/passthrough/arm/Makefile
>>> b/xen/drivers/passthrough/arm/Makefile
>>> index 0484b79..f4cd26e 100644
>>> --- a/xen/drivers/passthrough/arm/Makefile
>>> +++ b/xen/drivers/passthrough/arm/Makefile
>>> @@ -1 +1,2 @@
>>>   obj-y += iommu.o
>>> +obj-y += smmu.o
>>> diff --git a/xen/drivers/passthrough/arm/smmu.c
>>> b/xen/drivers/passthrough/arm/smmu.c
>>> index 8a6514f..3cf1773 100644
>>> --- a/xen/drivers/passthrough/arm/smmu.c
>>> +++ b/xen/drivers/passthrough/arm/smmu.c
>>> @@ -18,6 +18,13 @@
>>>    *
>>>    * Author: Will Deacon <will.deacon@arm.com>
>>>    *
>>> + * Based on Linux drivers/iommu/arm-smmu.c
>>> + *    => commit e6b5be2be4e30037eb551e0ed09dd97bd00d85d3
>>> + *
>>> + * Xen modification:
>>> + * Julien Grall <julien.grall@linaro.org>
>>> + * Copyright (C) 2014 Linaro Limited.
>>> + *
>>>    * This driver currently supports:
>>>    *    - SMMUv1 and v2 implementations
>>>    *    - Stream-matching and stream-indexing
>>> @@ -28,26 +35,154 @@
>>>    *    - Context fault reporting
>>>    */
>>
>> <<<snip >>>
>>
>>> +/* Xen: Dummy iommu_domain */
>>> +struct iommu_domain
>>> +{
>>> +    struct arm_smmu_domain        *priv;
>>> +
>>> +    /* Used to link domain contexts for a same domain */
>>> +    struct list_head        list;
>>> +};
>>> +
>>> +/* Xen: Describes informations required for a Xen domain */
>>> +struct arm_smmu_xen_domain {
>>> +    spinlock_t            lock;
>>> +    /* List of context (i.e iommu_domain) associated to this domain */
>>> +    struct list_head        contexts;
>>> +};
>>> +
>>> +/* Xen: Information about each device stored in dev->archdata.iommu */
>>> +struct arm_smmu_xen_device {
>>> +    struct iommu_domain *domain;
>>> +    struct iommu_group *group;
>>> +};
>>> +
>>> +#define dev_archdata(dev) ((struct arm_smmu_xen_device
>>> *)dev->archdata.iommu)
>>> +#define dev_iommu_domain(dev) (dev_archdata(dev)->domain)
>>> +#define dev_iommu_group(dev) (dev_archdata(dev)->group)
>>> +
>>> +/* Xen: Dummy iommu_group */
>>> +struct iommu_group
>>> +{
>>> +    struct arm_smmu_master_cfg *cfg;
>>> +
>>> +    atomic_t ref;
>>> +};
>>> +
>> The naming needs to be revisited in this patch. Original driver from
>> Will has arm_smmu_domain. This patch adds  iommu_domain,
>> arm_smmu_xen_domain, iommu_group.
>
> I can't change the naming of the structure. iommu_domain and iommu_group
> are from Linux. As we don't have it on Xen, I have to add dummy
> structure for it.
>
>> Could you please add some description about the relation and hierarchy
>> of these data structures.
>
> Good point, I will try to add more comment and explain why we have to do
> it.
>
> Regards,
>

-- 
Julien Grall

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

* Re: [PATCH for 4.6 13/13] xen/iommu: smmu: Add Xen specific code to be able to use the driver
  2015-02-18 11:54       ` Julien Grall
@ 2015-02-18 17:30         ` Jaggi, Manish
  2015-02-18 18:22           ` Julien Grall
  0 siblings, 1 reply; 41+ messages in thread
From: Jaggi, Manish @ 2015-02-18 17:30 UTC (permalink / raw)
  To: Julien Grall, xen-devel; +Cc: stefano.stabellini, tim, ian.campbell

_______________________________________
From: Julien Grall <julien.grall@linaro.org>
Sent: Wednesday, February 18, 2015 5:24 PM
To: Jaggi, Manish; xen-devel@lists.xenproject.org >> xen-devel
Cc: ian.campbell@citrix.com; tim@xen.org; stefano.stabellini@citrix.com; Jaggi, Manish
Subject: Re: [PATCH for 4.6 13/13] xen/iommu: smmu: Add Xen specific code to be able to use the driver

BTW, I have sent few versions of this series since then. Please comment
on the latest series as the code may have change.

[manish] Somehow I could not find your recent revision, but I am looking at your den-unstable tree assuming it has your latest version.

Nonetheless, you are comment is still valid for the v3 :).

[manish] There are general comments on the data structures
(a) I don't see a use case where for same domain (VM) there would be different context banks , so linked list may not be required. 
(b) Also iommu group may not be relevant for the same reason.
I am curious to find the use cases.


Regards,

On 18/02/2015 11:47, Julien Grall wrote:
> Hi Manish,
>
> On 18/02/2015 01:02, Manish wrote:
>>
>> On 17/12/14 1:38 am, Julien Grall wrote:
>>> The main goal is to modify as little the Linux code to be able to port
>>> easily new feature added in Linux repo for the driver.
>>>
>>> To achieve that we:
>>>      - Add helpers to Linux function not implemented on Xen
>>>      - Add callbacks used by Xen to do our own stuff and call Linux ones
>>>      - Only modify when required the code which comes from Linux. If
>>> so a
>>>      comment has been added with /* Xen: ... */ explaining why it's
>>>      necessary.
>>>
>>> The support for PCI has been commented because it's not yet supported by
>>> Xen ARM and therefore won't compile.
>>>
>>> Signed-off-by: Julien Grall <julien.grall@linaro.org>
>>> ---
>>>   xen/drivers/passthrough/arm/Makefile |   1 +
>>>   xen/drivers/passthrough/arm/smmu.c   | 668
>>> +++++++++++++++++++++++++++++++----
>>>   2 files changed, 602 insertions(+), 67 deletions(-)
>>>
>>> diff --git a/xen/drivers/passthrough/arm/Makefile
>>> b/xen/drivers/passthrough/arm/Makefile
>>> index 0484b79..f4cd26e 100644
>>> --- a/xen/drivers/passthrough/arm/Makefile
>>> +++ b/xen/drivers/passthrough/arm/Makefile
>>> @@ -1 +1,2 @@
>>>   obj-y += iommu.o
>>> +obj-y += smmu.o
>>> diff --git a/xen/drivers/passthrough/arm/smmu.c
>>> b/xen/drivers/passthrough/arm/smmu.c
>>> index 8a6514f..3cf1773 100644
>>> --- a/xen/drivers/passthrough/arm/smmu.c
>>> +++ b/xen/drivers/passthrough/arm/smmu.c
>>> @@ -18,6 +18,13 @@
>>>    *
>>>    * Author: Will Deacon <will.deacon@arm.com>
>>>    *
>>> + * Based on Linux drivers/iommu/arm-smmu.c
>>> + *    => commit e6b5be2be4e30037eb551e0ed09dd97bd00d85d3
>>> + *
>>> + * Xen modification:
>>> + * Julien Grall <julien.grall@linaro.org>
>>> + * Copyright (C) 2014 Linaro Limited.
>>> + *
>>>    * This driver currently supports:
>>>    *    - SMMUv1 and v2 implementations
>>>    *    - Stream-matching and stream-indexing
>>> @@ -28,26 +35,154 @@
>>>    *    - Context fault reporting
>>>    */
>>
>> <<<snip >>>
>>
>>> +/* Xen: Dummy iommu_domain */
>>> +struct iommu_domain
>>> +{
>>> +    struct arm_smmu_domain        *priv;
>>> +
>>> +    /* Used to link domain contexts for a same domain */
>>> +    struct list_head        list;
>>> +};
>>> +
>>> +/* Xen: Describes informations required for a Xen domain */
>>> +struct arm_smmu_xen_domain {
>>> +    spinlock_t            lock;
>>> +    /* List of context (i.e iommu_domain) associated to this domain */
>>> +    struct list_head        contexts;
>>> +};
>>> +
>>> +/* Xen: Information about each device stored in dev->archdata.iommu */
>>> +struct arm_smmu_xen_device {
>>> +    struct iommu_domain *domain;
>>> +    struct iommu_group *group;
>>> +};
>>> +
>>> +#define dev_archdata(dev) ((struct arm_smmu_xen_device
>>> *)dev->archdata.iommu)
>>> +#define dev_iommu_domain(dev) (dev_archdata(dev)->domain)
>>> +#define dev_iommu_group(dev) (dev_archdata(dev)->group)
>>> +
>>> +/* Xen: Dummy iommu_group */
>>> +struct iommu_group
>>> +{
>>> +    struct arm_smmu_master_cfg *cfg;
>>> +
>>> +    atomic_t ref;
>>> +};
>>> +
>> The naming needs to be revisited in this patch. Original driver from
>> Will has arm_smmu_domain. This patch adds  iommu_domain,
>> arm_smmu_xen_domain, iommu_group.
>
> I can't change the naming of the structure. iommu_domain and iommu_group
> are from Linux. As we don't have it on Xen, I have to add dummy
> structure for it.
>
>> Could you please add some description about the relation and hierarchy
>> of these data structures.
>
> Good point, I will try to add more comment and explain why we have to do
> it.
>
> Regards,
>

--
Julien Grall

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

* Re: [PATCH for 4.6 13/13] xen/iommu: smmu: Add Xen specific code to be able to use the driver
  2015-02-18 17:30         ` Jaggi, Manish
@ 2015-02-18 18:22           ` Julien Grall
  2015-02-19  2:55             ` Manish
  0 siblings, 1 reply; 41+ messages in thread
From: Julien Grall @ 2015-02-18 18:22 UTC (permalink / raw)
  To: Jaggi, Manish, xen-devel; +Cc: stefano.stabellini, tim, ian.campbell



On 18/02/2015 17:30, Jaggi, Manish wrote:
> [manish] There are general comments on the data structures
> (a) I don't see a use case where for same domain (VM) there would be different context banks , so linked list may not be required.

I guess you mean the list in arm_smmu_xen_domain? All the devices 
pass-through to a domain may not be protected by the same SMMU. 
Therefore the context banks are different.

Also, for now a context is allocated per-device. It should be rework to 
share the context between multiple device protected by the same SMMU and 
pass-through to the same domain.

> (b) Also iommu group may not be relevant for the same reason.
> I am curious to find the use cases.

The iommu_group is used to store the configuration of the device 
protected by an SMMU (i.e the stream ids associated to this domain).

I'm a bit surprised that you think they are not useful...

Regards,

-- 
Julien Grall

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

* Re: [PATCH for 4.6 13/13] xen/iommu: smmu: Add Xen specific code to be able to use the driver
  2015-02-18 18:22           ` Julien Grall
@ 2015-02-19  2:55             ` Manish
  2015-02-19  6:01               ` Julien Grall
  2015-02-19 10:34               ` Andrew Cooper
  0 siblings, 2 replies; 41+ messages in thread
From: Manish @ 2015-02-19  2:55 UTC (permalink / raw)
  To: Julien Grall, Jaggi, Manish, xen-devel
  Cc: stefano.stabellini, tim, ian.campbell


On 18/02/15 11:52 pm, Julien Grall wrote:
>
>
> On 18/02/2015 17:30, Jaggi, Manish wrote:
>> [manish] There are general comments on the data structures
>> (a) I don't see a use case where for same domain (VM) there would be 
>> different context banks , so linked list may not be required.
>
> I guess you mean the list in arm_smmu_xen_domain? All the devices 
> pass-through to a domain may not be protected by the same SMMU. 
> Therefore the context banks are different.
you are right. For each smmu the context bank instance for a xen domain 
is duplicated with just a change in context bank id. I was thinking can 
it be minimized.
>
> Also, for now a context is allocated per-device. It should be rework 
> to share the context between multiple device protected by the same 
> SMMU and pass-through to the same domain.
Yes, this is exactly I an doing at my end.
>
>> (b) Also iommu group may not be relevant for the same reason.
>> I am curious to find the use cases.
>
> The iommu_group is used to store the configuration of the device 
> protected by an SMMU (i.e the stream ids associated to this domain).
>
> I'm a bit surprised that you think they are not useful...
How do we create an iommu_group in xen ? AFAIK an iommu group is a vfio 
group in linux which is assigned to a a VM.
lkvm run -m 512 -k home/Image ...  --vfio-groups=48, 51

For Xen There are 2 ways of attaching devices using xl pci-attach or in 
domU cfg file. Should they create different iommu_groups ?

>
> Regards,
>

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

* Re: [PATCH for 4.6 13/13] xen/iommu: smmu: Add Xen specific code to be able to use the driver
  2015-02-19  2:55             ` Manish
@ 2015-02-19  6:01               ` Julien Grall
  2015-02-19  7:16                 ` Manish
  2015-02-19 10:34               ` Andrew Cooper
  1 sibling, 1 reply; 41+ messages in thread
From: Julien Grall @ 2015-02-19  6:01 UTC (permalink / raw)
  To: Manish, Jaggi, Manish, xen-devel; +Cc: stefano.stabellini, tim, ian.campbell



On 19/02/2015 02:55, Manish wrote:
>
> On 18/02/15 11:52 pm, Julien Grall wrote:
>>
>>
>> On 18/02/2015 17:30, Jaggi, Manish wrote:
>>> [manish] There are general comments on the data structures
>>> (a) I don't see a use case where for same domain (VM) there would be
>>> different context banks , so linked list may not be required.
>>
>> I guess you mean the list in arm_smmu_xen_domain? All the devices
>> pass-through to a domain may not be protected by the same SMMU.
>> Therefore the context banks are different.
> you are right. For each smmu the context bank instance for a xen domain
> is duplicated with just a change in context bank id. I was thinking can
> it be minimized.
>>
>> Also, for now a context is allocated per-device. It should be rework
>> to share the context between multiple device protected by the same
>> SMMU and pass-through to the same domain.
> Yes, this is exactly I an doing at my end.
>>
>>> (b) Also iommu group may not be relevant for the same reason.
>>> I am curious to find the use cases.
>>
>> The iommu_group is used to store the configuration of the device
>> protected by an SMMU (i.e the stream ids associated to this domain).
>>
>> I'm a bit surprised that you think they are not useful...
> How do we create an iommu_group in xen ? AFAIK an iommu group is a vfio
> group in linux which is assigned to a a VM.
> lkvm run -m 512 -k home/Image ...  --vfio-groups=48, 51

  > For Xen There are 2 ways of attaching devices using xl pci-attach or in
> domU cfg file. Should they create different iommu_groups ?

Why are you talking about VFIO? The iommu_group is created by the SMMU 
when a new device is added (see arm_smmu_device). At the time we are 
speaking, Linux and Xen use one iommu group per-device. No matters what 
VFIO does.

Even though we have two ways to attach PCI on the toolstack, both are 
using the same hypercall at the end.

Regards,

-- 
Julien Grall

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

* Re: [PATCH for 4.6 13/13] xen/iommu: smmu: Add Xen specific code to be able to use the driver
  2015-02-19  6:01               ` Julien Grall
@ 2015-02-19  7:16                 ` Manish
  0 siblings, 0 replies; 41+ messages in thread
From: Manish @ 2015-02-19  7:16 UTC (permalink / raw)
  To: Julien Grall
  Cc: stefano.stabellini, Jaggi, Manish, tim, ian.campbell, xen-devel


On 19/02/15 11:31 am, Julien Grall wrote:
>
>
> On 19/02/2015 02:55, Manish wrote:
>>
>> On 18/02/15 11:52 pm, Julien Grall wrote:
>>>
>>>
>>> On 18/02/2015 17:30, Jaggi, Manish wrote:
>>>> [manish] There are general comments on the data structures
>>>> (a) I don't see a use case where for same domain (VM) there would be
>>>> different context banks , so linked list may not be required.
>>>
>>> I guess you mean the list in arm_smmu_xen_domain? All the devices
>>> pass-through to a domain may not be protected by the same SMMU.
>>> Therefore the context banks are different.
>> you are right. For each smmu the context bank instance for a xen domain
>> is duplicated with just a change in context bank id. I was thinking can
>> it be minimized.
>>>
>>> Also, for now a context is allocated per-device. It should be rework
>>> to share the context between multiple device protected by the same
>>> SMMU and pass-through to the same domain.
>> Yes, this is exactly I an doing at my end.
>>>
>>>> (b) Also iommu group may not be relevant for the same reason.
>>>> I am curious to find the use cases.
>>>
>>> The iommu_group is used to store the configuration of the device
>>> protected by an SMMU (i.e the stream ids associated to this domain).
>>>
>>> I'm a bit surprised that you think they are not useful...
>> How do we create an iommu_group in xen ? AFAIK an iommu group is a vfio
>> group in linux which is assigned to a a VM.
>> lkvm run -m 512 -k home/Image ...  --vfio-groups=48, 51
>
>  > For Xen There are 2 ways of attaching devices using xl pci-attach 
> or in
>> domU cfg file. Should they create different iommu_groups ?
>
> Why are you talking about VFIO? The iommu_group is created by the SMMU 
> when a new device is added (see arm_smmu_device). At the time we are 
> speaking, Linux and Xen use one iommu group per-device. 
> No matters what VFIO does.
>
> Even though we have two ways to attach PCI on the toolstack, both are 
> using the same hypercall at the end.
>
> Regards,
>
I think we can have a discussion over it, parallely I will send RFC 
patches on your current state of xen-unstable smmu code.

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

* Re: [PATCH for 4.6 13/13] xen/iommu: smmu: Add Xen specific code to be able to use the driver
  2015-02-19  2:55             ` Manish
  2015-02-19  6:01               ` Julien Grall
@ 2015-02-19 10:34               ` Andrew Cooper
  1 sibling, 0 replies; 41+ messages in thread
From: Andrew Cooper @ 2015-02-19 10:34 UTC (permalink / raw)
  To: Manish, Julien Grall, Jaggi, Manish, xen-devel
  Cc: tim, stefano.stabellini, ian.campbell

On 19/02/15 02:55, Manish wrote:
>
> On 18/02/15 11:52 pm, Julien Grall wrote:
>>
>>
>> On 18/02/2015 17:30, Jaggi, Manish wrote:
>>> [manish] There are general comments on the data structures
>>> (a) I don't see a use case where for same domain (VM) there would be
>>> different context banks , so linked list may not be required.
>>
>> I guess you mean the list in arm_smmu_xen_domain? All the devices
>> pass-through to a domain may not be protected by the same SMMU.
>> Therefore the context banks are different.
> you are right. For each smmu the context bank instance for a xen
> domain is duplicated with just a change in context bank id. I was
> thinking can it be minimized.
>>
>> Also, for now a context is allocated per-device. It should be rework
>> to share the context between multiple device protected by the same
>> SMMU and pass-through to the same domain.
> Yes, this is exactly I an doing at my end.
>>
>>> (b) Also iommu group may not be relevant for the same reason.
>>> I am curious to find the use cases.
>>
>> The iommu_group is used to store the configuration of the device
>> protected by an SMMU (i.e the stream ids associated to this domain).
>>
>> I'm a bit surprised that you think they are not useful...
> How do we create an iommu_group in xen ?

Xen currently has no concept of iommu groups like KVM has, and relies on
the host administrator to know exactly what is and is not safe/possible
to achieve.  Xen really should gain the same concept, but this is a
substantial quantity of work.

~Andrew

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

end of thread, other threads:[~2015-02-19 10:35 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-12-16 20:08 [PATCH for 4.6 00/13] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
2014-12-16 20:08 ` [PATCH for 4.6 01/13] xen/arm: gic-v2: Change the device name in DT_DEVICE_START Julien Grall
2014-12-16 20:08 ` [PATCH for 4.6 02/13] xen/arm: vgic: Drop unecessary include asm/device.h Julien Grall
2014-12-16 20:08 ` [PATCH for 4.6 03/13] xen: Introduce ACCESS_ONCE macro Julien Grall
2014-12-17 10:05   ` Jan Beulich
2014-12-17 12:54     ` Julien Grall
2014-12-17 17:10       ` Jan Beulich
2014-12-17 17:52         ` Andrew Cooper
2014-12-18 15:58           ` Julien Grall
2014-12-18 15:58           ` Julien Grall
2015-01-15 13:39           ` Julien Grall
2014-12-16 20:08 ` [PATCH for 4.6 04/13] xen/dt: Extend dt_device_match to possibly store data Julien Grall
2014-12-16 20:08 ` [PATCH for 4.6 05/13] xen/arm: device: Rename device_type into device_match Julien Grall
2014-12-16 20:08 ` [PATCH for 4.6 06/13] xen/iommu: arm: Remove temporary the SMMU driver Julien Grall
2014-12-16 20:08 ` [PATCH for 4.6 07/13] xen: Introduce a generic way to describe device Julien Grall
2014-12-17 10:16   ` Jan Beulich
2014-12-17 10:30     ` Julien Grall
2014-12-17 10:46       ` Jan Beulich
2014-12-17 13:03         ` Julien Grall
2014-12-17 17:17           ` Jan Beulich
2014-12-18 15:56             ` Julien Grall
2014-12-18 16:02               ` Jan Beulich
2014-12-18 16:07                 ` Julien Grall
2014-12-18  1:12   ` Zhang, Yang Z
2014-12-18  8:52     ` Julien Grall
2014-12-16 20:08 ` [PATCH for 4.6 08/13] xen/iommu: Consolidate device assignment ops into a single set Julien Grall
2014-12-17 10:20   ` Jan Beulich
2014-12-16 20:08 ` [PATCH for 4.6 09/13] xen/arm: Describe device supported by a driver with dt_match_node Julien Grall
2014-12-16 20:08 ` [PATCH for 4.6 10/13] xen/iommu: arm: Import the SMMU driver from Linux Julien Grall
2014-12-16 20:08 ` [PATCH for 4.6 11/13] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices Julien Grall
2014-12-16 20:08 ` [PATCH for 4.6 12/13] xen/iommu: smmu: Introduce automatic stream-id-masking Julien Grall
2014-12-16 20:08 ` [PATCH for 4.6 13/13] xen/iommu: smmu: Add Xen specific code to be able to use the driver Julien Grall
2015-02-18  1:02   ` Manish
2015-02-18 11:47     ` Julien Grall
2015-02-18 11:54       ` Julien Grall
2015-02-18 17:30         ` Jaggi, Manish
2015-02-18 18:22           ` Julien Grall
2015-02-19  2:55             ` Manish
2015-02-19  6:01               ` Julien Grall
2015-02-19  7:16                 ` Manish
2015-02-19 10:34               ` Andrew Cooper

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.