All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/12] xen/arm: Resync the SMMU driver with the Linux one
@ 2015-01-16 14:23 Julien Grall
  2015-01-16 14:23 ` [PATCH v2 01/12] xen/arm: gic-v2: Change the device name in DT_DEVICE_START Julien Grall
                   ` (11 more replies)
  0 siblings, 12 replies; 51+ messages in thread
From: Julien Grall @ 2015-01-16 14:23 UTC (permalink / raw)
  To: xen-devel; +Cc: stefano.stabellini, 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 code of the SMMU drivers (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 made in several step
    1) Revert the current SMMU driver (patch #5)
    2) Import as it is the driver from Linux (patch #9)
    3) Apply 2 fixes which are useful to correctly use the SATA with SMMU on
    calxeda. I don't know why Linux didn't yet applied (patch #10-11)
    4) Chanes for Xen (patch #12)

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 platform device passthrough (
ops assing_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 platform device).

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

Major changes in v2:
    - Introduce the generic struct to describe device on ARM. Alias it
    to pci_dev on x86
    - Defer the introduction of PCI in the generic device later

Regards,

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 (10):
  xen/arm: gic-v2: Change the device name in DT_DEVICE_START
  xen/arm: vgic: Drop unecessary include asm/device.h
  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/arm: 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                 |   25 +-
 xen/arch/arm/gic-v2.c                 |   12 +-
 xen/arch/arm/gic-v3.c                 |    8 +-
 xen/arch/arm/platform.c               |    2 +-
 xen/arch/arm/vgic-v2.c                |    1 -
 xen/arch/arm/vgic-v3.c                |    1 -
 xen/common/device.c                   |   21 +
 xen/common/device_tree.c              |   15 +-
 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/drivers/passthrough/arm/smmu.c    | 4115 +++++++++++++++++++++------------
 xen/drivers/passthrough/device_tree.c |    5 +-
 xen/drivers/passthrough/pci.c         |   20 +-
 xen/include/asm-arm/device.h          |   38 +-
 xen/include/asm-arm/gic.h             |   15 +-
 xen/include/asm-x86/device.h          |   25 +
 xen/include/xen/device_tree.h         |   19 +-
 xen/include/xen/iommu.h               |   18 +-
 xen/include/xen/pci.h                 |    1 +
 21 files changed, 2781 insertions(+), 1596 deletions(-)
 create mode 100644 xen/common/device.c
 create mode 100644 xen/include/asm-x86/device.h

-- 
2.1.4

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

* [PATCH v2 01/12] xen/arm: gic-v2: Change the device name in DT_DEVICE_START
  2015-01-16 14:23 [PATCH v2 00/12] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
@ 2015-01-16 14:23 ` Julien Grall
  2015-01-27 15:52   ` Stefano Stabellini
  2015-01-16 14:23 ` [PATCH v2 02/12] xen/arm: vgic: Drop unecessary include asm/device.h Julien Grall
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 51+ messages in thread
From: Julien Grall @ 2015-01-16 14:23 UTC (permalink / raw)
  To: xen-devel; +Cc: stefano.stabellini, 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.4

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

* [PATCH v2 02/12] xen/arm: vgic: Drop unecessary include asm/device.h
  2015-01-16 14:23 [PATCH v2 00/12] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
  2015-01-16 14:23 ` [PATCH v2 01/12] xen/arm: gic-v2: Change the device name in DT_DEVICE_START Julien Grall
@ 2015-01-16 14:23 ` Julien Grall
  2015-01-27 15:53   ` Stefano Stabellini
  2015-01-16 14:23 ` [PATCH v2 03/12] xen/dt: Extend dt_device_match to possibly store data Julien Grall
                   ` (9 subsequent siblings)
  11 siblings, 1 reply; 51+ messages in thread
From: Julien Grall @ 2015-01-16 14:23 UTC (permalink / raw)
  To: xen-devel; +Cc: stefano.stabellini, 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 4dc2267..1ac46cc 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 c738ca9..757ffd9 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.4

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

* [PATCH v2 03/12] xen/dt: Extend dt_device_match to possibly store data
  2015-01-16 14:23 [PATCH v2 00/12] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
  2015-01-16 14:23 ` [PATCH v2 01/12] xen/arm: gic-v2: Change the device name in DT_DEVICE_START Julien Grall
  2015-01-16 14:23 ` [PATCH v2 02/12] xen/arm: vgic: Drop unecessary include asm/device.h Julien Grall
@ 2015-01-16 14:23 ` Julien Grall
  2015-01-27 15:57   ` Stefano Stabellini
  2015-01-16 14:23 ` [PATCH v2 04/12] xen/arm: device: Rename device_type into device_match Julien Grall
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 51+ messages in thread
From: Julien Grall @ 2015-01-16 14:23 UTC (permalink / raw)
  To: xen-devel; +Cc: stefano.stabellini, 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.4

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

* [PATCH v2 04/12] xen/arm: device: Rename device_type into device_match
  2015-01-16 14:23 [PATCH v2 00/12] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (2 preceding siblings ...)
  2015-01-16 14:23 ` [PATCH v2 03/12] xen/dt: Extend dt_device_match to possibly store data Julien Grall
@ 2015-01-16 14:23 ` Julien Grall
  2015-01-27 16:05   ` Stefano Stabellini
  2015-01-16 14:24 ` [PATCH v2 05/12] xen/iommu: arm: Remove temporary the SMMU driver Julien Grall
                   ` (7 subsequent siblings)
  11 siblings, 1 reply; 51+ messages in thread
From: Julien Grall @ 2015-01-16 14:23 UTC (permalink / raw)
  To: xen-devel; +Cc: stefano.stabellini, 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.4

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

* [PATCH v2 05/12] xen/iommu: arm: Remove temporary the SMMU driver
  2015-01-16 14:23 [PATCH v2 00/12] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (3 preceding siblings ...)
  2015-01-16 14:23 ` [PATCH v2 04/12] xen/arm: device: Rename device_type into device_match Julien Grall
@ 2015-01-16 14:24 ` Julien Grall
  2015-01-27 16:05   ` Stefano Stabellini
  2015-01-16 14:24 ` [PATCH v2 06/12] xen/arm: Introduce a generic way to describe device Julien Grall
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 51+ messages in thread
From: Julien Grall @ 2015-01-16 14:24 UTC (permalink / raw)
  To: xen-devel; +Cc: stefano.stabellini, 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.4

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

* [PATCH v2 06/12] xen/arm: Introduce a generic way to describe device
  2015-01-16 14:23 [PATCH v2 00/12] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (4 preceding siblings ...)
  2015-01-16 14:24 ` [PATCH v2 05/12] xen/iommu: arm: Remove temporary the SMMU driver Julien Grall
@ 2015-01-16 14:24 ` Julien Grall
  2015-01-16 14:59   ` Jan Beulich
  2015-01-27 16:13   ` Stefano Stabellini
  2015-01-16 14:24 ` [PATCH v2 07/12] xen/iommu: Consolidate device assignment ops into a single set Julien Grall
                   ` (5 subsequent siblings)
  11 siblings, 2 replies; 51+ messages in thread
From: Julien Grall @ 2015-01-16 14:24 UTC (permalink / raw)
  To: xen-devel
  Cc: Keir Fraser, ian.campbell, Andrew Cooper, Julien Grall, tim,
	stefano.stabellini, Jan Beulich

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

While Xen only supports Platform device on ARM, Xen will gain support of
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.

As x86 only supports PCI, introduce a new type device_t which will be an
alias to pci_dev for this architecture. It will avoid to add a new field
for this place.

Signed-off-by: Julien Grall <julien.grall@linaro.org>
CC: Jan Beulich <jbeulich@suse.com>
CC: Keir Fraser <keir@xen.org>
CC: Andrew Cooper <andrew.cooper3@citrix.com>

---
    Changes in v2:
        - As x86 will only support PCI, only introduce the generic
        device on ARM
        - Introduce a typedef device_t to be used in common code
        - Drop the PCI code for ARM for now. It will be reintroduced
        when PCI support will be added
        - s#asm/device.h#xen/device.h# is not anymore needed
---
 xen/common/device.c           | 21 +++++++++++++++++++++
 xen/common/device_tree.c      |  3 +++
 xen/include/asm-arm/device.h  | 26 ++++++++++++++++++++++++++
 xen/include/asm-x86/device.h  | 25 +++++++++++++++++++++++++
 xen/include/xen/device_tree.h | 13 +++++++++++++
 xen/include/xen/iommu.h       |  1 +
 xen/include/xen/pci.h         |  1 +
 7 files changed, 90 insertions(+)
 create mode 100644 xen/common/device.c
 create mode 100644 xen/include/asm-x86/device.h

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..d1c716f 100644
--- a/xen/common/device_tree.c
+++ b/xen/common/device_tree.c
@@ -1454,6 +1454,9 @@ static unsigned long __init unflatten_dt_node(const void *fdt,
             ((char *)pp->value)[sz - 1] = 0;
             dt_dprintk("fixed up name for %s -> %s\n", pathp,
                        (char *)pp->value);
+            /* Generic device initialization */
+            np->dev.type = DEV_DT;
+            np->dev.of_node = np;
         }
     }
     if ( allnextpp )
diff --git a/xen/include/asm-arm/device.h b/xen/include/asm-arm/device.h
index 72a9028..2fe4280 100644
--- a/xen/include/asm-arm/device.h
+++ b/xen/include/asm-arm/device.h
@@ -2,8 +2,34 @@
 #define __ASM_ARM_DEVICE_H
 
 #include <xen/init.h>
+
+enum device_type
+{
+    DEV_DT,
+};
+
+struct dev_archdata {
+    void *iommu;    /* IOMMU private data */
+};
+
+/* 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;
+};
+
+typedef struct device device_t;
+
 #include <xen/device_tree.h>
 
+/* TODO: Correctly implement dev_is_pci when PCI will be supported on ARM */
+#define dev_is_pci(dev) ((void)(dev), 0)
+#define dev_is_dt(dev)  ((dev->type == DEV_DT)
+
 enum device_match
 {
     DEVICE_SERIAL,
diff --git a/xen/include/asm-x86/device.h b/xen/include/asm-x86/device.h
new file mode 100644
index 0000000..a016112
--- /dev/null
+++ b/xen/include/asm-x86/device.h
@@ -0,0 +1,25 @@
+#ifndef __ASM_X86_DEVICE_H
+#define __ASM_X86_DEVICE_H
+
+#include <xen/pci.h>
+
+/*
+ * x86 is only supported PCI. Therefore it's possible to directly use
+ * pci_dev to avoid adding new field.
+ */
+
+typedef struct pci_dev device_t;
+
+#define dev_is_pci(dev) ((void)(dev), 1)
+#define pci_to_dev(pci) (pci)
+
+#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_tree.h b/xen/include/xen/device_tree.h
index 6502369..c8a0375 100644
--- a/xen/include/xen/device_tree.h
+++ b/xen/include/xen/device_tree.h
@@ -11,7 +11,9 @@
 #define __XEN_DEVICE_TREE_H__
 
 #include <asm/byteorder.h>
+#include <asm/device.h>
 #include <public/xen.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/iommu.h b/xen/include/xen/iommu.h
index 8eb764a..ecb2627 100644
--- a/xen/include/xen/iommu.h
+++ b/xen/include/xen/iommu.h
@@ -25,6 +25,7 @@
 #include <xen/pci.h>
 #include <public/hvm/ioreq.h>
 #include <public/domctl.h>
+#include <asm/device.h>
 #include <asm/iommu.h>
 
 extern bool_t iommu_enable, iommu_enabled;
diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h
index 5f295f3..3988ee68 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 <asm/device.h>
 #include <asm/pci.h>
 
 /*
-- 
2.1.4

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

* [PATCH v2 07/12] xen/iommu: Consolidate device assignment ops into a single set
  2015-01-16 14:23 [PATCH v2 00/12] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (5 preceding siblings ...)
  2015-01-16 14:24 ` [PATCH v2 06/12] xen/arm: Introduce a generic way to describe device Julien Grall
@ 2015-01-16 14:24 ` Julien Grall
  2015-01-16 15:03   ` Jan Beulich
  2015-01-27 16:16   ` Stefano Stabellini
  2015-01-16 14:24 ` [PATCH v2 08/12] xen/arm: Describe device supported by a driver with dt_match_node Julien Grall
                   ` (4 subsequent siblings)
  11 siblings, 2 replies; 51+ messages in thread
From: Julien Grall @ 2015-01-16 14:24 UTC (permalink / raw)
  To: xen-devel
  Cc: stefano.stabellini, Julien Grall, tim, ian.campbell, Jan Beulich

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

The x86 iommu drivers has not been modified to replace 'struct pci_dev'
by "device_t" because the latter is an alias of the former.

Signed-off-by: Julien Grall <julien.grall@linaro.org>
CC: Jan Beulich <jbeulich@suse.com>

---
    Changes in v2:
        - Use the newly type device_t
        - It's not neccessary to modify the x86 iommu drivers
---
 xen/drivers/passthrough/device_tree.c |  5 +++--
 xen/drivers/passthrough/pci.c         | 20 +++++++++++---------
 xen/include/xen/iommu.h               | 17 ++++++-----------
 3 files changed, 20 insertions(+), 22 deletions(-)

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 78c6977..71415d1 100644
--- a/xen/drivers/passthrough/pci.c
+++ b/xen/drivers/passthrough/pci.c
@@ -1254,7 +1254,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;
 
@@ -1263,7 +1263,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);
@@ -1284,7 +1284,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)
@@ -1306,7 +1306,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;
 
@@ -1315,7 +1315,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));
 }
 
 /*
@@ -1376,7 +1376,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 )
@@ -1384,7 +1384,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),
@@ -1419,7 +1419,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;
 
@@ -1429,7 +1430,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/include/xen/iommu.h b/xen/include/xen/iommu.h
index ecb2627..bf4aff0 100644
--- a/xen/include/xen/iommu.h
+++ b/xen/include/xen/iommu.h
@@ -124,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, device_t *dev);
+    int (*enable_device)(device_t *dev);
+    int (*remove_device)(u8 devfn, device_t *dev);
+    int (*assign_device)(struct domain *, u8 devfn, device_t *dev);
     int (*reassign_device)(struct domain *s, struct domain *t,
-			   u8 devfn, struct pci_dev *);
+                           u8 devfn, device_t *dev);
+#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.4

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

* [PATCH v2 08/12] xen/arm: Describe device supported by a driver with dt_match_node
  2015-01-16 14:23 [PATCH v2 00/12] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (6 preceding siblings ...)
  2015-01-16 14:24 ` [PATCH v2 07/12] xen/iommu: Consolidate device assignment ops into a single set Julien Grall
@ 2015-01-16 14:24 ` Julien Grall
  2015-01-27 16:28   ` Stefano Stabellini
  2015-01-16 14:24 ` [PATCH v2 09/12] xen/iommu: arm: Import the SMMU driver from Linux Julien Grall
                   ` (3 subsequent siblings)
  11 siblings, 1 reply; 51+ messages in thread
From: Julien Grall @ 2015-01-16 14:24 UTC (permalink / raw)
  To: xen-devel; +Cc: stefano.stabellini, 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       |  4 ++--
 xen/include/asm-arm/gic.h          | 15 +++++----------
 9 files changed, 35 insertions(+), 59 deletions(-)

diff --git a/xen/arch/arm/device.c b/xen/arch/arm/device.c
index 693b9af..e4f4603 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 f149e09..34d622f 100644
--- a/xen/arch/arm/gic-v2.c
+++ b/xen/arch/arm/gic-v2.c
@@ -764,16 +764,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 076aa62..35f80b3 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 cba8729..bac1c2b 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 161b251..d443880 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 16d1454..d8f64ea 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 57274d9..67e6df5 100644
--- a/xen/drivers/char/pl011.c
+++ b/xen/drivers/char/pl011.c
@@ -285,14 +285,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 2fe4280..e4666b4 100644
--- a/xen/include/asm-arm/device.h
+++ b/xen/include/asm-arm/device.h
@@ -44,8 +44,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.4

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

* [PATCH v2 09/12] xen/iommu: arm: Import the SMMU driver from Linux
  2015-01-16 14:23 [PATCH v2 00/12] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (7 preceding siblings ...)
  2015-01-16 14:24 ` [PATCH v2 08/12] xen/arm: Describe device supported by a driver with dt_match_node Julien Grall
@ 2015-01-16 14:24 ` Julien Grall
  2015-01-27 16:28   ` Stefano Stabellini
  2015-01-16 14:24 ` [PATCH v2 10/12] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices Julien Grall
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 51+ messages in thread
From: Julien Grall @ 2015-01-16 14:24 UTC (permalink / raw)
  To: xen-devel; +Cc: stefano.stabellini, 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-off-by: Julien Grall <julien.grall@linaro.org>

---
    Chnages in v2:
        - Fix typo in the Signed-off-by
---
 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.4

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

* [PATCH v2 10/12] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices
  2015-01-16 14:23 [PATCH v2 00/12] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (8 preceding siblings ...)
  2015-01-16 14:24 ` [PATCH v2 09/12] xen/iommu: arm: Import the SMMU driver from Linux Julien Grall
@ 2015-01-16 14:24 ` Julien Grall
  2015-01-27 16:30   ` Stefano Stabellini
  2015-01-16 14:24 ` [PATCH v2 11/12] xen/iommu: smmu: Introduce automatic stream-id-masking Julien Grall
  2015-01-16 14:24 ` [PATCH v2 12/12] xen/iommu: smmu: Add Xen specific code to be able to use the driver Julien Grall
  11 siblings, 1 reply; 51+ messages in thread
From: Julien Grall @ 2015-01-16 14:24 UTC (permalink / raw)
  To: xen-devel
  Cc: ian.campbell, Andreas Herrmann, 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.4

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

* [PATCH v2 11/12] xen/iommu: smmu: Introduce automatic stream-id-masking
  2015-01-16 14:23 [PATCH v2 00/12] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (9 preceding siblings ...)
  2015-01-16 14:24 ` [PATCH v2 10/12] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices Julien Grall
@ 2015-01-16 14:24 ` Julien Grall
  2015-01-16 14:24 ` [PATCH v2 12/12] xen/iommu: smmu: Add Xen specific code to be able to use the driver Julien Grall
  11 siblings, 0 replies; 51+ messages in thread
From: Julien Grall @ 2015-01-16 14:24 UTC (permalink / raw)
  To: xen-devel
  Cc: ian.campbell, Andreas Herrmann, 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.4

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

* [PATCH v2 12/12] xen/iommu: smmu: Add Xen specific code to be able to use the driver
  2015-01-16 14:23 [PATCH v2 00/12] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
                   ` (10 preceding siblings ...)
  2015-01-16 14:24 ` [PATCH v2 11/12] xen/iommu: smmu: Introduce automatic stream-id-masking Julien Grall
@ 2015-01-16 14:24 ` Julien Grall
  2015-01-27 16:46   ` Stefano Stabellini
  11 siblings, 1 reply; 51+ messages in thread
From: Julien Grall @ 2015-01-16 14:24 UTC (permalink / raw)
  To: xen-devel; +Cc: stefano.stabellini, 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>

---
    Changes in v2:
        - Add the ACCESS_ONCE definition in the drivers. The patch to
        introduce the one in common code has been dropped.
        - The include xen/device.h has been dropped in favor of
        asm/device.h
---
 xen/drivers/passthrough/arm/Makefile |   1 +
 xen/drivers/passthrough/arm/smmu.c   | 678 +++++++++++++++++++++++++++++++----
 2 files changed, 612 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..373eee8 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,164 @@
  *	- 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/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/device.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 */
+
+/* The macro ACCESS_ONCE start to be replaced in Linux in favor of
+ * {READ, WRITE}_ONCE. Rather than introducing in the common code, keep a
+ * version here. We will have to drop it when the SMMU code in Linux will
+ * switch to {READ, WRITE}_ONCE.
+ */
+#define __ACCESS_ONCE(x) ({ \
+	 __maybe_unused typeof(x) __var = 0; \
+	(volatile typeof(x) *)&(x); })
+#define ACCESS_ONCE(x) (*__ACCESS_ONCE(x))
+
+/***** Start of SMMU definitions *****/
 
 /* Maximum number of stream IDs assigned to a single device */
 #define MAX_MASTER_STREAMIDS		MAX_PHANDLE_ARGS
@@ -330,10 +475,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 +555,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 +577,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 +690,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 +699,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 +793,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 +891,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 +907,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 +919,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 +938,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 +953,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 +962,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 +987,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 +996,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 +1080,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 +1131,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 +1288,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 +1298,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 +1366,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 +1508,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 +1632,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 +1657,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 +1689,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 +1704,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 +2000,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 +2032,7 @@ static void __arm_smmu_release_pci_iommudata(void *data)
 {
 	kfree(data);
 }
+#endif
 
 static int arm_smmu_add_device(struct device *dev)
 {
@@ -1784,6 +2042,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 +2057,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 +2076,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 +2097,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 +2156,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 +2172,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 +2202,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 +2314,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 +2349,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 +2417,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 +2445,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 +2468,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 +2485,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 +2561,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 +2669,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.4

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

* Re: [PATCH v2 06/12] xen/arm: Introduce a generic way to describe device
  2015-01-16 14:24 ` [PATCH v2 06/12] xen/arm: Introduce a generic way to describe device Julien Grall
@ 2015-01-16 14:59   ` Jan Beulich
  2015-01-16 15:01     ` Julien Grall
  2015-01-27 16:13   ` Stefano Stabellini
  1 sibling, 1 reply; 51+ messages in thread
From: Jan Beulich @ 2015-01-16 14:59 UTC (permalink / raw)
  To: Julien Grall
  Cc: Keir Fraser, ian.campbell, Andrew Cooper, tim,
	stefano.stabellini, xen-devel

>>> On 16.01.15 at 15:24, <julien.grall@linaro.org> wrote:
> ---
>  xen/common/device.c           | 21 +++++++++++++++++++++
>  xen/common/device_tree.c      |  3 +++

Is there a Makefile change missing here?

> --- /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..d1c716f 100644
> --- a/xen/common/device_tree.c
> +++ b/xen/common/device_tree.c
> @@ -1454,6 +1454,9 @@ static unsigned long __init unflatten_dt_node(const 
> void *fdt,
>              ((char *)pp->value)[sz - 1] = 0;
>              dt_dprintk("fixed up name for %s -> %s\n", pathp,
>                         (char *)pp->value);
> +            /* Generic device initialization */
> +            np->dev.type = DEV_DT;
> +            np->dev.of_node = np;

Why is this not device_initialize(&np->dev, DEV_DT)?

> --- /dev/null
> +++ b/xen/include/asm-x86/device.h
> @@ -0,0 +1,25 @@
> +#ifndef __ASM_X86_DEVICE_H
> +#define __ASM_X86_DEVICE_H
> +
> +#include <xen/pci.h>
> +
> +/*
> + * x86 is only supported PCI. Therefore it's possible to directly use

x86 only supports ...

Jan

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

* Re: [PATCH v2 06/12] xen/arm: Introduce a generic way to describe device
  2015-01-16 14:59   ` Jan Beulich
@ 2015-01-16 15:01     ` Julien Grall
  0 siblings, 0 replies; 51+ messages in thread
From: Julien Grall @ 2015-01-16 15:01 UTC (permalink / raw)
  To: Jan Beulich
  Cc: Keir Fraser, ian.campbell, Andrew Cooper, tim,
	stefano.stabellini, xen-devel

Hi Jan,

On 16/01/15 14:59, Jan Beulich wrote:
>>>> On 16.01.15 at 15:24, <julien.grall@linaro.org> wrote:
>> ---
>>  xen/common/device.c           | 21 +++++++++++++++++++++
>>  xen/common/device_tree.c      |  3 +++
> 
> Is there a Makefile change missing here?

No. The file common/device.c should not be there. I drop for my tree but
somehow it stay in the patch. Sorry.

[..]

>> diff --git a/xen/common/device_tree.c b/xen/common/device_tree.c
>> index 34a1b9e..d1c716f 100644
>> --- a/xen/common/device_tree.c
>> +++ b/xen/common/device_tree.c
>> @@ -1454,6 +1454,9 @@ static unsigned long __init unflatten_dt_node(const 
>> void *fdt,
>>              ((char *)pp->value)[sz - 1] = 0;
>>              dt_dprintk("fixed up name for %s -> %s\n", pathp,
>>                         (char *)pp->value);
>> +            /* Generic device initialization */
>> +            np->dev.type = DEV_DT;
>> +            np->dev.of_node = np;
> 
> Why is this not device_initialize(&np->dev, DEV_DT)?

Because the file containing the function has been dropped. It's a
left-over of the previous version.

Regards,

-- 
Julien Grall

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

* Re: [PATCH v2 07/12] xen/iommu: Consolidate device assignment ops into a single set
  2015-01-16 14:24 ` [PATCH v2 07/12] xen/iommu: Consolidate device assignment ops into a single set Julien Grall
@ 2015-01-16 15:03   ` Jan Beulich
  2015-01-27 16:16   ` Stefano Stabellini
  1 sibling, 0 replies; 51+ messages in thread
From: Jan Beulich @ 2015-01-16 15:03 UTC (permalink / raw)
  To: Julien Grall; +Cc: xen-devel, stefano.stabellini, ian.campbell, tim

>>> On 16.01.15 at 15:24, <julien.grall@linaro.org> wrote:
> On ARM, the way to assign device tree node is exactly the same as PCI.
> Futhermore, all devices can be represented by a 'device_t'.
> Therefore there is no need to add separate ops.
> 
> The x86 iommu drivers has not been modified to replace 'struct pci_dev'
> by "device_t" because the latter is an alias of the former.
> 
> Signed-off-by: Julien Grall <julien.grall@linaro.org>

Acked-by: Jan Beulich <jbeulich@suse.com>

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

* Re: [PATCH v2 01/12] xen/arm: gic-v2: Change the device name in DT_DEVICE_START
  2015-01-16 14:23 ` [PATCH v2 01/12] xen/arm: gic-v2: Change the device name in DT_DEVICE_START Julien Grall
@ 2015-01-27 15:52   ` Stefano Stabellini
  0 siblings, 0 replies; 51+ messages in thread
From: Stefano Stabellini @ 2015-01-27 15:52 UTC (permalink / raw)
  To: Julien Grall; +Cc: xen-devel, tim, ian.campbell, stefano.stabellini

On Fri, 16 Jan 2015, Julien Grall wrote:
> 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>

Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>


>  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.4
> 

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

* Re: [PATCH v2 02/12] xen/arm: vgic: Drop unecessary include asm/device.h
  2015-01-16 14:23 ` [PATCH v2 02/12] xen/arm: vgic: Drop unecessary include asm/device.h Julien Grall
@ 2015-01-27 15:53   ` Stefano Stabellini
  0 siblings, 0 replies; 51+ messages in thread
From: Stefano Stabellini @ 2015-01-27 15:53 UTC (permalink / raw)
  To: Julien Grall; +Cc: xen-devel, tim, ian.campbell, stefano.stabellini

On Fri, 16 Jan 2015, Julien Grall wrote:
> 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>

Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>


>  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 4dc2267..1ac46cc 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 c738ca9..757ffd9 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.4
> 

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

* Re: [PATCH v2 03/12] xen/dt: Extend dt_device_match to possibly store data
  2015-01-16 14:23 ` [PATCH v2 03/12] xen/dt: Extend dt_device_match to possibly store data Julien Grall
@ 2015-01-27 15:57   ` Stefano Stabellini
  2015-01-27 16:07     ` Julien Grall
  0 siblings, 1 reply; 51+ messages in thread
From: Stefano Stabellini @ 2015-01-27 15:57 UTC (permalink / raw)
  To: Julien Grall; +Cc: xen-devel, tim, ian.campbell, stefano.stabellini

On Fri, 16 Jan 2015, Julien Grall wrote:
> 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;

Why are you adding this field? It doesn't seem to be required by the
changes to dt_match_node you are making in this patch.


>  };
>  
>  #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

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

* Re: [PATCH v2 04/12] xen/arm: device: Rename device_type into device_match
  2015-01-16 14:23 ` [PATCH v2 04/12] xen/arm: device: Rename device_type into device_match Julien Grall
@ 2015-01-27 16:05   ` Stefano Stabellini
  2015-01-27 16:09     ` Julien Grall
  0 siblings, 1 reply; 51+ messages in thread
From: Stefano Stabellini @ 2015-01-27 16:05 UTC (permalink / raw)
  To: Julien Grall; +Cc: xen-devel, tim, ian.campbell, stefano.stabellini

On Fri, 16 Jan 2015, Julien Grall wrote:
> 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>

If you really want to rename this field, please find a more descriptive
name for it. device_match doesn't really tell me what we are trying to
match against.  At least device_type would tell me that we are matching
against the type.



>  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.4
> 

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

* Re: [PATCH v2 05/12] xen/iommu: arm: Remove temporary the SMMU driver
  2015-01-16 14:24 ` [PATCH v2 05/12] xen/iommu: arm: Remove temporary the SMMU driver Julien Grall
@ 2015-01-27 16:05   ` Stefano Stabellini
  0 siblings, 0 replies; 51+ messages in thread
From: Stefano Stabellini @ 2015-01-27 16:05 UTC (permalink / raw)
  To: Julien Grall; +Cc: xen-devel, tim, ian.campbell, stefano.stabellini

On Fri, 16 Jan 2015, Julien Grall wrote:
> The current SMMU driver has completly diverged. That makes me hard to
> maintain.
> 
> Signed-off-by: Julien Grall <julien.grall@linaro.org>

Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>


>  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.4
> 

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

* Re: [PATCH v2 03/12] xen/dt: Extend dt_device_match to possibly store data
  2015-01-27 15:57   ` Stefano Stabellini
@ 2015-01-27 16:07     ` Julien Grall
  2015-01-27 16:10       ` Stefano Stabellini
  0 siblings, 1 reply; 51+ messages in thread
From: Julien Grall @ 2015-01-27 16:07 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: xen-devel, tim, ian.campbell, stefano.stabellini

Hi Stefano,

On 27/01/15 15:57, Stefano Stabellini wrote:
>>  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;
> 
> Why are you adding this field? It doesn't seem to be required by the
> changes to dt_match_node you are making in this patch.

It's required for the SMMU drivers. The version of the SMMU is stored in
the field data.

+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 },
+	{ },
+};

The main goal of this patch is too add the field data. The change of
dt_match_node is only a side effect.

Regards,

-- 
Julien Grall

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

* Re: [PATCH v2 04/12] xen/arm: device: Rename device_type into device_match
  2015-01-27 16:05   ` Stefano Stabellini
@ 2015-01-27 16:09     ` Julien Grall
  2015-01-27 16:13       ` Stefano Stabellini
  0 siblings, 1 reply; 51+ messages in thread
From: Julien Grall @ 2015-01-27 16:09 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: xen-devel, tim, ian.campbell, stefano.stabellini

Hi Stefano,

On 27/01/15 16:05, Stefano Stabellini wrote:
> On Fri, 16 Jan 2015, Julien Grall wrote:
>> 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>
> 
> If you really want to rename this field, please find a more descriptive
> name for it. device_match doesn't really tell me what we are trying to
> match against.  At least device_type would tell me that we are matching
> against the type.

I was struggling to find a correct name. For me device_type refers to
PCI/platform device. And it will be used this way later.

Maybe device_category?

Regards,

-- 
Julien Grall

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

* Re: [PATCH v2 03/12] xen/dt: Extend dt_device_match to possibly store data
  2015-01-27 16:07     ` Julien Grall
@ 2015-01-27 16:10       ` Stefano Stabellini
  2015-01-27 16:35         ` Julien Grall
  0 siblings, 1 reply; 51+ messages in thread
From: Stefano Stabellini @ 2015-01-27 16:10 UTC (permalink / raw)
  To: Julien Grall
  Cc: xen-devel, tim, ian.campbell, stefano.stabellini, Stefano Stabellini

On Tue, 27 Jan 2015, Julien Grall wrote:
> Hi Stefano,
> 
> On 27/01/15 15:57, Stefano Stabellini wrote:
> >>  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;
> > 
> > Why are you adding this field? It doesn't seem to be required by the
> > changes to dt_match_node you are making in this patch.
> 
> It's required for the SMMU drivers. The version of the SMMU is stored in
> the field data.
> 
> +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 },
> +	{ },
> +};
> 
> The main goal of this patch is too add the field data. The change of
> dt_match_node is only a side effect.

In that case please make sure to write it clearly in the commit message.

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

* Re: [PATCH v2 06/12] xen/arm: Introduce a generic way to describe device
  2015-01-16 14:24 ` [PATCH v2 06/12] xen/arm: Introduce a generic way to describe device Julien Grall
  2015-01-16 14:59   ` Jan Beulich
@ 2015-01-27 16:13   ` Stefano Stabellini
  1 sibling, 0 replies; 51+ messages in thread
From: Stefano Stabellini @ 2015-01-27 16:13 UTC (permalink / raw)
  To: Julien Grall
  Cc: Keir Fraser, ian.campbell, Andrew Cooper, tim,
	stefano.stabellini, Jan Beulich, xen-devel

On Fri, 16 Jan 2015, Julien Grall wrote:
> Currently, Xen is supporting PCI and Platform device (based on Device Tree).
> 
> While Xen only supports Platform device on ARM, Xen will gain support of
> 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.
> 
> As x86 only supports PCI, introduce a new type device_t which will be an
> alias to pci_dev for this architecture. It will avoid to add a new field
> for this place.
> 
> Signed-off-by: Julien Grall <julien.grall@linaro.org>
> CC: Jan Beulich <jbeulich@suse.com>
> CC: Keir Fraser <keir@xen.org>
> CC: Andrew Cooper <andrew.cooper3@citrix.com>

It looks OK to me


>     Changes in v2:
>         - As x86 will only support PCI, only introduce the generic
>         device on ARM
>         - Introduce a typedef device_t to be used in common code
>         - Drop the PCI code for ARM for now. It will be reintroduced
>         when PCI support will be added
>         - s#asm/device.h#xen/device.h# is not anymore needed
> ---
>  xen/common/device.c           | 21 +++++++++++++++++++++
>  xen/common/device_tree.c      |  3 +++
>  xen/include/asm-arm/device.h  | 26 ++++++++++++++++++++++++++
>  xen/include/asm-x86/device.h  | 25 +++++++++++++++++++++++++
>  xen/include/xen/device_tree.h | 13 +++++++++++++
>  xen/include/xen/iommu.h       |  1 +
>  xen/include/xen/pci.h         |  1 +
>  7 files changed, 90 insertions(+)
>  create mode 100644 xen/common/device.c
>  create mode 100644 xen/include/asm-x86/device.h
> 
> 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..d1c716f 100644
> --- a/xen/common/device_tree.c
> +++ b/xen/common/device_tree.c
> @@ -1454,6 +1454,9 @@ static unsigned long __init unflatten_dt_node(const void *fdt,
>              ((char *)pp->value)[sz - 1] = 0;
>              dt_dprintk("fixed up name for %s -> %s\n", pathp,
>                         (char *)pp->value);
> +            /* Generic device initialization */
> +            np->dev.type = DEV_DT;
> +            np->dev.of_node = np;
>          }
>      }
>      if ( allnextpp )
> diff --git a/xen/include/asm-arm/device.h b/xen/include/asm-arm/device.h
> index 72a9028..2fe4280 100644
> --- a/xen/include/asm-arm/device.h
> +++ b/xen/include/asm-arm/device.h
> @@ -2,8 +2,34 @@
>  #define __ASM_ARM_DEVICE_H
>  
>  #include <xen/init.h>
> +
> +enum device_type
> +{
> +    DEV_DT,
> +};
> +
> +struct dev_archdata {
> +    void *iommu;    /* IOMMU private data */
> +};
> +
> +/* 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;
> +};
> +
> +typedef struct device device_t;
> +
>  #include <xen/device_tree.h>
>  
> +/* TODO: Correctly implement dev_is_pci when PCI will be supported on ARM */
> +#define dev_is_pci(dev) ((void)(dev), 0)
> +#define dev_is_dt(dev)  ((dev->type == DEV_DT)
> +
>  enum device_match
>  {
>      DEVICE_SERIAL,
> diff --git a/xen/include/asm-x86/device.h b/xen/include/asm-x86/device.h
> new file mode 100644
> index 0000000..a016112
> --- /dev/null
> +++ b/xen/include/asm-x86/device.h
> @@ -0,0 +1,25 @@
> +#ifndef __ASM_X86_DEVICE_H
> +#define __ASM_X86_DEVICE_H
> +
> +#include <xen/pci.h>
> +
> +/*
> + * x86 is only supported PCI. Therefore it's possible to directly use
> + * pci_dev to avoid adding new field.
> + */
> +
> +typedef struct pci_dev device_t;
> +
> +#define dev_is_pci(dev) ((void)(dev), 1)
> +#define pci_to_dev(pci) (pci)
> +
> +#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_tree.h b/xen/include/xen/device_tree.h
> index 6502369..c8a0375 100644
> --- a/xen/include/xen/device_tree.h
> +++ b/xen/include/xen/device_tree.h
> @@ -11,7 +11,9 @@
>  #define __XEN_DEVICE_TREE_H__
>  
>  #include <asm/byteorder.h>
> +#include <asm/device.h>
>  #include <public/xen.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/iommu.h b/xen/include/xen/iommu.h
> index 8eb764a..ecb2627 100644
> --- a/xen/include/xen/iommu.h
> +++ b/xen/include/xen/iommu.h
> @@ -25,6 +25,7 @@
>  #include <xen/pci.h>
>  #include <public/hvm/ioreq.h>
>  #include <public/domctl.h>
> +#include <asm/device.h>
>  #include <asm/iommu.h>
>  
>  extern bool_t iommu_enable, iommu_enabled;
> diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h
> index 5f295f3..3988ee68 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 <asm/device.h>
>  #include <asm/pci.h>
>  
>  /*
> -- 
> 2.1.4
> 

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

* Re: [PATCH v2 04/12] xen/arm: device: Rename device_type into device_match
  2015-01-27 16:09     ` Julien Grall
@ 2015-01-27 16:13       ` Stefano Stabellini
  2015-01-27 22:26         ` Julien Grall
  0 siblings, 1 reply; 51+ messages in thread
From: Stefano Stabellini @ 2015-01-27 16:13 UTC (permalink / raw)
  To: Julien Grall
  Cc: xen-devel, tim, ian.campbell, stefano.stabellini, Stefano Stabellini

On Tue, 27 Jan 2015, Julien Grall wrote:
> Hi Stefano,
> 
> On 27/01/15 16:05, Stefano Stabellini wrote:
> > On Fri, 16 Jan 2015, Julien Grall wrote:
> >> 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>
> > 
> > If you really want to rename this field, please find a more descriptive
> > name for it. device_match doesn't really tell me what we are trying to
> > match against.  At least device_type would tell me that we are matching
> > against the type.
> 
> I was struggling to find a correct name. For me device_type refers to
> PCI/platform device. And it will be used this way later.
> 
> Maybe device_category?

Yes, or device_class

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

* Re: [PATCH v2 07/12] xen/iommu: Consolidate device assignment ops into a single set
  2015-01-16 14:24 ` [PATCH v2 07/12] xen/iommu: Consolidate device assignment ops into a single set Julien Grall
  2015-01-16 15:03   ` Jan Beulich
@ 2015-01-27 16:16   ` Stefano Stabellini
  1 sibling, 0 replies; 51+ messages in thread
From: Stefano Stabellini @ 2015-01-27 16:16 UTC (permalink / raw)
  To: Julien Grall
  Cc: xen-devel, Jan Beulich, tim, ian.campbell, stefano.stabellini

On Fri, 16 Jan 2015, Julien Grall wrote:
> On ARM, the way to assign device tree node is exactly the same as PCI.
> Futhermore, all devices can be represented by a 'device_t'.
> Therefore there is no need to add separate ops.
> 
> The x86 iommu drivers has not been modified to replace 'struct pci_dev'
> by "device_t" because the latter is an alias of the former.
> 
> Signed-off-by: Julien Grall <julien.grall@linaro.org>
> CC: Jan Beulich <jbeulich@suse.com>
> 
Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>


>     Changes in v2:
>         - Use the newly type device_t
>         - It's not neccessary to modify the x86 iommu drivers
> ---
>  xen/drivers/passthrough/device_tree.c |  5 +++--
>  xen/drivers/passthrough/pci.c         | 20 +++++++++++---------
>  xen/include/xen/iommu.h               | 17 ++++++-----------
>  3 files changed, 20 insertions(+), 22 deletions(-)
> 
> 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 78c6977..71415d1 100644
> --- a/xen/drivers/passthrough/pci.c
> +++ b/xen/drivers/passthrough/pci.c
> @@ -1254,7 +1254,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;
>  
> @@ -1263,7 +1263,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);
> @@ -1284,7 +1284,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)
> @@ -1306,7 +1306,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;
>  
> @@ -1315,7 +1315,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));
>  }
>  
>  /*
> @@ -1376,7 +1376,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 )
> @@ -1384,7 +1384,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),
> @@ -1419,7 +1419,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;
>  
> @@ -1429,7 +1430,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/include/xen/iommu.h b/xen/include/xen/iommu.h
> index ecb2627..bf4aff0 100644
> --- a/xen/include/xen/iommu.h
> +++ b/xen/include/xen/iommu.h
> @@ -124,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, device_t *dev);
> +    int (*enable_device)(device_t *dev);
> +    int (*remove_device)(u8 devfn, device_t *dev);
> +    int (*assign_device)(struct domain *, u8 devfn, device_t *dev);
>      int (*reassign_device)(struct domain *s, struct domain *t,
> -			   u8 devfn, struct pci_dev *);
> +                           u8 devfn, device_t *dev);
> +#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.4
> 

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

* Re: [PATCH v2 08/12] xen/arm: Describe device supported by a driver with dt_match_node
  2015-01-16 14:24 ` [PATCH v2 08/12] xen/arm: Describe device supported by a driver with dt_match_node Julien Grall
@ 2015-01-27 16:28   ` Stefano Stabellini
  0 siblings, 0 replies; 51+ messages in thread
From: Stefano Stabellini @ 2015-01-27 16:28 UTC (permalink / raw)
  To: Julien Grall; +Cc: xen-devel, tim, ian.campbell, stefano.stabellini

On Fri, 16 Jan 2015, Julien Grall wrote:
> 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>

Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>


>  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       |  4 ++--
>  xen/include/asm-arm/gic.h          | 15 +++++----------
>  9 files changed, 35 insertions(+), 59 deletions(-)
> 
> diff --git a/xen/arch/arm/device.c b/xen/arch/arm/device.c
> index 693b9af..e4f4603 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 f149e09..34d622f 100644
> --- a/xen/arch/arm/gic-v2.c
> +++ b/xen/arch/arm/gic-v2.c
> @@ -764,16 +764,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 076aa62..35f80b3 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 cba8729..bac1c2b 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 161b251..d443880 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 16d1454..d8f64ea 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 57274d9..67e6df5 100644
> --- a/xen/drivers/char/pl011.c
> +++ b/xen/drivers/char/pl011.c
> @@ -285,14 +285,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 2fe4280..e4666b4 100644
> --- a/xen/include/asm-arm/device.h
> +++ b/xen/include/asm-arm/device.h
> @@ -44,8 +44,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.4
> 

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

* Re: [PATCH v2 09/12] xen/iommu: arm: Import the SMMU driver from Linux
  2015-01-16 14:24 ` [PATCH v2 09/12] xen/iommu: arm: Import the SMMU driver from Linux Julien Grall
@ 2015-01-27 16:28   ` Stefano Stabellini
  2015-01-27 16:51     ` Julien Grall
  0 siblings, 1 reply; 51+ messages in thread
From: Stefano Stabellini @ 2015-01-27 16:28 UTC (permalink / raw)
  To: Julien Grall; +Cc: xen-devel, tim, ian.campbell, stefano.stabellini

On Fri, 16 Jan 2015, Julien Grall wrote:
> 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.

Do you mean a verbatim copy? As in unmmodified?
If so:

Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>


> Compare to the previous drivers it gains support of PCI. Though it will
> need a bit of plumbing for Xen.
> 
> Signed-off-by: Julien Grall <julien.grall@linaro.org>
> 
> ---
>     Chnages in v2:
>         - Fix typo in the Signed-off-by
> ---
>  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.4
> 

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

* Re: [PATCH v2 10/12] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices
  2015-01-16 14:24 ` [PATCH v2 10/12] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices Julien Grall
@ 2015-01-27 16:30   ` Stefano Stabellini
  2015-01-27 16:52     ` Julien Grall
  0 siblings, 1 reply; 51+ messages in thread
From: Stefano Stabellini @ 2015-01-27 16:30 UTC (permalink / raw)
  To: Julien Grall
  Cc: ian.campbell, tim, Andreas Herrmann, stefano.stabellini,
	Andreas Herrmann, xen-devel

On Fri, 16 Jan 2015, Julien Grall wrote:
> 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>

Why didn't you just take a more recent version of the Linux smmu driver?


>  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.4
> 

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

* Re: [PATCH v2 03/12] xen/dt: Extend dt_device_match to possibly store data
  2015-01-27 16:10       ` Stefano Stabellini
@ 2015-01-27 16:35         ` Julien Grall
  2015-01-27 16:46           ` Stefano Stabellini
  0 siblings, 1 reply; 51+ messages in thread
From: Julien Grall @ 2015-01-27 16:35 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: xen-devel, tim, ian.campbell, stefano.stabellini

On 27/01/15 16:10, Stefano Stabellini wrote:
> On Tue, 27 Jan 2015, Julien Grall wrote:
>> Hi Stefano,
>>
>> On 27/01/15 15:57, Stefano Stabellini wrote:
>>>>  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;
>>>
>>> Why are you adding this field? It doesn't seem to be required by the
>>> changes to dt_match_node you are making in this patch.
>>
>> It's required for the SMMU drivers. The version of the SMMU is stored in
>> the field data.
>>
>> +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 },
>> +	{ },
>> +};
>>
>> The main goal of this patch is too add the field data. The change of
>> dt_match_node is only a side effect.
> 
> In that case please make sure to write it clearly in the commit message.

It's already on the title "xen/dt: Extend dt_device_match to possibly
store data" and the reason is the first line of the commit message
"Some drivers may want to configure differently the device depending on
the compatible string.".

I'm not sure how I can make this more clearly.

Regards,

-- 
Julien Grall

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

* Re: [PATCH v2 12/12] xen/iommu: smmu: Add Xen specific code to be able to use the driver
  2015-01-16 14:24 ` [PATCH v2 12/12] xen/iommu: smmu: Add Xen specific code to be able to use the driver Julien Grall
@ 2015-01-27 16:46   ` Stefano Stabellini
  2015-01-27 17:05     ` Julien Grall
  0 siblings, 1 reply; 51+ messages in thread
From: Stefano Stabellini @ 2015-01-27 16:46 UTC (permalink / raw)
  To: Julien Grall; +Cc: xen-devel, tim, ian.campbell, stefano.stabellini

On Fri, 16 Jan 2015, 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.

Agreed.


> To achieve that we:
>     - Add helpers to Linux function not implemented on Xen

Good idea, I would take it further and move the helpers to a separate
file that smmu.c #includes. Or it could work the other way around: you
could move the original code to smmu-linux.c that get #included into
smmu.c


>     - 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.

I wonder if it makes sense to keep your changes in patch format and
apply the patch at run time as part of the make system.

Of course you would need to be careful to keep the patched smmu.c
separate from the original, otherwise it would get very confusing when
people works on their changes.


> 
> 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>
> 
> ---
>     Changes in v2:
>         - Add the ACCESS_ONCE definition in the drivers. The patch to
>         introduce the one in common code has been dropped.
>         - The include xen/device.h has been dropped in favor of
>         asm/device.h
> ---
>  xen/drivers/passthrough/arm/Makefile |   1 +
>  xen/drivers/passthrough/arm/smmu.c   | 678 +++++++++++++++++++++++++++++++----
>  2 files changed, 612 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..373eee8 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,164 @@
>   *	- 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/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/device.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 */
> +
> +/* The macro ACCESS_ONCE start to be replaced in Linux in favor of
> + * {READ, WRITE}_ONCE. Rather than introducing in the common code, keep a
> + * version here. We will have to drop it when the SMMU code in Linux will
> + * switch to {READ, WRITE}_ONCE.
> + */
> +#define __ACCESS_ONCE(x) ({ \
> +	 __maybe_unused typeof(x) __var = 0; \
> +	(volatile typeof(x) *)&(x); })
> +#define ACCESS_ONCE(x) (*__ACCESS_ONCE(x))
> +
> +/***** Start of SMMU definitions *****/
>  
>  /* Maximum number of stream IDs assigned to a single device */
>  #define MAX_MASTER_STREAMIDS		MAX_PHANDLE_ARGS
> @@ -330,10 +475,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 +555,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 +577,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 +690,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 +699,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 +793,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 +891,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 +907,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 +919,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 +938,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 +953,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 +962,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 +987,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 +996,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 +1080,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 +1131,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 +1288,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 +1298,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 +1366,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 +1508,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 +1632,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 +1657,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 +1689,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 +1704,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 +2000,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 +2032,7 @@ static void __arm_smmu_release_pci_iommudata(void *data)
>  {
>  	kfree(data);
>  }
> +#endif
>  
>  static int arm_smmu_add_device(struct device *dev)
>  {
> @@ -1784,6 +2042,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 +2057,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 +2076,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 +2097,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 +2156,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 +2172,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 +2202,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 +2314,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 +2349,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 +2417,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 +2445,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 +2468,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 +2485,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 +2561,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 +2669,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.4
> 

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

* Re: [PATCH v2 03/12] xen/dt: Extend dt_device_match to possibly store data
  2015-01-27 16:35         ` Julien Grall
@ 2015-01-27 16:46           ` Stefano Stabellini
  2015-01-27 16:49             ` Julien Grall
  0 siblings, 1 reply; 51+ messages in thread
From: Stefano Stabellini @ 2015-01-27 16:46 UTC (permalink / raw)
  To: Julien Grall
  Cc: xen-devel, tim, ian.campbell, stefano.stabellini, Stefano Stabellini

On Tue, 27 Jan 2015, Julien Grall wrote:
> On 27/01/15 16:10, Stefano Stabellini wrote:
> > On Tue, 27 Jan 2015, Julien Grall wrote:
> >> Hi Stefano,
> >>
> >> On 27/01/15 15:57, Stefano Stabellini wrote:
> >>>>  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;
> >>>
> >>> Why are you adding this field? It doesn't seem to be required by the
> >>> changes to dt_match_node you are making in this patch.
> >>
> >> It's required for the SMMU drivers. The version of the SMMU is stored in
> >> the field data.
> >>
> >> +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 },
> >> +	{ },
> >> +};
> >>
> >> The main goal of this patch is too add the field data. The change of
> >> dt_match_node is only a side effect.
> > 
> > In that case please make sure to write it clearly in the commit message.
> 
> It's already on the title "xen/dt: Extend dt_device_match to possibly
> store data" and the reason is the first line of the commit message
> "Some drivers may want to configure differently the device depending on
> the compatible string.".

What data? How are you extending it?


> I'm not sure how I can make this more clearly.
> 
> Regards,
> 
> -- 
> Julien Grall
> 

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

* Re: [PATCH v2 03/12] xen/dt: Extend dt_device_match to possibly store data
  2015-01-27 16:46           ` Stefano Stabellini
@ 2015-01-27 16:49             ` Julien Grall
  0 siblings, 0 replies; 51+ messages in thread
From: Julien Grall @ 2015-01-27 16:49 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: xen-devel, tim, ian.campbell, stefano.stabellini

On 27/01/15 16:46, Stefano Stabellini wrote:
> On Tue, 27 Jan 2015, Julien Grall wrote:
>> On 27/01/15 16:10, Stefano Stabellini wrote:
>>> On Tue, 27 Jan 2015, Julien Grall wrote:
>>>> Hi Stefano,
>>>>
>>>> On 27/01/15 15:57, Stefano Stabellini wrote:
>>>>>>  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;
>>>>>
>>>>> Why are you adding this field? It doesn't seem to be required by the
>>>>> changes to dt_match_node you are making in this patch.
>>>>
>>>> It's required for the SMMU drivers. The version of the SMMU is stored in
>>>> the field data.
>>>>
>>>> +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 },
>>>> +	{ },
>>>> +};
>>>>
>>>> The main goal of this patch is too add the field data. The change of
>>>> dt_match_node is only a side effect.
>>>
>>> In that case please make sure to write it clearly in the commit message.
>>
>> It's already on the title "xen/dt: Extend dt_device_match to possibly
>> store data" and the reason is the first line of the commit message
>> "Some drivers may want to configure differently the device depending on
>> the compatible string.".
> 
> What data? How are you extending it?

Data could be anything. I think it's enough logic to know that
dt_device_match is a structure and a new field will be added.

Regards,

-- 
Julien Grall

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

* Re: [PATCH v2 09/12] xen/iommu: arm: Import the SMMU driver from Linux
  2015-01-27 16:28   ` Stefano Stabellini
@ 2015-01-27 16:51     ` Julien Grall
  0 siblings, 0 replies; 51+ messages in thread
From: Julien Grall @ 2015-01-27 16:51 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: xen-devel, tim, ian.campbell, stefano.stabellini

On 27/01/15 16:28, Stefano Stabellini wrote:
> On Fri, 16 Jan 2015, Julien Grall wrote:
>> 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.
> 
> Do you mean a verbatim copy? As in unmmodified?
> If so:

Yes. I could extend a bit the commit message.

Regards,

-- 
Julien Grall

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

* Re: [PATCH v2 10/12] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices
  2015-01-27 16:30   ` Stefano Stabellini
@ 2015-01-27 16:52     ` Julien Grall
  2015-01-27 17:02       ` Stefano Stabellini
  0 siblings, 1 reply; 51+ messages in thread
From: Julien Grall @ 2015-01-27 16:52 UTC (permalink / raw)
  To: Stefano Stabellini
  Cc: ian.campbell, tim, Andreas Herrmann, stefano.stabellini,
	Andreas Herrmann, xen-devel

On 27/01/15 16:30, Stefano Stabellini wrote:
> On Fri, 16 Jan 2015, Julien Grall wrote:
>> 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>
> 
> Why didn't you just take a more recent version of the Linux smmu driver?

The SMMU driver very is recent (see commit in the previous patch)...
Just this patch has never reached upstream.

Regards,

-- 
Julien Grall

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

* Re: [PATCH v2 10/12] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices
  2015-01-27 16:52     ` Julien Grall
@ 2015-01-27 17:02       ` Stefano Stabellini
  2015-01-27 17:33         ` Julien Grall
  0 siblings, 1 reply; 51+ messages in thread
From: Stefano Stabellini @ 2015-01-27 17:02 UTC (permalink / raw)
  To: Julien Grall
  Cc: ian.campbell, Stefano Stabellini, Andreas Herrmann, tim,
	stefano.stabellini, Andreas Herrmann, xen-devel

On Tue, 27 Jan 2015, Julien Grall wrote:
> On 27/01/15 16:30, Stefano Stabellini wrote:
> > On Fri, 16 Jan 2015, Julien Grall wrote:
> >> 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>
> > 
> > Why didn't you just take a more recent version of the Linux smmu driver?
> 
> The SMMU driver very is recent (see commit in the previous patch)...
> Just this patch has never reached upstream.

That is not good. It might be worth to wait for it to go upstream.

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

* Re: [PATCH v2 12/12] xen/iommu: smmu: Add Xen specific code to be able to use the driver
  2015-01-27 16:46   ` Stefano Stabellini
@ 2015-01-27 17:05     ` Julien Grall
  2015-01-27 17:34       ` Stefano Stabellini
  0 siblings, 1 reply; 51+ messages in thread
From: Julien Grall @ 2015-01-27 17:05 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: xen-devel, tim, ian.campbell, stefano.stabellini

On 27/01/15 16:46, Stefano Stabellini wrote:
> On Fri, 16 Jan 2015, 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.
> 
> Agreed.
> 
> 
>> To achieve that we:
>>     - Add helpers to Linux function not implemented on Xen
> 
> Good idea, I would take it further and move the helpers to a separate
> file that smmu.c #includes. Or it could work the other way around: you
> could move the original code to smmu-linux.c that get #included into
> smmu.c

We would have to modify smmu-linux.c for disabling some functions and/or
modify them.

Although for the later, there is only a couple functions modified.

> 
>>     - 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.
> 
> I wonder if it makes sense to keep your changes in patch format and
> apply the patch at run time as part of the make system.
> 
> Of course you would need to be careful to keep the patched smmu.c
> separate from the original, otherwise it would get very confusing when
> people works on their changes.

IHMO, it makes more difficult to review and work with it. Although it
may be easier for porting change from Linux in the future.

Regards,

-- 
Julien Grall

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

* Re: [PATCH v2 10/12] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices
  2015-01-27 17:02       ` Stefano Stabellini
@ 2015-01-27 17:33         ` Julien Grall
  2015-01-27 17:36           ` Stefano Stabellini
  2015-01-27 17:44           ` Ian Campbell
  0 siblings, 2 replies; 51+ messages in thread
From: Julien Grall @ 2015-01-27 17:33 UTC (permalink / raw)
  To: Stefano Stabellini
  Cc: ian.campbell, tim, Andreas Herrmann, stefano.stabellini,
	Andreas Herrmann, xen-devel

On 27/01/15 17:02, Stefano Stabellini wrote:
> On Tue, 27 Jan 2015, Julien Grall wrote:
>> On 27/01/15 16:30, Stefano Stabellini wrote:
>>> On Fri, 16 Jan 2015, Julien Grall wrote:
>>>> 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>
>>>
>>> Why didn't you just take a more recent version of the Linux smmu driver?
>>
>> The SMMU driver very is recent (see commit in the previous patch)...
>> Just this patch has never reached upstream.
> 
> That is not good. It might be worth to wait for it to go upstream.

The patch was sent one year ago. Just before Calxeda was shutdown.

This is a requirement for the following patch. Do you think the other
patch should be upstream to Linux before? If so, Calxeda server won't be
able to use properly SMMU.

Even though the server will never be used, I do all my SMMU development
on it.

FWIW, we use already use the 2 patches in the current SMMU drivers.

Regards,

-- 
Julien Grall

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

* Re: [PATCH v2 12/12] xen/iommu: smmu: Add Xen specific code to be able to use the driver
  2015-01-27 17:05     ` Julien Grall
@ 2015-01-27 17:34       ` Stefano Stabellini
  2015-01-28 10:31         ` Ian Campbell
  0 siblings, 1 reply; 51+ messages in thread
From: Stefano Stabellini @ 2015-01-27 17:34 UTC (permalink / raw)
  To: Julien Grall
  Cc: xen-devel, tim, ian.campbell, stefano.stabellini, Stefano Stabellini

On Tue, 27 Jan 2015, Julien Grall wrote:
> On 27/01/15 16:46, Stefano Stabellini wrote:
> > On Fri, 16 Jan 2015, 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.
> > 
> > Agreed.
> > 
> > 
> >> To achieve that we:
> >>     - Add helpers to Linux function not implemented on Xen
> > 
> > Good idea, I would take it further and move the helpers to a separate
> > file that smmu.c #includes. Or it could work the other way around: you
> > could move the original code to smmu-linux.c that get #included into
> > smmu.c
> 
> We would have to modify smmu-linux.c for disabling some functions and/or
> modify them.
> 
> Although for the later, there is only a couple functions modified.
> 
> > 
> >>     - 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.
> > 
> > I wonder if it makes sense to keep your changes in patch format and
> > apply the patch at run time as part of the make system.
> > 
> > Of course you would need to be careful to keep the patched smmu.c
> > separate from the original, otherwise it would get very confusing when
> > people works on their changes.
> 
> IHMO, it makes more difficult to review and work with it. Although it
> may be easier for porting change from Linux in the future.

Yes, the reason to do that would be to make it as easy as possible to
update it in the future.  Ian, do you have an opinion on this?

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

* Re: [PATCH v2 10/12] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices
  2015-01-27 17:33         ` Julien Grall
@ 2015-01-27 17:36           ` Stefano Stabellini
  2015-01-27 17:44           ` Ian Campbell
  1 sibling, 0 replies; 51+ messages in thread
From: Stefano Stabellini @ 2015-01-27 17:36 UTC (permalink / raw)
  To: Julien Grall
  Cc: ian.campbell, Stefano Stabellini, Andreas Herrmann, tim,
	stefano.stabellini, Andreas Herrmann, xen-devel

On Tue, 27 Jan 2015, Julien Grall wrote:
> On 27/01/15 17:02, Stefano Stabellini wrote:
> > On Tue, 27 Jan 2015, Julien Grall wrote:
> >> On 27/01/15 16:30, Stefano Stabellini wrote:
> >>> On Fri, 16 Jan 2015, Julien Grall wrote:
> >>>> 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>
> >>>
> >>> Why didn't you just take a more recent version of the Linux smmu driver?
> >>
> >> The SMMU driver very is recent (see commit in the previous patch)...
> >> Just this patch has never reached upstream.
> > 
> > That is not good. It might be worth to wait for it to go upstream.
> 
> The patch was sent one year ago. Just before Calxeda was shutdown.
> 
> This is a requirement for the following patch. Do you think the other
> patch should be upstream to Linux before? If so, Calxeda server won't be
> able to use properly SMMU.
> 
> Even though the server will never be used, I do all my SMMU development
> on it.
> 
> FWIW, we use already use the 2 patches in the current SMMU drivers.

Ah, that's too bad.  In that case we can make an exception.
It might be worth reposting the patches to linux-arm too.

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

* Re: [PATCH v2 10/12] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices
  2015-01-27 17:33         ` Julien Grall
  2015-01-27 17:36           ` Stefano Stabellini
@ 2015-01-27 17:44           ` Ian Campbell
  2015-01-27 17:51             ` Julien Grall
  1 sibling, 1 reply; 51+ messages in thread
From: Ian Campbell @ 2015-01-27 17:44 UTC (permalink / raw)
  To: Julien Grall
  Cc: Stefano Stabellini, tim, Andreas Herrmann, stefano.stabellini,
	Andreas Herrmann, xen-devel

On Tue, 2015-01-27 at 17:33 +0000, Julien Grall wrote:
> On 27/01/15 17:02, Stefano Stabellini wrote:
> > On Tue, 27 Jan 2015, Julien Grall wrote:
> >> On 27/01/15 16:30, Stefano Stabellini wrote:
> >>> On Fri, 16 Jan 2015, Julien Grall wrote:
> >>>> 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>
> >>>
> >>> Why didn't you just take a more recent version of the Linux smmu driver?
> >>
> >> The SMMU driver very is recent (see commit in the previous patch)...
> >> Just this patch has never reached upstream.
> > 
> > That is not good. It might be worth to wait for it to go upstream.
> 
> The patch was sent one year ago. Just before Calxeda was shutdown.
> 
> This is a requirement for the following patch. Do you think the other
> patch should be upstream to Linux before?

In general we should be reluctant to diverge over these things, since it
just ends up making things harder for us in the future (e.g. the next
resync or the one after).

Is the issue being fixed here specific to Calxeda? e.g. is duplicate
stream ids a h/w bug which has yet to be observed elsewhere or is it a
valid configuration per the h/w specs which it just happens only Calxeda
implemented?

Unless it's a h/w bug I don't see why it couldn't go upstream first.
Even if it is it might still stand a chance.

> If so, Calxeda server won't be able to use properly SMMU.
> 
> Even though the server will never be used, I do all my SMMU development
> on it.

The fact that Calxeda are no longer around doesn't in itself preclude
getting these patches upstreamed. After all, no one is deleting CX
support and some people do have them (not just us). It's hardly the most
obscure platform supported by Linux...

Ian.

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

* Re: [PATCH v2 10/12] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices
  2015-01-27 17:44           ` Ian Campbell
@ 2015-01-27 17:51             ` Julien Grall
  2015-01-27 20:54               ` Andreas Herrmann
  2015-01-28 10:38               ` Ian Campbell
  0 siblings, 2 replies; 51+ messages in thread
From: Julien Grall @ 2015-01-27 17:51 UTC (permalink / raw)
  To: Ian Campbell
  Cc: Stefano Stabellini, tim, Andreas Herrmann, stefano.stabellini,
	Andreas Herrmann, xen-devel

On 27/01/15 17:44, Ian Campbell wrote:
> On Tue, 2015-01-27 at 17:33 +0000, Julien Grall wrote:
>> On 27/01/15 17:02, Stefano Stabellini wrote:
>>> On Tue, 27 Jan 2015, Julien Grall wrote:
>>>> On 27/01/15 16:30, Stefano Stabellini wrote:
>>>>> On Fri, 16 Jan 2015, Julien Grall wrote:
>>>>>> 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>
>>>>>
>>>>> Why didn't you just take a more recent version of the Linux smmu driver?
>>>>
>>>> The SMMU driver very is recent (see commit in the previous patch)...
>>>> Just this patch has never reached upstream.
>>>
>>> That is not good. It might be worth to wait for it to go upstream.
>>
>> The patch was sent one year ago. Just before Calxeda was shutdown.
>>
>> This is a requirement for the following patch. Do you think the other
>> patch should be upstream to Linux before?
> 
> In general we should be reluctant to diverge over these things, since it
> just ends up making things harder for us in the future (e.g. the next
> resync or the one after).
> 
> Is the issue being fixed here specific to Calxeda? e.g. is duplicate
> stream ids a h/w bug which has yet to be observed elsewhere or is it a
> valid configuration per the h/w specs which it just happens only Calxeda
> implemented?

It's for catching possible error in the device tree. If the ID is
duplicated the multi match algo in the following patch may not work
correctly.

> 
> Unless it's a h/w bug I don't see why it couldn't go upstream first.
> Even if it is it might still stand a chance.

Because nobody take care to upstream it (see why below).

>> If so, Calxeda server won't be able to use properly SMMU.
>>
>> Even though the server will never be used, I do all my SMMU development
>> on it.
> 
> The fact that Calxeda are no longer around doesn't in itself preclude
> getting these patches upstreamed. After all, no one is deleting CX
> support and some people do have them (not just us). It's hardly the most
> obscure platform supported by Linux...

By default Linux is by-passing the stream ID and the device is not using
the SMMU.

You will have to use either passthrough or manually said : "I want to
enforce this device". And then you will see the error. So by default
both patches are not necessary.

On Xen, we can't by-pass the SMMU by default. At least it's not the way
it should work.

Regards,

-- 
Julien Grall

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

* Re: [PATCH v2 10/12] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices
  2015-01-27 17:51             ` Julien Grall
@ 2015-01-27 20:54               ` Andreas Herrmann
  2015-01-28 10:38               ` Ian Campbell
  1 sibling, 0 replies; 51+ messages in thread
From: Andreas Herrmann @ 2015-01-27 20:54 UTC (permalink / raw)
  To: Julien Grall
  Cc: Ian Campbell, Stefano Stabellini, tim, stefano.stabellini,
	Andreas Herrmann, xen-devel

FYI, I am not actively working on the SMMU driver anymore.


Andreas

--
Pawns in the Game (W.G. Carr).

Think twice: People addicted to media (all?) who believe w/o
questioning all personalized information presented to them, are
massively manipulable via social media and designed plots might become
self-fulfilling prophecies.

--

On Tue, Jan 27, 2015 at 05:51:52PM +0000, Julien Grall wrote:
> On 27/01/15 17:44, Ian Campbell wrote:
> > On Tue, 2015-01-27 at 17:33 +0000, Julien Grall wrote:
> >> On 27/01/15 17:02, Stefano Stabellini wrote:
> >>> On Tue, 27 Jan 2015, Julien Grall wrote:
> >>>> On 27/01/15 16:30, Stefano Stabellini wrote:
> >>>>> On Fri, 16 Jan 2015, Julien Grall wrote:
> >>>>>> 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>
> >>>>>
> >>>>> Why didn't you just take a more recent version of the Linux smmu driver?
> >>>>
> >>>> The SMMU driver very is recent (see commit in the previous patch)...
> >>>> Just this patch has never reached upstream.
> >>>
> >>> That is not good. It might be worth to wait for it to go upstream.
> >>
> >> The patch was sent one year ago. Just before Calxeda was shutdown.
> >>
> >> This is a requirement for the following patch. Do you think the other
> >> patch should be upstream to Linux before?
> > 
> > In general we should be reluctant to diverge over these things, since it
> > just ends up making things harder for us in the future (e.g. the next
> > resync or the one after).
> > 
> > Is the issue being fixed here specific to Calxeda? e.g. is duplicate
> > stream ids a h/w bug which has yet to be observed elsewhere or is it a
> > valid configuration per the h/w specs which it just happens only Calxeda
> > implemented?
> 
> It's for catching possible error in the device tree. If the ID is
> duplicated the multi match algo in the following patch may not work
> correctly.
> 
> > 
> > Unless it's a h/w bug I don't see why it couldn't go upstream first.
> > Even if it is it might still stand a chance.
> 
> Because nobody take care to upstream it (see why below).
> 
> >> If so, Calxeda server won't be able to use properly SMMU.
> >>
> >> Even though the server will never be used, I do all my SMMU development
> >> on it.
> > 
> > The fact that Calxeda are no longer around doesn't in itself preclude
> > getting these patches upstreamed. After all, no one is deleting CX
> > support and some people do have them (not just us). It's hardly the most
> > obscure platform supported by Linux...
> 
> By default Linux is by-passing the stream ID and the device is not using
> the SMMU.
> 
> You will have to use either passthrough or manually said : "I want to
> enforce this device". And then you will see the error. So by default
> both patches are not necessary.
> 
> On Xen, we can't by-pass the SMMU by default. At least it's not the way
> it should work.
> 
> Regards,
> 
> -- 
> Julien Grall

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

* Re: [PATCH v2 04/12] xen/arm: device: Rename device_type into device_match
  2015-01-27 16:13       ` Stefano Stabellini
@ 2015-01-27 22:26         ` Julien Grall
  0 siblings, 0 replies; 51+ messages in thread
From: Julien Grall @ 2015-01-27 22:26 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: xen-devel, tim, ian.campbell, stefano.stabellini



On 27/01/2015 16:13, Stefano Stabellini wrote:
> On Tue, 27 Jan 2015, Julien Grall wrote:
>> Hi Stefano,
>>
>> On 27/01/15 16:05, Stefano Stabellini wrote:
>>> On Fri, 16 Jan 2015, Julien Grall wrote:
>>>> 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>
>>>
>>> If you really want to rename this field, please find a more descriptive
>>> name for it. device_match doesn't really tell me what we are trying to
>>> match against.  At least device_type would tell me that we are matching
>>> against the type.
>>
>> I was struggling to find a correct name. For me device_type refers to
>> PCI/platform device. And it will be used this way later.
>>
>> Maybe device_category?
>
> Yes, or device_class

device_class seems better. I will use it in the next version.

Regards,


-- 
Julien Grall

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

* Re: [PATCH v2 12/12] xen/iommu: smmu: Add Xen specific code to be able to use the driver
  2015-01-27 17:34       ` Stefano Stabellini
@ 2015-01-28 10:31         ` Ian Campbell
  2015-01-28 11:49           ` Julien Grall
  0 siblings, 1 reply; 51+ messages in thread
From: Ian Campbell @ 2015-01-28 10:31 UTC (permalink / raw)
  To: Stefano Stabellini; +Cc: xen-devel, Julien Grall, tim, stefano.stabellini

On Tue, 2015-01-27 at 17:34 +0000, Stefano Stabellini wrote:
> On Tue, 27 Jan 2015, Julien Grall wrote:
> > On 27/01/15 16:46, Stefano Stabellini wrote:
> > > On Fri, 16 Jan 2015, 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.
> > > 
> > > Agreed.
> > > 
> > > 
> > >> To achieve that we:
> > >>     - Add helpers to Linux function not implemented on Xen
> > > 
> > > Good idea, I would take it further and move the helpers to a separate
> > > file that smmu.c #includes. Or it could work the other way around: you
> > > could move the original code to smmu-linux.c that get #included into
> > > smmu.c
> > 
> > We would have to modify smmu-linux.c for disabling some functions and/or
> > modify them.
> > 
> > Although for the later, there is only a couple functions modified.
> > 
> > > 
> > >>     - 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.
> > > 
> > > I wonder if it makes sense to keep your changes in patch format and
> > > apply the patch at run time as part of the make system.
> > > 
> > > Of course you would need to be careful to keep the patched smmu.c
> > > separate from the original, otherwise it would get very confusing when
> > > people works on their changes.
> > 
> > IHMO, it makes more difficult to review and work with it. Although it
> > may be easier for porting change from Linux in the future.
> 
> Yes, the reason to do that would be to make it as easy as possible to
> update it in the future.  Ian, do you have an opinion on this?

Build time application of patches is a nightmare, we should definitely
not do that.

IMHO the best way to approach this sort of thing is to separate it from
the main body of imported code as much as possible, i.e. by patching in
a minimal set of hooks the implementations of which can be segregated
away from the imported code, even if that makes for a slightly less
clean design overall.

Looking briefly at the patch:
      * Can things like supporting larger address spaces not be
        upstreamed?
      * Can we not avoid the #if 0 in many cases by just leaving them
        and making it so the compiler can statically eliminate the call
        (i.e. with expressions which expand to if(0)?
      * The fixups for differing configurations could be done all at
        once in a xen hook called at the end of arm_smmu_device_reset
        which applies our delta (overriding what the upstream code had
        done)
      * etc

Ian.

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

* Re: [PATCH v2 10/12] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices
  2015-01-27 17:51             ` Julien Grall
  2015-01-27 20:54               ` Andreas Herrmann
@ 2015-01-28 10:38               ` Ian Campbell
  2015-01-28 11:39                 ` Julien Grall
  1 sibling, 1 reply; 51+ messages in thread
From: Ian Campbell @ 2015-01-28 10:38 UTC (permalink / raw)
  To: Julien Grall
  Cc: Stefano Stabellini, tim, Andreas Herrmann, stefano.stabellini,
	Andreas Herrmann, xen-devel

On Tue, 2015-01-27 at 17:51 +0000, Julien Grall wrote:
> On 27/01/15 17:44, Ian Campbell wrote:
> > On Tue, 2015-01-27 at 17:33 +0000, Julien Grall wrote:
> >> On 27/01/15 17:02, Stefano Stabellini wrote:
> >>> On Tue, 27 Jan 2015, Julien Grall wrote:
> >>>> On 27/01/15 16:30, Stefano Stabellini wrote:
> >>>>> On Fri, 16 Jan 2015, Julien Grall wrote:
> >>>>>> 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>
> >>>>>
> >>>>> Why didn't you just take a more recent version of the Linux smmu driver?
> >>>>
> >>>> The SMMU driver very is recent (see commit in the previous patch)...
> >>>> Just this patch has never reached upstream.
> >>>
> >>> That is not good. It might be worth to wait for it to go upstream.
> >>
> >> The patch was sent one year ago. Just before Calxeda was shutdown.
> >>
> >> This is a requirement for the following patch. Do you think the other
> >> patch should be upstream to Linux before?
> > 
> > In general we should be reluctant to diverge over these things, since it
> > just ends up making things harder for us in the future (e.g. the next
> > resync or the one after).
> > 
> > Is the issue being fixed here specific to Calxeda? e.g. is duplicate
> > stream ids a h/w bug which has yet to be observed elsewhere or is it a
> > valid configuration per the h/w specs which it just happens only Calxeda
> > implemented?
> 
> It's for catching possible error in the device tree.

So Calxeda h/w was buggy and provided a DT with duplicate IDs? Or does
the hardware actually use duplicate IDs?

Can we fix this up in xen/arch/arm/platforms/midway.c somehow, perhaps
with a new quirk or hook (e.g. ->fixup_dtb) instead of patching the
imported code. Or by doing a post-hoc fixup after we init the smmu, i.e.
out of line from the imported code.

>  If the ID is
> duplicated the multi match algo in the following patch may not work
> correctly.

Are either of these situations (duplicates in DT or in h/w) actually
improper according to the spec? If not then the multi match algo ought
to cope somehow, even if only by detecting and ignoring.

> > Unless it's a h/w bug I don't see why it couldn't go upstream first.
> > Even if it is it might still stand a chance.
> 
> Because nobody take care to upstream it (see why below).

I'm afraid I'm none the wiser having read below...

> >> If so, Calxeda server won't be able to use properly SMMU.
> >>
> >> Even though the server will never be used, I do all my SMMU development
> >> on it.
> > 
> > The fact that Calxeda are no longer around doesn't in itself preclude
> > getting these patches upstreamed. After all, no one is deleting CX
> > support and some people do have them (not just us). It's hardly the most
> > obscure platform supported by Linux...
> 
> By default Linux is by-passing the stream ID and the device is not using
> the SMMU.
> 
> You will have to use either passthrough or manually said : "I want to
> enforce this device". And then you will see the error. So by default
> both patches are not necessary.

Are you talking about under Xen or under Linux here?

If Linux then the fact that you need to use passthrough or take other
manual action to expose the issue doesn't invalidate the possibility of
upstreaming the fix.

Ian.

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

* Re: [PATCH v2 10/12] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices
  2015-01-28 10:38               ` Ian Campbell
@ 2015-01-28 11:39                 ` Julien Grall
  0 siblings, 0 replies; 51+ messages in thread
From: Julien Grall @ 2015-01-28 11:39 UTC (permalink / raw)
  To: Ian Campbell
  Cc: Stefano Stabellini, tim, Andreas Herrmann, stefano.stabellini,
	Andreas Herrmann, xen-devel

On 28/01/15 10:38, Ian Campbell wrote:
> On Tue, 2015-01-27 at 17:51 +0000, Julien Grall wrote:
>> On 27/01/15 17:44, Ian Campbell wrote:
>>> On Tue, 2015-01-27 at 17:33 +0000, Julien Grall wrote:
>>>> On 27/01/15 17:02, Stefano Stabellini wrote:
>>>>> On Tue, 27 Jan 2015, Julien Grall wrote:
>>>>>> On 27/01/15 16:30, Stefano Stabellini wrote:
>>>>>>> On Fri, 16 Jan 2015, Julien Grall wrote:
>>>>>>>> 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>
>>>>>>>
>>>>>>> Why didn't you just take a more recent version of the Linux smmu driver?
>>>>>>
>>>>>> The SMMU driver very is recent (see commit in the previous patch)...
>>>>>> Just this patch has never reached upstream.
>>>>>
>>>>> That is not good. It might be worth to wait for it to go upstream.
>>>>
>>>> The patch was sent one year ago. Just before Calxeda was shutdown.
>>>>
>>>> This is a requirement for the following patch. Do you think the other
>>>> patch should be upstream to Linux before?
>>>
>>> In general we should be reluctant to diverge over these things, since it
>>> just ends up making things harder for us in the future (e.g. the next
>>> resync or the one after).
>>>
>>> Is the issue being fixed here specific to Calxeda? e.g. is duplicate
>>> stream ids a h/w bug which has yet to be observed elsewhere or is it a
>>> valid configuration per the h/w specs which it just happens only Calxeda
>>> implemented?
>>
>> It's for catching possible error in the device tree.
> 
> So Calxeda h/w was buggy and provided a DT with duplicate IDs? Or does
> the hardware actually use duplicate IDs?

No, it's a safe guard for the algorithm.

> Can we fix this up in xen/arch/arm/platforms/midway.c somehow, perhaps
> with a new quirk or hook (e.g. ->fixup_dtb) instead of patching the
> imported code. Or by doing a post-hoc fixup after we init the smmu, i.e.
> out of line from the imported code.
> 
>>  If the ID is
>> duplicated the multi match algo in the following patch may not work
>> correctly.
> 
> Are either of these situations (duplicates in DT or in h/w) actually
> improper according to the spec? If not then the multi match algo ought
> to cope somehow, even if only by detecting and ignoring.

It would not be possible in the h/w. Because one streamid is assigned to
specific device.

But, the developer made have written by mistake twice the streamid for
the device.

>>> Unless it's a h/w bug I don't see why it couldn't go upstream first.
>>> Even if it is it might still stand a chance.
>>
>> Because nobody take care to upstream it (see why below).
> 
> I'm afraid I'm none the wiser having read below...
> 
>>>> If so, Calxeda server won't be able to use properly SMMU.
>>>>
>>>> Even though the server will never be used, I do all my SMMU development
>>>> on it.
>>>
>>> The fact that Calxeda are no longer around doesn't in itself preclude
>>> getting these patches upstreamed. After all, no one is deleting CX
>>> support and some people do have them (not just us). It's hardly the most
>>> obscure platform supported by Linux...
>>
>> By default Linux is by-passing the stream ID and the device is not using
>> the SMMU.
>>
>> You will have to use either passthrough or manually said : "I want to
>> enforce this device". And then you will see the error. So by default
>> both patches are not necessary.
> 
> Are you talking about under Xen or under Linux here?

I was talking about Linux.

> If Linux then the fact that you need to use passthrough or take other
> manual action to expose the issue doesn't invalidate the possibility of
> upstreaming the fix.

This patch is not strictly necessary, I could drop if you don't want it.

But the following patch is required to use the SATA with SMMU. It has
more stream IDs than stream matching register.

So Linux would have a problem when enforcing the device and/or use
passthrough for SATA.

Honestly, the patches has been for a long time on the mailing list and
nobody really care about it for now. So I don't have any plan to spend
time on upstreaming patch in Linux and it's not required for our use case.

Regards,

-- 
Julien Grall

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

* Re: [PATCH v2 12/12] xen/iommu: smmu: Add Xen specific code to be able to use the driver
  2015-01-28 10:31         ` Ian Campbell
@ 2015-01-28 11:49           ` Julien Grall
  2015-01-28 12:13             ` Ian Campbell
  2015-01-28 12:15             ` Stefano Stabellini
  0 siblings, 2 replies; 51+ messages in thread
From: Julien Grall @ 2015-01-28 11:49 UTC (permalink / raw)
  To: Ian Campbell, Stefano Stabellini; +Cc: xen-devel, tim, stefano.stabellini

On 28/01/15 10:31, Ian Campbell wrote:
> On Tue, 2015-01-27 at 17:34 +0000, Stefano Stabellini wrote:
>> On Tue, 27 Jan 2015, Julien Grall wrote:
>>> On 27/01/15 16:46, Stefano Stabellini wrote:
>>>> On Fri, 16 Jan 2015, 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.
>>>>
>>>> Agreed.
>>>>
>>>>
>>>>> To achieve that we:
>>>>>     - Add helpers to Linux function not implemented on Xen
>>>>
>>>> Good idea, I would take it further and move the helpers to a separate
>>>> file that smmu.c #includes. Or it could work the other way around: you
>>>> could move the original code to smmu-linux.c that get #included into
>>>> smmu.c
>>>
>>> We would have to modify smmu-linux.c for disabling some functions and/or
>>> modify them.
>>>
>>> Although for the later, there is only a couple functions modified.
>>>
>>>>
>>>>>     - 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.
>>>>
>>>> I wonder if it makes sense to keep your changes in patch format and
>>>> apply the patch at run time as part of the make system.
>>>>
>>>> Of course you would need to be careful to keep the patched smmu.c
>>>> separate from the original, otherwise it would get very confusing when
>>>> people works on their changes.
>>>
>>> IHMO, it makes more difficult to review and work with it. Although it
>>> may be easier for porting change from Linux in the future.
>>
>> Yes, the reason to do that would be to make it as easy as possible to
>> update it in the future.  Ian, do you have an opinion on this?
> 
> Build time application of patches is a nightmare, we should definitely
> not do that.

I though about it during the night. It would be the best approach to
keep in sync with Linux more quickly.

It would also made clear what are our modifications and what was
upstreamed in Linux (assuming someone want to port/test our changes).

> IMHO the best way to approach this sort of thing is to separate it from
> the main body of imported code as much as possible, i.e. by patching in
> a minimal set of hooks the implementations of which can be segregated
> away from the imported code, even if that makes for a slightly less
> clean design overall.

It's already the case, if you look the code. The number of modifications
in the Linux code is very tiny compare to the number of changes (about
50 lines changes).

> Looking briefly at the patch:
>       * Can things like supporting larger address spaces not be
>         upstreamed?

I'm not sure which one you are talking about. But Linux is use separate
page table by device. Until now they were able to support only a certain
number of address bits.

I know they are reworking the SMMU drivers for 3.20. I haven't yet take
a look to it.


>       * Can we not avoid the #if 0 in many cases by just leaving them
>         and making it so the compiler can statically eliminate the call
>         (i.e. with expressions which expand to if(0)?

No, most of this code is self-contained in callbacks that we don't use.

>       * The fixups for differing configurations could be done all at
>         once in a xen hook called at the end of arm_smmu_device_reset
>         which applies our delta (overriding what the upstream code had
>         done)

No, because we may for a slight moment allow the device to bypass the
SMMU and therefore mess up the real memory.

>       * etc

I believe the most of my changes in the Linux code are justified.

There is a certain limit about using Linux drivers. They are moving fast
(at least on the ARM IOMMU sides). It will never be possible to have an
nearly exact (99%) match with Linux.

Regards,

-- 
Julien Grall

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

* Re: [PATCH v2 12/12] xen/iommu: smmu: Add Xen specific code to be able to use the driver
  2015-01-28 11:49           ` Julien Grall
@ 2015-01-28 12:13             ` Ian Campbell
  2015-01-28 12:15             ` Stefano Stabellini
  1 sibling, 0 replies; 51+ messages in thread
From: Ian Campbell @ 2015-01-28 12:13 UTC (permalink / raw)
  To: Julien Grall; +Cc: xen-devel, tim, stefano.stabellini, Stefano Stabellini

On Wed, 2015-01-28 at 11:49 +0000, Julien Grall wrote:
> > Build time application of patches is a nightmare, we should definitely
> > not do that.
> 
> I though about it during the night. It would be the best approach to
> keep in sync with Linux more quickly.
> 
> It would also made clear what are our modifications and what was
> upstreamed in Linux (assuming someone want to port/test our changes).

Just to be completely clear: I will nack any patches which try and apply
patches to the hypervisor at build time. It's not happening, sorry.

Ian.

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

* Re: [PATCH v2 12/12] xen/iommu: smmu: Add Xen specific code to be able to use the driver
  2015-01-28 11:49           ` Julien Grall
  2015-01-28 12:13             ` Ian Campbell
@ 2015-01-28 12:15             ` Stefano Stabellini
  1 sibling, 0 replies; 51+ messages in thread
From: Stefano Stabellini @ 2015-01-28 12:15 UTC (permalink / raw)
  To: Julien Grall
  Cc: xen-devel, tim, Ian Campbell, stefano.stabellini, Stefano Stabellini

On Wed, 28 Jan 2015, Julien Grall wrote:
> On 28/01/15 10:31, Ian Campbell wrote:
> > On Tue, 2015-01-27 at 17:34 +0000, Stefano Stabellini wrote:
> >> On Tue, 27 Jan 2015, Julien Grall wrote:
> >>> On 27/01/15 16:46, Stefano Stabellini wrote:
> >>>> On Fri, 16 Jan 2015, 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.
> >>>>
> >>>> Agreed.
> >>>>
> >>>>
> >>>>> To achieve that we:
> >>>>>     - Add helpers to Linux function not implemented on Xen
> >>>>
> >>>> Good idea, I would take it further and move the helpers to a separate
> >>>> file that smmu.c #includes. Or it could work the other way around: you
> >>>> could move the original code to smmu-linux.c that get #included into
> >>>> smmu.c
> >>>
> >>> We would have to modify smmu-linux.c for disabling some functions and/or
> >>> modify them.
> >>>
> >>> Although for the later, there is only a couple functions modified.
> >>>
> >>>>
> >>>>>     - 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.
> >>>>
> >>>> I wonder if it makes sense to keep your changes in patch format and
> >>>> apply the patch at run time as part of the make system.
> >>>>
> >>>> Of course you would need to be careful to keep the patched smmu.c
> >>>> separate from the original, otherwise it would get very confusing when
> >>>> people works on their changes.
> >>>
> >>> IHMO, it makes more difficult to review and work with it. Although it
> >>> may be easier for porting change from Linux in the future.
> >>
> >> Yes, the reason to do that would be to make it as easy as possible to
> >> update it in the future.  Ian, do you have an opinion on this?
> > 
> > Build time application of patches is a nightmare, we should definitely
> > not do that.
> 
> I though about it during the night. It would be the best approach to
> keep in sync with Linux more quickly.
> 
> It would also made clear what are our modifications and what was
> upstreamed in Linux (assuming someone want to port/test our changes).

I agree.  Anybody could upgrade the smmu driver at any time, if we did
it that way.

I don't think that it would be a nightmare, if we think carefully about
the changes to the build system and we make sure that the patched driver
is updated appropriately.

In fact in xen-unstable we already do this with the current stubdom
patches. The stubdom build system is certainly not a great example to
follow, but it is not the patching that causes problems.

Of course the patch (if any) should be a small as possible following the
same principles that Ian wrote below.


> > IMHO the best way to approach this sort of thing is to separate it from
> > the main body of imported code as much as possible, i.e. by patching in
> > a minimal set of hooks the implementations of which can be segregated
> > away from the imported code, even if that makes for a slightly less
> > clean design overall.
> 
> It's already the case, if you look the code. The number of modifications
> in the Linux code is very tiny compare to the number of changes (about
> 50 lines changes).
> 
> > Looking briefly at the patch:
> >       * Can things like supporting larger address spaces not be
> >         upstreamed?
> 
> I'm not sure which one you are talking about. But Linux is use separate
> page table by device. Until now they were able to support only a certain
> number of address bits.
> 
> I know they are reworking the SMMU drivers for 3.20. I haven't yet take
> a look to it.
> 
> 
> >       * Can we not avoid the #if 0 in many cases by just leaving them
> >         and making it so the compiler can statically eliminate the call
> >         (i.e. with expressions which expand to if(0)?
> 
> No, most of this code is self-contained in callbacks that we don't use.
> 
> >       * The fixups for differing configurations could be done all at
> >         once in a xen hook called at the end of arm_smmu_device_reset
> >         which applies our delta (overriding what the upstream code had
> >         done)
> 
> No, because we may for a slight moment allow the device to bypass the
> SMMU and therefore mess up the real memory.
> 
> >       * etc
> 
> I believe the most of my changes in the Linux code are justified.
> 
> There is a certain limit about using Linux drivers. They are moving fast
> (at least on the ARM IOMMU sides). It will never be possible to have an
> nearly exact (99%) match with Linux.
> 
> Regards,
> 
> -- 
> Julien Grall
> 

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

end of thread, other threads:[~2015-01-28 12:16 UTC | newest]

Thread overview: 51+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-01-16 14:23 [PATCH v2 00/12] xen/arm: Resync the SMMU driver with the Linux one Julien Grall
2015-01-16 14:23 ` [PATCH v2 01/12] xen/arm: gic-v2: Change the device name in DT_DEVICE_START Julien Grall
2015-01-27 15:52   ` Stefano Stabellini
2015-01-16 14:23 ` [PATCH v2 02/12] xen/arm: vgic: Drop unecessary include asm/device.h Julien Grall
2015-01-27 15:53   ` Stefano Stabellini
2015-01-16 14:23 ` [PATCH v2 03/12] xen/dt: Extend dt_device_match to possibly store data Julien Grall
2015-01-27 15:57   ` Stefano Stabellini
2015-01-27 16:07     ` Julien Grall
2015-01-27 16:10       ` Stefano Stabellini
2015-01-27 16:35         ` Julien Grall
2015-01-27 16:46           ` Stefano Stabellini
2015-01-27 16:49             ` Julien Grall
2015-01-16 14:23 ` [PATCH v2 04/12] xen/arm: device: Rename device_type into device_match Julien Grall
2015-01-27 16:05   ` Stefano Stabellini
2015-01-27 16:09     ` Julien Grall
2015-01-27 16:13       ` Stefano Stabellini
2015-01-27 22:26         ` Julien Grall
2015-01-16 14:24 ` [PATCH v2 05/12] xen/iommu: arm: Remove temporary the SMMU driver Julien Grall
2015-01-27 16:05   ` Stefano Stabellini
2015-01-16 14:24 ` [PATCH v2 06/12] xen/arm: Introduce a generic way to describe device Julien Grall
2015-01-16 14:59   ` Jan Beulich
2015-01-16 15:01     ` Julien Grall
2015-01-27 16:13   ` Stefano Stabellini
2015-01-16 14:24 ` [PATCH v2 07/12] xen/iommu: Consolidate device assignment ops into a single set Julien Grall
2015-01-16 15:03   ` Jan Beulich
2015-01-27 16:16   ` Stefano Stabellini
2015-01-16 14:24 ` [PATCH v2 08/12] xen/arm: Describe device supported by a driver with dt_match_node Julien Grall
2015-01-27 16:28   ` Stefano Stabellini
2015-01-16 14:24 ` [PATCH v2 09/12] xen/iommu: arm: Import the SMMU driver from Linux Julien Grall
2015-01-27 16:28   ` Stefano Stabellini
2015-01-27 16:51     ` Julien Grall
2015-01-16 14:24 ` [PATCH v2 10/12] xen/iommu: smmu: Check for duplicate stream IDs when registering master devices Julien Grall
2015-01-27 16:30   ` Stefano Stabellini
2015-01-27 16:52     ` Julien Grall
2015-01-27 17:02       ` Stefano Stabellini
2015-01-27 17:33         ` Julien Grall
2015-01-27 17:36           ` Stefano Stabellini
2015-01-27 17:44           ` Ian Campbell
2015-01-27 17:51             ` Julien Grall
2015-01-27 20:54               ` Andreas Herrmann
2015-01-28 10:38               ` Ian Campbell
2015-01-28 11:39                 ` Julien Grall
2015-01-16 14:24 ` [PATCH v2 11/12] xen/iommu: smmu: Introduce automatic stream-id-masking Julien Grall
2015-01-16 14:24 ` [PATCH v2 12/12] xen/iommu: smmu: Add Xen specific code to be able to use the driver Julien Grall
2015-01-27 16:46   ` Stefano Stabellini
2015-01-27 17:05     ` Julien Grall
2015-01-27 17:34       ` Stefano Stabellini
2015-01-28 10:31         ` Ian Campbell
2015-01-28 11:49           ` Julien Grall
2015-01-28 12:13             ` Ian Campbell
2015-01-28 12:15             ` Stefano Stabellini

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.