Skip to contentSkip to conversation

SMMUv3 driver and the supporting framework

RFC In review Sameer Goel 4 participants 34 messages Sep 21, 2017 – Nov 27, 2017

lore
Share & tools
lore
#0 lore raw

v2 cover SMMUv3 driver and the supporting framework

This change incoporates most of the review comments from [1] and adds the proposed SMMUv3 driver.

List of changes:
- Introduce the iommu_fwspec implementation - No change from the last RFC
- IORT port from linux. The differences are as under:
* Modified the code for creating the SMMU devices. This code also initializes the discoverd SMMU devices.
* MSI code is left as is, but this code is untested.
* IORT node data parsing is delegated to the driver. Looking for comments
on enabling the code in IORT driver. This will need a standard resource object. (Direct port from Linux or a new define for Xen?)
* Assumptions on PCI IORT SMMU interaction. PCI assign device will call iort_iommu_configure to setup the streamids.Then it will call SMMU assign device with the right struct device argument.
- SMMUv3 port from Linux. The list of changes are as under:
* The Xen iommu_ops list is at parity with SMMUv2.
* There is generally no need for an IOMMU group, but have kept a dummy define for now.
* Have commented out the S1 translation code.
* MSI code is commented out.
* Page table ops are commented out as the driver shares the page tables with the cpu.
* The list of SMMU devices is maintained from the driver code.

Open questions:
- IORT regeneration for DOM0. I was hoping to get some update on [2].
- We also need a notification framework to get the Named node information from DSDT.
- Should we port over code for non-hsared page tables from the kernel or leverage [3].

[1] "[RFC 0/6] IORT support and introduce fwspec"
[2] "[Xen-devel] [RFC] [PATCH] arm-acpi: Hide SMMU from IORT for hardware domain"
[3] "Non-shared" IOMMU support on ARM"

Sameer Goel (7):
passthrough/arm: Modify SMMU driver to use generic device definition
arm64: Add definitions for fwnode_handle xen/passthrough/arm: Introduce iommu_fwspec
ACPI: arm: Support for IORT
acpi:arm64: Add support for parsing IORT table
Add verbatim copy of arm-smmu-v3.c from Linux
xen/iommu: smmu-v3: Add Xen specific code to enable the ported driver

 xen/arch/arm/setup.c                  |    3 +
 xen/drivers/acpi/Makefile             |    1 +
 xen/drivers/acpi/arm/Makefile         |    1 +
 xen/drivers/acpi/arm/iort.c           |  986 ++++++++++
 xen/drivers/passthrough/arm/Makefile  |    1 +
 xen/drivers/passthrough/arm/iommu.c   |   66 +
 xen/drivers/passthrough/arm/smmu-v3.c | 3412 +++++++++++++++++++++++++++++++++
 xen/drivers/passthrough/arm/smmu.c    |   13 +-
 xen/include/acpi/acpi_iort.h          |   61 +
 xen/include/asm-arm/device.h          |    5 +
 xen/include/xen/acpi.h                |   21 +
 xen/include/xen/fwnode.h              |   33 +
 xen/include/xen/iommu.h               |   29 +
 xen/include/xen/pci.h                 |    8 +
 14 files changed, 4634 insertions(+), 6 deletions(-)
 create mode 100644 xen/drivers/acpi/arm/Makefile
 create mode 100644 xen/drivers/acpi/arm/iort.c
 create mode 100644 xen/drivers/passthrough/arm/smmu-v3.c
 create mode 100644 xen/include/acpi/acpi_iort.h
 create mode 100644 xen/include/xen/fwnode.h
Signature · 7 lines
Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.


_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#1 lore raw

RFC v2 1/7 passthrough/arm: Modify SMMU driver to use generic device definition

Modify the SMMU code to use generic device instead of dt_device_node for functions that can be used for ACPI based systems too.

---
 xen/drivers/passthrough/arm/smmu.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
xen/drivers/passthrough/arm/smmu.c +6 -6
index 74c09b0..362d578 100644
@@ -76,7 +76,7 @@ struct resource
 
 #define resource_size(res) (res)->size;
 
-#define platform_device dt_device_node
+#define platform_device device
 
 #define IORESOURCE_MEM 0
 #define IORESOURCE_IRQ 1
@@ -97,12 +97,12 @@ static struct resource *platform_get_resource(struct platform_device *pdev,
 
 	switch (type) {
 	case IORESOURCE_MEM:
-		ret = dt_device_get_address(pdev, num, &res.addr, &res.size);
+		ret = dt_device_get_address(dev_to_dt(pdev), num, &res.addr, &res.size);
 
 		return ((ret) ? NULL : &res);
 
 	case IORESOURCE_IRQ:
-		ret = platform_get_irq(pdev, num);
+		ret = platform_get_irq(dev_to_dt(pdev), num);
 		if (ret < 0)
 			return NULL;
 
@@ -2285,7 +2285,7 @@ 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 device *dev = pdev;
 	struct rb_node *node;
 	struct of_phandle_args masterspec;
 	int num_irqs, i, err;
@@ -2338,7 +2338,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
 	}
 
 	for (i = 0; i < num_irqs; ++i) {
-		int irq = platform_get_irq(pdev, i);
+		int irq = platform_get_irq(dev_to_dt(pdev), i);
 
 		if (irq < 0) {
 			dev_err(dev, "failed to get irq index %d\n", i);
@@ -2819,7 +2819,7 @@ static __init int arm_smmu_dt_init(struct dt_device_node *dev,
 	 */
 	dt_device_set_used_by(dev, DOMID_XEN);
 
-	rc = arm_smmu_device_dt_probe(dev);
+	rc = arm_smmu_device_dt_probe(dt_to_dev(dev));
 	if (rc)
 		return rc;
 
Signature · 7 lines
Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.


_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#2 lore raw

RFC v2 2/7 arm64: Add definitions for fwnode_handle

In reply to Sameer Goel

This will be used as a device property to match the DMA capable devices with the associated SMMU. The header file is a port from linux. The code was changed to remove the types that were not needed for Xen.

Linux ChangeId:ce793486e23e: driver core / ACPI: Represent ACPI companions using fwnode_handle

---
 xen/include/asm-arm/device.h |  2 ++
 xen/include/xen/fwnode.h     | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 35 insertions(+)
 create mode 100644 xen/include/xen/fwnode.h
xen/include/asm-arm/device.h +2 -0
index 6734ae8..78c38fe 100644
@@ -2,6 +2,7 @@
 #define __ASM_ARM_DEVICE_H
 
 #include <xen/init.h>
+#include <xen/fwnode.h>
 
 enum device_type
 {
@@ -19,6 +20,7 @@ struct device
 #ifdef CONFIG_HAS_DEVICE_TREE
     struct dt_device_node *of_node; /* Used by drivers imported from Linux */
 #endif
+    struct fwnode_handle *fwnode; /*fw device node identifier */
     struct dev_archdata archdata;
 };
 
xen/include/xen/fwnode.h new file +33 -0
new file mode 100644
index 0000000..0fed958
@@ -0,0 +1,33 @@
+/*
+ * fwnode.h - Firmware device node object handle type definition.
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Author: Rafael J. Wysocki <[email protected]>
+ *
+ * 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.
+ *
+ * Ported from Linux include/linux/fwnode.h
+ *  => commit ce793486e23e0162a732c605189c8028e0910e86
+ *
+ * No functional Xen modifications.
+ */
+
+#ifndef __XEN_FWNODE_H_
+#define __XEN_FWNODE_H_
+
+enum fwnode_type {
+	FWNODE_INVALID = 0,
+	FWNODE_OF,
+	FWNODE_ACPI,
+	FWNODE_ACPI_STATIC,
+	FWNODE_IRQCHIP
+};
+
+struct fwnode_handle {
+	enum fwnode_type type;
+	struct fwnode_handle *secondary;
+};
+
+#endif
Signature · 7 lines
Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.


_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#3 lore raw

Re: [RFC v2 2/7] arm64: Add definitions for fwnode_handle

Hi,

Quoted 3 lines · Sameer Goel

This will be used as a device property to match the DMA capable devices with the associated SMMU. The header file is a port from linux. The code was changed to remove the types that were not needed for Xen.

I think you probably want a bit more context in the commit message about implement fwnode.h in common code.

Within this series, fwnode seems to only be used by Arm. So what would be the advantage to get that in xen/? Is it going to be used by x86 or taken advantage in common code?

Quoted 24 lines

Linux ChangeId:ce793486e23e: driver core / ACPI: Represent ACPI companions using fwnode_handle

---
  xen/include/asm-arm/device.h |  2 ++
  xen/include/xen/fwnode.h     | 33 +++++++++++++++++++++++++++++++++
  2 files changed, 35 insertions(+)

create mode 100644 xen/include/xen/fwnode.h

xen/include/asm-arm/device.h +1 -0
index 6734ae8..78c38fe 100644
@@ -2,6 +2,7 @@
  #define __ASM_ARM_DEVICE_H
 
  #include <xen/init.h>
+#include <xen/fwnode.h>
 
  enum device_type
  {

xen/include/asm-arm/device.h struct device · line 21

Quoted 2 lines
xen/include/asm-arm/device.h +0 -0
@@ -19,6 +20,7 @@ struct device
  #ifdef CONFIG_HAS_DEVICE_TREE
      struct dt_device_node *of_node; /* Used by drivers imported from Linux */

I was expecting a todo in the code after the discussion about leave of_node here.

xen/include/asm-arm/device.h struct device · line 23

Quoted 2 lines
xen/include/asm-arm/device.h +1 -0
  #endif
+    struct fwnode_handle *fwnode; /*fw device node identifier */

Space missing before "fw".

Quoted 26 lines
xen/include/asm-arm/device.h +0 -0
      struct dev_archdata archdata;
  };
 
xen/include/xen/fwnode.h new file +17 -0
new file mode 100644
index 0000000..0fed958
@@ -0,0 +1,33 @@
+/*
+ * fwnode.h - Firmware device node object handle type definition.
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Author: Rafael J. Wysocki <[email protected]>
+ *
+ * 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.
+ *
+ * Ported from Linux include/linux/fwnode.h
+ *  => commit ce793486e23e0162a732c605189c8028e0910e86
+ *
+ * No functional Xen modifications.
+ */
+
+#ifndef __XEN_FWNODE_H_

xen/include/xen/fwnode.h line 27

Quoted 10 lines
xen/include/xen/fwnode.h new file +10 -0
+#define __XEN_FWNODE_H_
+
+enum fwnode_type {
+	FWNODE_INVALID = 0,
+	FWNODE_OF,
+	FWNODE_ACPI,
+	FWNODE_ACPI_STATIC,
+	FWNODE_IRQCHIP
+};
+

Looking at Linux code, the fwnode_type already disappeared from Linux (see commit db3e50f3234b "device property: Get rid of struct fwnode_handle type field").

I understand the goal on using fwnode is to help porting drivers from Linux. So how much this has changed now?

Cheers,

Quoted 7 lines
xen/include/xen/fwnode.h new file +6 -0
+struct fwnode_handle {
+	enum fwnode_type type;
+	struct fwnode_handle *secondary;
+};
+
+#endif
Signature · 6 lines
Julien Grall

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#4 lore raw

Re: [RFC v2 2/7] arm64: Add definitions for fwnode_handle

Quoted 22 lines · Julien Grall

Hi,

Quoted 3 lines · Sameer Goel

This will be used as a device property to match the DMA capable devices with the associated SMMU. The header file is a port from linux. The code was changed to remove the types that were not needed for Xen.

I think you probably want a bit more context in the commit message about implement fwnode.h in common code.

Within this series, fwnode seems to only be used by Arm. So what would be the advantage to get that in xen/? Is it going to be used by x86 or taken advantage in common code?

Quoted 11 lines

Linux ChangeId:ce793486e23e: driver core / ACPI: Represent ACPI companions using fwnode_handle

---
  xen/include/asm-arm/device.h |  2 ++ xen/include/xen/fwnode.h     | 33 +++++++++++++++++++++++++++++++++

  2 files changed, 35 insertions(+)

  create mode 100644 xen/include/xen/fwnode.h

Quoted 18 lines
Quoted 12 lines
xen/include/asm-arm/device.h +1 -0
index 6734ae8..78c38fe 100644
@@ -2,6 +2,7 @@
   #define __ASM_ARM_DEVICE_H
     #include <xen/init.h>
+#include <xen/fwnode.h>

    enum device_type
  {

xen/include/asm-arm/device.h +0 -0
@@ -19,6 +20,7 @@ struct device
   #ifdef CONFIG_HAS_DEVICE_TREE
Quoted 1 line

      struct dt_device_node *of_node; /* Used by drivers imported from Linux */

I was expecting a todo in the code after the discussion about leave of_node here.

Quoted 2 lines

  #endif
+    struct fwnode_handle *fwnode; /*fw device node identifier */

The fwnode handle was provide a match cookie for the SMMUs and not much else. Even with this around we will need the dt info in the device node. I agree that this rolls up into fw spec and I can look at the code cleanup for the next patch.

Quoted 41 lines

Space missing before "fw".

Quoted 25 lines

      struct dev_archdata archdata;
  };
  diff --git a/xen/include/xen/fwnode.h b/xen/include/xen/fwnode.h new file mode 100644
index 0000000..0fed958

xen/include/xen/fwnode.h new file +17 -0
@@ -0,0 +1,33 @@
+/*
+ * fwnode.h - Firmware device node object handle type definition.
+ *
+ * Copyright (C) 2015, Intel Corporation
+ * Author: Rafael J. Wysocki <[email protected]>
+ *
+ * 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.
+ *
+ * Ported from Linux include/linux/fwnode.h
+ *  => commit ce793486e23e0162a732c605189c8028e0910e86
+ *
+ * No functional Xen modifications.
+ */
+
+#ifndef __XEN_FWNODE_H_

xen/include/xen/fwnode.h line 27

Quoted 10 lines
xen/include/xen/fwnode.h new file +10 -0
+#define __XEN_FWNODE_H_
+
+enum fwnode_type {
+    FWNODE_INVALID = 0,
+    FWNODE_OF,
+    FWNODE_ACPI,
+    FWNODE_ACPI_STATIC,
+    FWNODE_IRQCHIP
+};
+

Looking at Linux code, the fwnode_type already disappeared from Linux (see commit db3e50f3234b "device property: Get rid of struct fwnode_handle type field").

Quoted 1 line

I understand the goal on using fwnode is to help porting drivers from Linux. So how much this has changed now?

This was not very useful in any case. So, I will move over to the new version.

Quoted 11 lines

Cheers,

Quoted 7 lines
xen/include/xen/fwnode.h new file +6 -0
+struct fwnode_handle {
+    enum fwnode_type type;
+    struct fwnode_handle *secondary;
+};
+
+#endif
Signature · 6 lines
 Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#5 lore raw

Re: [RFC v2 2/7] arm64: Add definitions for fwnode_handle

Hi Sameer,

Quoted 37 lines · Sameer
Quoted 36 lines · Julien Grall
Quoted 3 lines · Sameer Goel

This will be used as a device property to match the DMA capable devices with the associated SMMU. The header file is a port from linux. The code was changed to remove the types that were not needed for Xen.

I think you probably want a bit more context in the commit message about implement fwnode.h in common code.

Within this series, fwnode seems to only be used by Arm. So what would be the advantage to get that in xen/? Is it going to be used by x86 or taken advantage in common code?

Quoted 22 lines

Linux ChangeId:ce793486e23e: driver core / ACPI: Represent ACPI companions using fwnode_handle

---
   xen/include/asm-arm/device.h |  2 ++
   xen/include/xen/fwnode.h     | 33 +++++++++++++++++++++++++++++++++
   2 files changed, 35 insertions(+)

  create mode 100644 xen/include/xen/fwnode.h

xen/include/asm-arm/device.h +1 -0
index 6734ae8..78c38fe 100644
@@ -2,6 +2,7 @@
   #define __ASM_ARM_DEVICE_H
     #include <xen/init.h>
+#include <xen/fwnode.h>
     enum device_type
   {

xen/include/asm-arm/device.h struct device · line 21

Quoted 2 lines
xen/include/asm-arm/device.h +0 -0
@@ -19,6 +20,7 @@ struct device
   #ifdef CONFIG_HAS_DEVICE_TREE
       struct dt_device_node *of_node; /* Used by drivers imported from Linux */

I was expecting a todo in the code after the discussion about leave of_node here.

Quoted 4 lines
Quoted 2 lines

xen/include/asm-arm/device.h struct device · line 23

Quoted 2 lines
xen/include/asm-arm/device.h +1 -0
   #endif
+    struct fwnode_handle *fwnode; /*fw device node identifier */

The fwnode handle was provide a match cookie for the SMMUs and not much else. Even with this around we will need the dt info in the device node. I agree that this rolls up into fw spec and I can look at the code cleanup for the next patch.

A clean-up patch would be great but not necessary. What I expect is a TODO mentioning the possible clean-up.

Cheers,

Signature · 6 lines
Julien Grall

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#6 lore raw

Re: [RFC v2 2/7] arm64: Add definitions for fwnode_handle

Quoted 40 lines · Julien Grall

Hi Sameer,

Quoted 37 lines · Sameer
Quoted 36 lines · Julien Grall
Quoted 3 lines · Sameer Goel

This will be used as a device property to match the DMA capable devices with the associated SMMU. The header file is a port from linux. The code was changed to remove the types that were not needed for Xen.

I think you probably want a bit more context in the commit message about implement fwnode.h in common code.

Within this series, fwnode seems to only be used by Arm. So what would be the advantage to get that in xen/? Is it going to be used by x86 or taken advantage in common code?

Quoted 23 lines

Linux ChangeId:ce793486e23e: driver core / ACPI: Represent ACPI companions using fwnode_handle

---
   xen/include/asm-arm/device.h |  2 ++ xen/include/xen/fwnode.h     | 33 +++++++++++++++++++++++++++++++++

   2 files changed, 35 insertions(+)

   create mode 100644 xen/include/xen/fwnode.h

xen/include/asm-arm/device.h +1 -0
index 6734ae8..78c38fe 100644
@@ -2,6 +2,7 @@
    #define __ASM_ARM_DEVICE_H
      #include <xen/init.h>
+#include <xen/fwnode.h>

     enum device_type
   {

xen/include/asm-arm/device.h +0 -0
@@ -19,6 +20,7 @@ struct device
    #ifdef CONFIG_HAS_DEVICE_TREE
Quoted 1 line

       struct dt_device_node *of_node; /* Used by drivers imported from Linux */

I was expecting a todo in the code after the discussion about leave of_node here.

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
Quoted 2 lines

   #endif
+    struct fwnode_handle *fwnode; /*fw device node identifier */

The fwnode handle was provide a match cookie for the SMMUs and not much else. Even with this around we will need the dt info in the device node. I agree that this rolls up into fw spec and I can look at the code cleanup for the next patch.

A clean-up patch would be great but not necessary. What I expect is a TODO mentioning the possible clean-up.

I looked through the Linux code a bit more. The reason for pulling in the fwnode was to port the iort code as is. So, really I do not need this at all. All I need is a dummy struct for fwnode.

I will remove this header file and add a new definition in the linux compat header. What is a good location for this header?

Quoted 2 lines

Cheers,

Signature · 6 lines
 Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#7 lore raw

RFC v2 3/7 xen/passthrough/arm: Introduce iommu_fwspec

In reply to Sameer Goel

Introduce a common structure to hold the fw (ACPI or DT) defined configuration for SMMU hw. The current use case is for arm SMMUs. So, making this architecture specific.

Based on Linux kernel commit 57f98d2f61e1: iommu: Introduce iommu_fwspec

---
 xen/drivers/passthrough/arm/iommu.c | 66 +++++++++++++++++++++++++++++++++++++
 xen/include/asm-arm/device.h        |  1 +
 xen/include/xen/iommu.h             | 29 ++++++++++++++++
 3 files changed, 96 insertions(+)
xen/drivers/passthrough/arm/iommu.c +66 -0
index 95b1abb..41c6497 100644
@@ -73,3 +73,69 @@ int arch_iommu_populate_page_table(struct domain *d)
     /* The IOMMU shares the p2m with the CPU */
     return -ENOSYS;
 }
+
+const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode)
+{
+    return iommu_get_ops();
+}
+
+int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
+        const struct iommu_ops *ops)
+{
+    struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+    if ( fwspec )
+        return ops == fwspec->ops ? 0 : -EINVAL;
+
+    fwspec = _xzalloc(sizeof(struct iommu_fwspec), sizeof(void *));
+    if ( !fwspec )
+        return -ENOMEM;
+
+    fwspec->iommu_fwnode = iommu_fwnode;
+    fwspec->ops = ops;
+    dev->iommu_fwspec = fwspec;
+
+    return 0;
+}
+
+void iommu_fwspec_free(struct device *dev)
+{
+    struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+    if ( fwspec )
+    {
+        xfree(fwspec);
+        dev->iommu_fwspec = NULL;
+    }
+}
+
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
+{
+    struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+    struct iommu_fwspec *fwspec_n = NULL;
+    size_t size, size_n;
+    int i;
+
+    if ( !fwspec )
+        return -EINVAL;
+
+    size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids]);
+    size_n = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]);
+    if ( size_n > size )
+    {
+        fwspec_n = _xzalloc(size_n, sizeof(void *));
+        if ( !fwspec_n )
+            return -ENOMEM;
+
+        memcpy(fwspec_n, fwspec, size);
+        xfree(fwspec);
+    }
+
+    for (i = 0; i < num_ids; i++)
+        fwspec_n->ids[fwspec_n->num_ids + i] = ids[i];
+
+    fwspec_n->num_ids += num_ids;
+    dev->iommu_fwspec = fwspec_n;
+
+    return 0;
+}
xen/include/asm-arm/device.h +1 -0
index 78c38fe..5027c87 100644
@@ -21,6 +21,7 @@ struct device
     struct dt_device_node *of_node; /* Used by drivers imported from Linux */
 #endif
     struct fwnode_handle *fwnode; /*fw device node identifier */
+    struct iommu_fwspec *iommu_fwspec;
     struct dev_archdata archdata;
 };
 
xen/include/xen/iommu.h +29 -0
index 0dac4f3..34e8d68 100644
@@ -208,4 +208,33 @@ DECLARE_PER_CPU(bool_t, iommu_dont_flush_iotlb);
 extern struct spinlock iommu_pt_cleanup_lock;
 extern struct page_list_head iommu_pt_cleanup_list;
 
+/**
+ * Following block was ported from Linux to help with the implementation of
+ * arm64 iommu devices. Hence the architecture specific compile
+ */
+
+#if defined(CONFIG_ARM)
+/**
+ * struct iommu_fwspec - per-device IOMMU instance data
+ * @ops: ops for this device's IOMMU
+ * @iommu_fwnode: firmware handle for this device's IOMMU
+ * @iommu_priv: IOMMU driver private data for this device
+ * @num_ids: number of associated device IDs
+ * @ids: IDs which this device may present to the IOMMU
+ */
+struct iommu_fwspec {
+    const struct iommu_ops *ops;
+    struct fwnode_handle   *iommu_fwnode;
+    void                   *iommu_priv;
+    unsigned int           num_ids;
+    u32                    ids[1];
+};
+
+int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
+		      const struct iommu_ops *ops);
+void iommu_fwspec_free(struct device *dev);
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
+const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode);
+
+#endif
 #endif /* _IOMMU_H_ */
Signature · 7 lines
Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.


_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#8 lore raw

Re: [RFC v2 3/7] xen/passthrough/arm: Introduce iommu_fwspec

Hi,

Quoted 17 lines · Sameer Goel

Introduce a common structure to hold the fw (ACPI or DT) defined configuration for SMMU hw. The current use case is for arm SMMUs. So, making this architecture specific.

Based on Linux kernel commit 57f98d2f61e1: iommu: Introduce iommu_fwspec

---
  xen/drivers/passthrough/arm/iommu.c | 66 +++++++++++++++++++++++++++++++++++++
  xen/include/asm-arm/device.h        |  1 +
  xen/include/xen/iommu.h             | 29 ++++++++++++++++
  3 files changed, 96 insertions(+)
xen/drivers/passthrough/arm/iommu.c +0 -0
index 95b1abb..41c6497 100644

xen/drivers/passthrough/arm/iommu.c int arch_iommu_populate_page_table(struct domain *d) · line 79

Quoted 7 lines
xen/drivers/passthrough/arm/iommu.c +4 -0
@@ -73,3 +73,69 @@ int arch_iommu_populate_page_table(struct domain *d)
      /* The IOMMU shares the p2m with the CPU */
      return -ENOSYS;
  }
+
+const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode)
+{
+    return iommu_get_ops();

Can you please add a comment explain why you always return iommu_get_ops()?

Would it be possible that the device is not behind an IOMMU?

xen/drivers/passthrough/arm/iommu.c int arch_iommu_populate_page_table(struct domain *d) · line 90

Quoted 11 lines
xen/drivers/passthrough/arm/iommu.c +11 -0
+}
+
+int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
+        const struct iommu_ops *ops)
+{
+    struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+    if ( fwspec )
+        return ops == fwspec->ops ? 0 : -EINVAL;
+
+    fwspec = _xzalloc(sizeof(struct iommu_fwspec), sizeof(void *));

On the previous version this was xzalloc(struct iommu_fwspec), why?

I also don't understand the align on sizeof(void *).

Quoted 6 lines
xen/drivers/passthrough/arm/iommu.c +6 -0
+    if ( !fwspec )
+        return -ENOMEM;
+
+    fwspec->iommu_fwnode = iommu_fwnode;
+    fwspec->ops = ops;
+    dev->iommu_fwspec = fwspec;

xen/drivers/passthrough/arm/iommu.c int arch_iommu_populate_page_table(struct domain *d) · line 106

Quoted 10 lines
xen/drivers/passthrough/arm/iommu.c +10 -0
+
+    return 0;
+}
+
+void iommu_fwspec_free(struct device *dev)
+{
+    struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+    if ( fwspec )
+    {

Linux is dropping the reference on the iommu_fwnode. Are we never expecting to take reference on the it in Xen?

Quoted 9 lines
xen/drivers/passthrough/arm/iommu.c +9 -0
+        xfree(fwspec);
+        dev->iommu_fwspec = NULL;
+    }
+}
+
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
+{
+    struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+    struct iommu_fwspec *fwspec_n = NULL;

xen/drivers/passthrough/arm/iommu.c int arch_iommu_populate_page_table(struct domain *d)

Quoted 10 lines
xen/drivers/passthrough/arm/iommu.c +10 -0
+    size_t size, size_n;
+    int i;
+
+    if ( !fwspec )
+        return -EINVAL;
+
+    size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids]);
+    size_n = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]);
+    if ( size_n > size )
+    { > +        fwspec_n = _xzalloc(size_n, sizeof(void *));

Same question about _xzalloc() here.

Quoted 32 lines
xen/drivers/passthrough/arm/iommu.c +15 -0
+        if ( !fwspec_n )
+            return -ENOMEM;
+
+        memcpy(fwspec_n, fwspec, size);
+        xfree(fwspec);
+    }
+
+    for (i = 0; i < num_ids; i++)
+        fwspec_n->ids[fwspec_n->num_ids + i] = ids[i];
+
+    fwspec_n->num_ids += num_ids;
+    dev->iommu_fwspec = fwspec_n;
+
+    return 0;
+}
xen/include/asm-arm/device.h +1 -0
index 78c38fe..5027c87 100644
@@ -21,6 +21,7 @@ struct device
      struct dt_device_node *of_node; /* Used by drivers imported from Linux */
  #endif
      struct fwnode_handle *fwnode; /*fw device node identifier */
+    struct iommu_fwspec *iommu_fwspec;
      struct dev_archdata archdata;
  };
 
xen/include/xen/iommu.h +0 -0
index 0dac4f3..34e8d68 100644

xen/include/xen/iommu.h DECLARE_PER_CPU(bool_t, iommu_dont_flush_iotlb); · line 216

Quoted 9 lines
xen/include/xen/iommu.h +6 -0
@@ -208,4 +208,33 @@ DECLARE_PER_CPU(bool_t, iommu_dont_flush_iotlb);
  extern struct spinlock iommu_pt_cleanup_lock;
  extern struct page_list_head iommu_pt_cleanup_list;
 
+/**
+ * Following block was ported from Linux to help with the implementation of
+ * arm64 iommu devices. Hence the architecture specific compile
+ */
+
+#if defined(CONFIG_ARM)

If it is Arm only, then it should be moved in asm-arm/iommu.h.

Quoted 15 lines
xen/include/xen/iommu.h +14 -0
+/**
+ * struct iommu_fwspec - per-device IOMMU instance data
+ * @ops: ops for this device's IOMMU
+ * @iommu_fwnode: firmware handle for this device's IOMMU
+ * @iommu_priv: IOMMU driver private data for this device
+ * @num_ids: number of associated device IDs
+ * @ids: IDs which this device may present to the IOMMU
+ */
+struct iommu_fwspec {
+    const struct iommu_ops *ops;
+    struct fwnode_handle   *iommu_fwnode;
+    void                   *iommu_priv;
+    unsigned int           num_ids;
+    u32                    ids[1];

xen/include/xen/iommu.h DECLARE_PER_CPU(bool_t, iommu_dont_flush_iotlb); · line 240

Quoted 10 lines
xen/include/xen/iommu.h +9 -0
+};
+
+int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
+		      const struct iommu_ops *ops);
+void iommu_fwspec_free(struct device *dev);
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
+const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode);
+
+#endif
  #endif /* _IOMMU_H_ */

Cheers,

Signature · 6 lines
Julien Grall

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#9 lore raw

Re: [RFC v2 3/7] xen/passthrough/arm: Introduce iommu_fwspec

Hi,

Quoted 84 lines · Julien Grall

Hi,

Quoted 23 lines · Sameer Goel

Introduce a common structure to hold the fw (ACPI or DT) defined configuration for SMMU hw. The current use case is for arm SMMUs. So, making this architecture specific.

Based on Linux kernel commit 57f98d2f61e1: iommu: Introduce iommu_fwspec

---
  xen/drivers/passthrough/arm/iommu.c | 66
+++++++++++++++++++++++++++++++++++++
  xen/include/asm-arm/device.h        |  1 + xen/include/xen/iommu.h             | 29 ++++++++++++++++

  3 files changed, 96 insertions(+)
xen/drivers/passthrough/arm/iommu.c +2 -0
index 95b1abb..41c6497 100644
@@ -73,3 +73,69 @@ int arch_iommu_populate_page_table(struct domain *d)
       /* The IOMMU shares the p2m with the CPU */
       return -ENOSYS;
   }
+
+const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle 
Quoted 3 lines

*fwnode)
+{
+    return iommu_get_ops();

Can you please add a comment explain why you always return iommu_get_ops()?

Would it be possible that the device is not behind an IOMMU?

xen/drivers/passthrough/arm/iommu.c int arch_iommu_populate_page_table(struct domain *d) · line 90

Quoted 12 lines
xen/drivers/passthrough/arm/iommu.c +3 -0
+}
+
+int iommu_fwspec_init(struct device *dev, struct fwnode_handle 

*iommu_fwnode,
+        const struct iommu_ops *ops)

xen/drivers/passthrough/arm/iommu.c +7 -0
+{
+    struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+    if ( fwspec )
+        return ops == fwspec->ops ? 0 : -EINVAL;
+
+    fwspec = _xzalloc(sizeof(struct iommu_fwspec), sizeof(void *));

On the previous version this was xzalloc(struct iommu_fwspec), why?

I also don't understand the align on sizeof(void *).

Quoted 6 lines

+    if ( !fwspec )
+        return -ENOMEM;

xen/drivers/passthrough/arm/iommu.c +4 -0
+
+    fwspec->iommu_fwnode = iommu_fwnode;
+    fwspec->ops = ops;
+    dev->iommu_fwspec = fwspec;

xen/drivers/passthrough/arm/iommu.c int arch_iommu_populate_page_table(struct domain *d) · line 106

Quoted 10 lines
xen/drivers/passthrough/arm/iommu.c +10 -0
+
+    return 0;
+}
+
+void iommu_fwspec_free(struct device *dev)
+{
+    struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+    if ( fwspec )
+    {

Linux is dropping the reference on the iommu_fwnode. Are we never expecting to take reference on the it in Xen?

Quoted 14 lines

+        xfree(fwspec);
+        dev->iommu_fwspec = NULL;
+    }

xen/drivers/passthrough/arm/iommu.c +11 -0
+}
+
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
+{
+    struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+    struct iommu_fwspec *fwspec_n = NULL;
+    size_t size, size_n;
+    int i;
+
+    if ( !fwspec )
+        return -EINVAL;
Quoted 8 lines
Quoted 6 lines

+
+    size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids]); +    size_n = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]);
+    if ( size_n > size )
+    { > +        fwspec_n = _xzalloc(size_n, sizeof(void *));

Same question about _xzalloc() here.

Also, please see the comment I just made on "[RFC 3/6] Introduce _xrealloc".

I would prefer to explore the possibility of a generic helper rather than open-coding it. I think we have enough information in hand to get the size of the old region.

Cheers,

Signature · 6 lines
Julien Grall

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#10 lore raw

Re: [RFC v2 3/7] xen/passthrough/arm: Introduce iommu_fwspec

In reply to Julien Grall
Quoted 30 lines · Julien Grall

Hi,

Quoted 17 lines · Sameer Goel

Introduce a common structure to hold the fw (ACPI or DT) defined configuration for SMMU hw. The current use case is for arm SMMUs. So, making this architecture specific.

Based on Linux kernel commit 57f98d2f61e1: iommu: Introduce iommu_fwspec

---
  xen/drivers/passthrough/arm/iommu.c | 66 +++++++++++++++++++++++++++++++++++++ xen/include/asm-arm/device.h        |  1 + xen/include/xen/iommu.h             | 29 ++++++++++++++++

  3 files changed, 96 insertions(+)
xen/drivers/passthrough/arm/iommu.c +0 -0
index 95b1abb..41c6497 100644

xen/drivers/passthrough/arm/iommu.c int arch_iommu_populate_page_table(struct domain *d) · line 79

Quoted 7 lines
xen/drivers/passthrough/arm/iommu.c +4 -0
@@ -73,3 +73,69 @@ int arch_iommu_populate_page_table(struct domain *d)
       /* The IOMMU shares the p2m with the CPU */
       return -ENOSYS;
   }
+
+const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode)
+{
+    return iommu_get_ops();

Can you please add a comment explain why you always return iommu_get_ops()?

Quoted 1 line

Would it be possible that the device is not behind an IOMMU?

That is true. I will fix this.

Quoted 15 lines

xen/drivers/passthrough/arm/iommu.c int arch_iommu_populate_page_table(struct domain *d) · line 90

Quoted 12 lines
xen/drivers/passthrough/arm/iommu.c +11 -0
+}
+
+int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
+        const struct iommu_ops *ops)
+{
+    struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+    if ( fwspec )
+        return ops == fwspec->ops ? 0 : -EINVAL;
+
+    fwspec = _xzalloc(sizeof(struct iommu_fwspec), sizeof(void *));

On the previous version this was xzalloc(struct iommu_fwspec), why?

Quoted 1 line

I also don't understand the align on sizeof(void *).

Forgot why I did this. I will change it back. I just copied the alignment value from xzalloc.

Quoted 11 lines
Quoted 11 lines
xen/drivers/passthrough/arm/iommu.c +10 -0
+    if ( !fwspec )
+        return -ENOMEM;
+
+    fwspec->iommu_fwnode = iommu_fwnode;
+    fwspec->ops = ops;
+    dev->iommu_fwspec = fwspec;
+
+    return 0;
+}
+
Quoted 8 lines

xen/drivers/passthrough/arm/iommu.c int arch_iommu_populate_page_table(struct domain *d) · line 106

Quoted 6 lines
xen/drivers/passthrough/arm/iommu.c +6 -0
+void iommu_fwspec_free(struct device *dev)
+{
+    struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+
+    if ( fwspec )
+    {

Linux is dropping the reference on the iommu_fwnode. Are we never expecting to take reference on the it in Xen?

I will fix this.

Quoted 14 lines
Quoted 14 lines
xen/drivers/passthrough/arm/iommu.c +13 -0
+        xfree(fwspec);
+        dev->iommu_fwspec = NULL;
+    }
+}
+
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
+{
+    struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+    struct iommu_fwspec *fwspec_n = NULL;
+    size_t size, size_n;
+    int i;
+
+    if ( !fwspec )
Quoted 9 lines

xen/drivers/passthrough/arm/iommu.c int arch_iommu_populate_page_table(struct domain *d)

Quoted 6 lines
xen/drivers/passthrough/arm/iommu.c +6 -0
+        return -EINVAL;
+
+    size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids]);
+    size_n = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]);
+    if ( size_n > size )
+    { > +        fwspec_n = _xzalloc(size_n, sizeof(void *));

Same question about _xzalloc() here.

See above.

Quoted 33 lines
Quoted 33 lines
xen/drivers/passthrough/arm/iommu.c +15 -0
+        if ( !fwspec_n )
+            return -ENOMEM;
+
+        memcpy(fwspec_n, fwspec, size);
+        xfree(fwspec);
+    }
+
+    for (i = 0; i < num_ids; i++)
+        fwspec_n->ids[fwspec_n->num_ids + i] = ids[i];
+
+    fwspec_n->num_ids += num_ids;
+    dev->iommu_fwspec = fwspec_n;
+
+    return 0;
+}
xen/include/asm-arm/device.h +0 -0
index 78c38fe..5027c87 100644
@@ -21,6 +21,7 @@ struct device

      struct dt_device_node *of_node; /* Used by drivers imported from Linux */ #endif
      struct fwnode_handle *fwnode; /*fw device node identifier */ +    struct iommu_fwspec *iommu_fwspec;
      struct dev_archdata archdata;
  };
  diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h index 0dac4f3..34e8d68 100644

xen/include/xen/iommu.h +0 -0
@@ -208,4 +208,33 @@ DECLARE_PER_CPU(bool_t, iommu_dont_flush_iotlb);
   extern struct spinlock iommu_pt_cleanup_lock;
   extern struct page_list_head iommu_pt_cleanup_list;
Quoted 8 lines

xen/include/xen/iommu.h DECLARE_PER_CPU(bool_t, iommu_dont_flush_iotlb); · line 216

Quoted 6 lines

  +/**
+ * Following block was ported from Linux to help with the implementation of
+ * arm64 iommu devices. Hence the architecture specific compile
+ */

xen/include/xen/iommu.h +2 -0
+
+#if defined(CONFIG_ARM)

If it is Arm only, then it should be moved in asm-arm/iommu.h.

Will do.

Quoted 29 lines
Quoted 24 lines
xen/include/xen/iommu.h +23 -0
+/**
+ * struct iommu_fwspec - per-device IOMMU instance data
+ * @ops: ops for this device's IOMMU
+ * @iommu_fwnode: firmware handle for this device's IOMMU
+ * @iommu_priv: IOMMU driver private data for this device
+ * @num_ids: number of associated device IDs
+ * @ids: IDs which this device may present to the IOMMU
+ */
+struct iommu_fwspec {
+    const struct iommu_ops *ops;
+    struct fwnode_handle   *iommu_fwnode;
+    void                   *iommu_priv;
+    unsigned int           num_ids;
+    u32                    ids[1];
+};
+
+int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
+              const struct iommu_ops *ops);
+void iommu_fwspec_free(struct device *dev);
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
+const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode);
+
+#endif
Quoted 2 lines

  #endif /* _IOMMU_H_ */

Cheers,

Signature · 6 lines
 Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#11 lore raw

RFC v2 4/7 ACPI: arm: Support for IORT

In reply to Sameer Goel

Verbatim files from Linux kernel.
iort.c: commit d89cf2e41826: ACPI/IORT: Fix the indentation in iort_scan_node()
acpi_iort.h: commit 18b709beb503:ACPI/IORT: Make dma masks set-up IORT specific

---
 xen/drivers/acpi/arm/iort.c  | 961 +++++++++++++++++++++++++++++++++++++++++++
 xen/include/acpi/acpi_iort.h |  58 +++
 2 files changed, 1019 insertions(+)
 create mode 100644 xen/drivers/acpi/arm/iort.c
 create mode 100644 xen/include/acpi/acpi_iort.h
xen/drivers/acpi/arm/iort.c new file +961 -0
new file mode 100644
index 0000000..2e368a6
@@ -0,0 +1,961 @@
+/*
+ * Copyright (C) 2016, Semihalf
+ *	Author: Tomasz Nowicki <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * This file implements early detection/parsing of I/O mapping
+ * reported to OS through firmware via I/O Remapping Table (IORT)
+ * IORT document number: ARM DEN 0049A
+ */
+
+#define pr_fmt(fmt)	"ACPI: IORT: " fmt
+
+#include <linux/acpi_iort.h>
+#include <linux/iommu.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define IORT_TYPE_MASK(type)	(1 << (type))
+#define IORT_MSI_TYPE		(1 << ACPI_IORT_NODE_ITS_GROUP)
+#define IORT_IOMMU_TYPE		((1 << ACPI_IORT_NODE_SMMU) |	\
+				(1 << ACPI_IORT_NODE_SMMU_V3))
+
+struct iort_its_msi_chip {
+	struct list_head	list;
+	struct fwnode_handle	*fw_node;
+	u32			translation_id;
+};
+
+struct iort_fwnode {
+	struct list_head list;
+	struct acpi_iort_node *iort_node;
+	struct fwnode_handle *fwnode;
+};
+static LIST_HEAD(iort_fwnode_list);
+static DEFINE_SPINLOCK(iort_fwnode_lock);
+
+/**
+ * iort_set_fwnode() - Create iort_fwnode and use it to register
+ *		       iommu data in the iort_fwnode_list
+ *
+ * @node: IORT table node associated with the IOMMU
+ * @fwnode: fwnode associated with the IORT node
+ *
+ * Returns: 0 on success
+ *          <0 on failure
+ */
+static inline int iort_set_fwnode(struct acpi_iort_node *iort_node,
+				  struct fwnode_handle *fwnode)
+{
+	struct iort_fwnode *np;
+
+	np = kzalloc(sizeof(struct iort_fwnode), GFP_ATOMIC);
+
+	if (WARN_ON(!np))
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&np->list);
+	np->iort_node = iort_node;
+	np->fwnode = fwnode;
+
+	spin_lock(&iort_fwnode_lock);
+	list_add_tail(&np->list, &iort_fwnode_list);
+	spin_unlock(&iort_fwnode_lock);
+
+	return 0;
+}
+
+/**
+ * iort_get_fwnode() - Retrieve fwnode associated with an IORT node
+ *
+ * @node: IORT table node to be looked-up
+ *
+ * Returns: fwnode_handle pointer on success, NULL on failure
+ */
+static inline
+struct fwnode_handle *iort_get_fwnode(struct acpi_iort_node *node)
+{
+	struct iort_fwnode *curr;
+	struct fwnode_handle *fwnode = NULL;
+
+	spin_lock(&iort_fwnode_lock);
+	list_for_each_entry(curr, &iort_fwnode_list, list) {
+		if (curr->iort_node == node) {
+			fwnode = curr->fwnode;
+			break;
+		}
+	}
+	spin_unlock(&iort_fwnode_lock);
+
+	return fwnode;
+}
+
+/**
+ * iort_delete_fwnode() - Delete fwnode associated with an IORT node
+ *
+ * @node: IORT table node associated with fwnode to delete
+ */
+static inline void iort_delete_fwnode(struct acpi_iort_node *node)
+{
+	struct iort_fwnode *curr, *tmp;
+
+	spin_lock(&iort_fwnode_lock);
+	list_for_each_entry_safe(curr, tmp, &iort_fwnode_list, list) {
+		if (curr->iort_node == node) {
+			list_del(&curr->list);
+			kfree(curr);
+			break;
+		}
+	}
+	spin_unlock(&iort_fwnode_lock);
+}
+
+typedef acpi_status (*iort_find_node_callback)
+	(struct acpi_iort_node *node, void *context);
+
+/* Root pointer to the mapped IORT table */
+static struct acpi_table_header *iort_table;
+
+static LIST_HEAD(iort_msi_chip_list);
+static DEFINE_SPINLOCK(iort_msi_chip_lock);
+
+/**
+ * iort_register_domain_token() - register domain token and related ITS ID
+ * to the list from where we can get it back later on.
+ * @trans_id: ITS ID.
+ * @fw_node: Domain token.
+ *
+ * Returns: 0 on success, -ENOMEM if no memory when allocating list element
+ */
+int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node)
+{
+	struct iort_its_msi_chip *its_msi_chip;
+
+	its_msi_chip = kzalloc(sizeof(*its_msi_chip), GFP_KERNEL);
+	if (!its_msi_chip)
+		return -ENOMEM;
+
+	its_msi_chip->fw_node = fw_node;
+	its_msi_chip->translation_id = trans_id;
+
+	spin_lock(&iort_msi_chip_lock);
+	list_add(&its_msi_chip->list, &iort_msi_chip_list);
+	spin_unlock(&iort_msi_chip_lock);
+
+	return 0;
+}
+
+/**
+ * iort_deregister_domain_token() - Deregister domain token based on ITS ID
+ * @trans_id: ITS ID.
+ *
+ * Returns: none.
+ */
+void iort_deregister_domain_token(int trans_id)
+{
+	struct iort_its_msi_chip *its_msi_chip, *t;
+
+	spin_lock(&iort_msi_chip_lock);
+	list_for_each_entry_safe(its_msi_chip, t, &iort_msi_chip_list, list) {
+		if (its_msi_chip->translation_id == trans_id) {
+			list_del(&its_msi_chip->list);
+			kfree(its_msi_chip);
+			break;
+		}
+	}
+	spin_unlock(&iort_msi_chip_lock);
+}
+
+/**
+ * iort_find_domain_token() - Find domain token based on given ITS ID
+ * @trans_id: ITS ID.
+ *
+ * Returns: domain token when find on the list, NULL otherwise
+ */
+struct fwnode_handle *iort_find_domain_token(int trans_id)
+{
+	struct fwnode_handle *fw_node = NULL;
+	struct iort_its_msi_chip *its_msi_chip;
+
+	spin_lock(&iort_msi_chip_lock);
+	list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) {
+		if (its_msi_chip->translation_id == trans_id) {
+			fw_node = its_msi_chip->fw_node;
+			break;
+		}
+	}
+	spin_unlock(&iort_msi_chip_lock);
+
+	return fw_node;
+}
+
+static struct acpi_iort_node *iort_scan_node(enum acpi_iort_node_type type,
+					     iort_find_node_callback callback,
+					     void *context)
+{
+	struct acpi_iort_node *iort_node, *iort_end;
+	struct acpi_table_iort *iort;
+	int i;
+
+	if (!iort_table)
+		return NULL;
+
+	/* Get the first IORT node */
+	iort = (struct acpi_table_iort *)iort_table;
+	iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort,
+				 iort->node_offset);
+	iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
+				iort_table->length);
+
+	for (i = 0; i < iort->node_count; i++) {
+		if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND,
+			       "IORT node pointer overflows, bad table!\n"))
+			return NULL;
+
+		if (iort_node->type == type &&
+		    ACPI_SUCCESS(callback(iort_node, context)))
+			return iort_node;
+
+		iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
+					 iort_node->length);
+	}
+
+	return NULL;
+}
+
+static acpi_status
+iort_match_type_callback(struct acpi_iort_node *node, void *context)
+{
+	return AE_OK;
+}
+
+bool iort_node_match(u8 type)
+{
+	struct acpi_iort_node *node;
+
+	node = iort_scan_node(type, iort_match_type_callback, NULL);
+
+	return node != NULL;
+}
+
+static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
+					    void *context)
+{
+	struct device *dev = context;
+	acpi_status status;
+
+	if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
+		struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
+		struct acpi_device *adev = to_acpi_device_node(dev->fwnode);
+		struct acpi_iort_named_component *ncomp;
+
+		if (!adev) {
+			status = AE_NOT_FOUND;
+			goto out;
+		}
+
+		status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf);
+		if (ACPI_FAILURE(status)) {
+			dev_warn(dev, "Can't get device full path name\n");
+			goto out;
+		}
+
+		ncomp = (struct acpi_iort_named_component *)node->node_data;
+		status = !strcmp(ncomp->device_name, buf.pointer) ?
+							AE_OK : AE_NOT_FOUND;
+		acpi_os_free(buf.pointer);
+	} else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
+		struct acpi_iort_root_complex *pci_rc;
+		struct pci_bus *bus;
+
+		bus = to_pci_bus(dev);
+		pci_rc = (struct acpi_iort_root_complex *)node->node_data;
+
+		/*
+		 * It is assumed that PCI segment numbers maps one-to-one
+		 * with root complexes. Each segment number can represent only
+		 * one root complex.
+		 */
+		status = pci_rc->pci_segment_number == pci_domain_nr(bus) ?
+							AE_OK : AE_NOT_FOUND;
+	} else {
+		status = AE_NOT_FOUND;
+	}
+out:
+	return status;
+}
+
+static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,
+		       u32 *rid_out)
+{
+	/* Single mapping does not care for input id */
+	if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) {
+		if (type == ACPI_IORT_NODE_NAMED_COMPONENT ||
+		    type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
+			*rid_out = map->output_base;
+			return 0;
+		}
+
+		pr_warn(FW_BUG "[map %p] SINGLE MAPPING flag not allowed for node type %d, skipping ID map\n",
+			map, type);
+		return -ENXIO;
+	}
+
+	if (rid_in < map->input_base ||
+	    (rid_in >= map->input_base + map->id_count))
+		return -ENXIO;
+
+	*rid_out = map->output_base + (rid_in - map->input_base);
+	return 0;
+}
+
+static
+struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
+					u32 *id_out, u8 type_mask,
+					int index)
+{
+	struct acpi_iort_node *parent;
+	struct acpi_iort_id_mapping *map;
+
+	if (!node->mapping_offset || !node->mapping_count ||
+				     index >= node->mapping_count)
+		return NULL;
+
+	map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node,
+			   node->mapping_offset + index * sizeof(*map));
+
+	/* Firmware bug! */
+	if (!map->output_reference) {
+		pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n",
+		       node, node->type);
+		return NULL;
+	}
+
+	parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
+			       map->output_reference);
+
+	if (!(IORT_TYPE_MASK(parent->type) & type_mask))
+		return NULL;
+
+	if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) {
+		if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT ||
+		    node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
+			*id_out = map->output_base;
+			return parent;
+		}
+	}
+
+	return NULL;
+}
+
+static struct acpi_iort_node *iort_node_map_rid(struct acpi_iort_node *node,
+						u32 rid_in, u32 *rid_out,
+						u8 type_mask)
+{
+	u32 rid = rid_in;
+
+	/* Parse the ID mapping tree to find specified node type */
+	while (node) {
+		struct acpi_iort_id_mapping *map;
+		int i;
+
+		if (IORT_TYPE_MASK(node->type) & type_mask) {
+			if (rid_out)
+				*rid_out = rid;
+			return node;
+		}
+
+		if (!node->mapping_offset || !node->mapping_count)
+			goto fail_map;
+
+		map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node,
+				   node->mapping_offset);
+
+		/* Firmware bug! */
+		if (!map->output_reference) {
+			pr_err(FW_BUG "[node %p type %d] ID map has NULL parent reference\n",
+			       node, node->type);
+			goto fail_map;
+		}
+
+		/* Do the RID translation */
+		for (i = 0; i < node->mapping_count; i++, map++) {
+			if (!iort_id_map(map, node->type, rid, &rid))
+				break;
+		}
+
+		if (i == node->mapping_count)
+			goto fail_map;
+
+		node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
+				    map->output_reference);
+	}
+
+fail_map:
+	/* Map input RID to output RID unchanged on mapping failure*/
+	if (rid_out)
+		*rid_out = rid_in;
+
+	return NULL;
+}
+
+static struct acpi_iort_node *iort_find_dev_node(struct device *dev)
+{
+	struct pci_bus *pbus;
+
+	if (!dev_is_pci(dev))
+		return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
+				      iort_match_node_callback, dev);
+
+	/* Find a PCI root bus */
+	pbus = to_pci_dev(dev)->bus;
+	while (!pci_is_root_bus(pbus))
+		pbus = pbus->parent;
+
+	return iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
+			      iort_match_node_callback, &pbus->dev);
+}
+
+/**
+ * iort_msi_map_rid() - Map a MSI requester ID for a device
+ * @dev: The device for which the mapping is to be done.
+ * @req_id: The device requester ID.
+ *
+ * Returns: mapped MSI RID on success, input requester ID otherwise
+ */
+u32 iort_msi_map_rid(struct device *dev, u32 req_id)
+{
+	struct acpi_iort_node *node;
+	u32 dev_id;
+
+	node = iort_find_dev_node(dev);
+	if (!node)
+		return req_id;
+
+	iort_node_map_rid(node, req_id, &dev_id, IORT_MSI_TYPE);
+	return dev_id;
+}
+
+/**
+ * iort_dev_find_its_id() - Find the ITS identifier for a device
+ * @dev: The device.
+ * @idx: Index of the ITS identifier list.
+ * @its_id: ITS identifier.
+ *
+ * Returns: 0 on success, appropriate error value otherwise
+ */
+static int iort_dev_find_its_id(struct device *dev, u32 req_id,
+				unsigned int idx, int *its_id)
+{
+	struct acpi_iort_its_group *its;
+	struct acpi_iort_node *node;
+
+	node = iort_find_dev_node(dev);
+	if (!node)
+		return -ENXIO;
+
+	node = iort_node_map_rid(node, req_id, NULL, IORT_MSI_TYPE);
+	if (!node)
+		return -ENXIO;
+
+	/* Move to ITS specific data */
+	its = (struct acpi_iort_its_group *)node->node_data;
+	if (idx > its->its_count) {
+		dev_err(dev, "requested ITS ID index [%d] is greater than available [%d]\n",
+			idx, its->its_count);
+		return -ENXIO;
+	}
+
+	*its_id = its->identifiers[idx];
+	return 0;
+}
+
+/**
+ * iort_get_device_domain() - Find MSI domain related to a device
+ * @dev: The device.
+ * @req_id: Requester ID for the device.
+ *
+ * Returns: the MSI domain for this device, NULL otherwise
+ */
+struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id)
+{
+	struct fwnode_handle *handle;
+	int its_id;
+
+	if (iort_dev_find_its_id(dev, req_id, 0, &its_id))
+		return NULL;
+
+	handle = iort_find_domain_token(its_id);
+	if (!handle)
+		return NULL;
+
+	return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI);
+}
+
+static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
+{
+	u32 *rid = data;
+
+	*rid = alias;
+	return 0;
+}
+
+static int arm_smmu_iort_xlate(struct device *dev, u32 streamid,
+			       struct fwnode_handle *fwnode,
+			       const struct iommu_ops *ops)
+{
+	int ret = iommu_fwspec_init(dev, fwnode, ops);
+
+	if (!ret)
+		ret = iommu_fwspec_add_ids(dev, &streamid, 1);
+
+	return ret;
+}
+
+static const struct iommu_ops *iort_iommu_xlate(struct device *dev,
+					struct acpi_iort_node *node,
+					u32 streamid)
+{
+	const struct iommu_ops *ops = NULL;
+	int ret = -ENODEV;
+	struct fwnode_handle *iort_fwnode;
+
+	if (node) {
+		iort_fwnode = iort_get_fwnode(node);
+		if (!iort_fwnode)
+			return NULL;
+
+		ops = iommu_ops_from_fwnode(iort_fwnode);
+		if (!ops)
+			return NULL;
+
+		ret = arm_smmu_iort_xlate(dev, streamid, iort_fwnode, ops);
+	}
+
+	return ret ? NULL : ops;
+}
+
+/**
+ * iort_set_dma_mask - Set-up dma mask for a device.
+ *
+ * @dev: device to configure
+ */
+void iort_set_dma_mask(struct device *dev)
+{
+	/*
+	 * Set default coherent_dma_mask to 32 bit.  Drivers are expected to
+	 * setup the correct supported mask.
+	 */
+	if (!dev->coherent_dma_mask)
+		dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	/*
+	 * Set it to coherent_dma_mask by default if the architecture
+	 * code has not set it.
+	 */
+	if (!dev->dma_mask)
+		dev->dma_mask = &dev->coherent_dma_mask;
+}
+
+/**
+ * iort_iommu_configure - Set-up IOMMU configuration for a device.
+ *
+ * @dev: device to configure
+ *
+ * Returns: iommu_ops pointer on configuration success
+ *          NULL on configuration failure
+ */
+const struct iommu_ops *iort_iommu_configure(struct device *dev)
+{
+	struct acpi_iort_node *node, *parent;
+	const struct iommu_ops *ops = NULL;
+	u32 streamid = 0;
+
+	if (dev_is_pci(dev)) {
+		struct pci_bus *bus = to_pci_dev(dev)->bus;
+		u32 rid;
+
+		pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
+				       &rid);
+
+		node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
+				      iort_match_node_callback, &bus->dev);
+		if (!node)
+			return NULL;
+
+		parent = iort_node_map_rid(node, rid, &streamid,
+					   IORT_IOMMU_TYPE);
+
+		ops = iort_iommu_xlate(dev, parent, streamid);
+
+	} else {
+		int i = 0;
+
+		node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
+				      iort_match_node_callback, dev);
+		if (!node)
+			return NULL;
+
+		parent = iort_node_get_id(node, &streamid,
+					  IORT_IOMMU_TYPE, i++);
+
+		while (parent) {
+			ops = iort_iommu_xlate(dev, parent, streamid);
+
+			parent = iort_node_get_id(node, &streamid,
+						  IORT_IOMMU_TYPE, i++);
+		}
+	}
+
+	return ops;
+}
+
+static void __init acpi_iort_register_irq(int hwirq, const char *name,
+					  int trigger,
+					  struct resource *res)
+{
+	int irq = acpi_register_gsi(NULL, hwirq, trigger,
+				    ACPI_ACTIVE_HIGH);
+
+	if (irq <= 0) {
+		pr_err("could not register gsi hwirq %d name [%s]\n", hwirq,
+								      name);
+		return;
+	}
+
+	res->start = irq;
+	res->end = irq;
+	res->flags = IORESOURCE_IRQ;
+	res->name = name;
+}
+
+static int __init arm_smmu_v3_count_resources(struct acpi_iort_node *node)
+{
+	struct acpi_iort_smmu_v3 *smmu;
+	/* Always present mem resource */
+	int num_res = 1;
+
+	/* Retrieve SMMUv3 specific data */
+	smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
+
+	if (smmu->event_gsiv)
+		num_res++;
+
+	if (smmu->pri_gsiv)
+		num_res++;
+
+	if (smmu->gerr_gsiv)
+		num_res++;
+
+	if (smmu->sync_gsiv)
+		num_res++;
+
+	return num_res;
+}
+
+static void __init arm_smmu_v3_init_resources(struct resource *res,
+					      struct acpi_iort_node *node)
+{
+	struct acpi_iort_smmu_v3 *smmu;
+	int num_res = 0;
+
+	/* Retrieve SMMUv3 specific data */
+	smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
+
+	res[num_res].start = smmu->base_address;
+	res[num_res].end = smmu->base_address + SZ_128K - 1;
+	res[num_res].flags = IORESOURCE_MEM;
+
+	num_res++;
+
+	if (smmu->event_gsiv)
+		acpi_iort_register_irq(smmu->event_gsiv, "eventq",
+				       ACPI_EDGE_SENSITIVE,
+				       &res[num_res++]);
+
+	if (smmu->pri_gsiv)
+		acpi_iort_register_irq(smmu->pri_gsiv, "priq",
+				       ACPI_EDGE_SENSITIVE,
+				       &res[num_res++]);
+
+	if (smmu->gerr_gsiv)
+		acpi_iort_register_irq(smmu->gerr_gsiv, "gerror",
+				       ACPI_EDGE_SENSITIVE,
+				       &res[num_res++]);
+
+	if (smmu->sync_gsiv)
+		acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync",
+				       ACPI_EDGE_SENSITIVE,
+				       &res[num_res++]);
+}
+
+static bool __init arm_smmu_v3_is_coherent(struct acpi_iort_node *node)
+{
+	struct acpi_iort_smmu_v3 *smmu;
+
+	/* Retrieve SMMUv3 specific data */
+	smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
+
+	return smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE;
+}
+
+static int __init arm_smmu_count_resources(struct acpi_iort_node *node)
+{
+	struct acpi_iort_smmu *smmu;
+
+	/* Retrieve SMMU specific data */
+	smmu = (struct acpi_iort_smmu *)node->node_data;
+
+	/*
+	 * Only consider the global fault interrupt and ignore the
+	 * configuration access interrupt.
+	 *
+	 * MMIO address and global fault interrupt resources are always
+	 * present so add them to the context interrupt count as a static
+	 * value.
+	 */
+	return smmu->context_interrupt_count + 2;
+}
+
+static void __init arm_smmu_init_resources(struct resource *res,
+					   struct acpi_iort_node *node)
+{
+	struct acpi_iort_smmu *smmu;
+	int i, hw_irq, trigger, num_res = 0;
+	u64 *ctx_irq, *glb_irq;
+
+	/* Retrieve SMMU specific data */
+	smmu = (struct acpi_iort_smmu *)node->node_data;
+
+	res[num_res].start = smmu->base_address;
+	res[num_res].end = smmu->base_address + smmu->span - 1;
+	res[num_res].flags = IORESOURCE_MEM;
+	num_res++;
+
+	glb_irq = ACPI_ADD_PTR(u64, node, smmu->global_interrupt_offset);
+	/* Global IRQs */
+	hw_irq = IORT_IRQ_MASK(glb_irq[0]);
+	trigger = IORT_IRQ_TRIGGER_MASK(glb_irq[0]);
+
+	acpi_iort_register_irq(hw_irq, "arm-smmu-global", trigger,
+				     &res[num_res++]);
+
+	/* Context IRQs */
+	ctx_irq = ACPI_ADD_PTR(u64, node, smmu->context_interrupt_offset);
+	for (i = 0; i < smmu->context_interrupt_count; i++) {
+		hw_irq = IORT_IRQ_MASK(ctx_irq[i]);
+		trigger = IORT_IRQ_TRIGGER_MASK(ctx_irq[i]);
+
+		acpi_iort_register_irq(hw_irq, "arm-smmu-context", trigger,
+				       &res[num_res++]);
+	}
+}
+
+static bool __init arm_smmu_is_coherent(struct acpi_iort_node *node)
+{
+	struct acpi_iort_smmu *smmu;
+
+	/* Retrieve SMMU specific data */
+	smmu = (struct acpi_iort_smmu *)node->node_data;
+
+	return smmu->flags & ACPI_IORT_SMMU_COHERENT_WALK;
+}
+
+struct iort_iommu_config {
+	const char *name;
+	int (*iommu_init)(struct acpi_iort_node *node);
+	bool (*iommu_is_coherent)(struct acpi_iort_node *node);
+	int (*iommu_count_resources)(struct acpi_iort_node *node);
+	void (*iommu_init_resources)(struct resource *res,
+				     struct acpi_iort_node *node);
+};
+
+static const struct iort_iommu_config iort_arm_smmu_v3_cfg __initconst = {
+	.name = "arm-smmu-v3",
+	.iommu_is_coherent = arm_smmu_v3_is_coherent,
+	.iommu_count_resources = arm_smmu_v3_count_resources,
+	.iommu_init_resources = arm_smmu_v3_init_resources
+};
+
+static const struct iort_iommu_config iort_arm_smmu_cfg __initconst = {
+	.name = "arm-smmu",
+	.iommu_is_coherent = arm_smmu_is_coherent,
+	.iommu_count_resources = arm_smmu_count_resources,
+	.iommu_init_resources = arm_smmu_init_resources
+};
+
+static __init
+const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node)
+{
+	switch (node->type) {
+	case ACPI_IORT_NODE_SMMU_V3:
+		return &iort_arm_smmu_v3_cfg;
+	case ACPI_IORT_NODE_SMMU:
+		return &iort_arm_smmu_cfg;
+	default:
+		return NULL;
+	}
+}
+
+/**
+ * iort_add_smmu_platform_device() - Allocate a platform device for SMMU
+ * @node: Pointer to SMMU ACPI IORT node
+ *
+ * Returns: 0 on success, <0 failure
+ */
+static int __init iort_add_smmu_platform_device(struct acpi_iort_node *node)
+{
+	struct fwnode_handle *fwnode;
+	struct platform_device *pdev;
+	struct resource *r;
+	enum dev_dma_attr attr;
+	int ret, count;
+	const struct iort_iommu_config *ops = iort_get_iommu_cfg(node);
+
+	if (!ops)
+		return -ENODEV;
+
+	pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO);
+	if (!pdev)
+		return -ENOMEM;
+
+	count = ops->iommu_count_resources(node);
+
+	r = kcalloc(count, sizeof(*r), GFP_KERNEL);
+	if (!r) {
+		ret = -ENOMEM;
+		goto dev_put;
+	}
+
+	ops->iommu_init_resources(r, node);
+
+	ret = platform_device_add_resources(pdev, r, count);
+	/*
+	 * Resources are duplicated in platform_device_add_resources,
+	 * free their allocated memory
+	 */
+	kfree(r);
+
+	if (ret)
+		goto dev_put;
+
+	/*
+	 * Add a copy of IORT node pointer to platform_data to
+	 * be used to retrieve IORT data information.
+	 */
+	ret = platform_device_add_data(pdev, &node, sizeof(node));
+	if (ret)
+		goto dev_put;
+
+	/*
+	 * We expect the dma masks to be equivalent for
+	 * all SMMUs set-ups
+	 */
+	pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+
+	fwnode = iort_get_fwnode(node);
+
+	if (!fwnode) {
+		ret = -ENODEV;
+		goto dev_put;
+	}
+
+	pdev->dev.fwnode = fwnode;
+
+	attr = ops->iommu_is_coherent(node) ?
+			     DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT;
+
+	/* Configure DMA for the page table walker */
+	acpi_dma_configure(&pdev->dev, attr);
+
+	ret = platform_device_add(pdev);
+	if (ret)
+		goto dma_deconfigure;
+
+	return 0;
+
+dma_deconfigure:
+	acpi_dma_deconfigure(&pdev->dev);
+dev_put:
+	platform_device_put(pdev);
+
+	return ret;
+}
+
+static void __init iort_init_platform_devices(void)
+{
+	struct acpi_iort_node *iort_node, *iort_end;
+	struct acpi_table_iort *iort;
+	struct fwnode_handle *fwnode;
+	int i, ret;
+
+	/*
+	 * iort_table and iort both point to the start of IORT table, but
+	 * have different struct types
+	 */
+	iort = (struct acpi_table_iort *)iort_table;
+
+	/* Get the first IORT node */
+	iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort,
+				 iort->node_offset);
+	iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort,
+				iort_table->length);
+
+	for (i = 0; i < iort->node_count; i++) {
+		if (iort_node >= iort_end) {
+			pr_err("iort node pointer overflows, bad table\n");
+			return;
+		}
+
+		if ((iort_node->type == ACPI_IORT_NODE_SMMU) ||
+			(iort_node->type == ACPI_IORT_NODE_SMMU_V3)) {
+
+			fwnode = acpi_alloc_fwnode_static();
+			if (!fwnode)
+				return;
+
+			iort_set_fwnode(iort_node, fwnode);
+
+			ret = iort_add_smmu_platform_device(iort_node);
+			if (ret) {
+				iort_delete_fwnode(iort_node);
+				acpi_free_fwnode_static(fwnode);
+				return;
+			}
+		}
+
+		iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
+					 iort_node->length);
+	}
+}
+
+void __init acpi_iort_init(void)
+{
+	acpi_status status;
+
+	status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table);
+	if (ACPI_FAILURE(status)) {
+		if (status != AE_NOT_FOUND) {
+			const char *msg = acpi_format_exception(status);
+
+			pr_err("Failed to get table, %s\n", msg);
+		}
+
+		return;
+	}
+
+	iort_init_platform_devices();
+
+	acpi_probe_device_table(iort);
+}
xen/include/acpi/acpi_iort.h new file +58 -0

58 lines not on this page · raw message

Signature · 7 lines
Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.


_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#12 lore raw

RFC v2 5/7 acpi:arm64: Add support for parsing IORT table

In reply to Sameer Goel

Add support for parsing IORT table to initialize SMMU devices.
* The code for creating an SMMU device has been modified, so that the SMMU device can be initialized.
* The NAMED NODE code has been commented out as this will need DOM0 kernel support.
* ITS code has been included but it has not been tested.

---
 xen/arch/arm/setup.c               |   3 +
 xen/drivers/acpi/Makefile          |   1 +
 xen/drivers/acpi/arm/Makefile      |   1 +
 xen/drivers/acpi/arm/iort.c        | 173 +++++++++++++++++++++----------------
 xen/drivers/passthrough/arm/smmu.c |   1 +
 xen/include/acpi/acpi_iort.h       |  17 ++--
 xen/include/asm-arm/device.h       |   2 +
 xen/include/xen/acpi.h             |  21 +++++
 xen/include/xen/pci.h              |   8 ++
 9 files changed, 146 insertions(+), 81 deletions(-)
 create mode 100644 xen/drivers/acpi/arm/Makefile
xen/arch/arm/setup.c +3 -0
index 92f173b..4ba09b2 100644
@@ -49,6 +49,7 @@
 #include <asm/setup.h>
 #include <xsm/xsm.h>
 #include <asm/acpi.h>
+#include <acpi/acpi_iort.h>
 
 struct bootinfo __initdata bootinfo;
 
@@ -796,6 +797,8 @@ void __init start_xen(unsigned long boot_phys_offset,
 
     tasklet_subsys_init();
 
+    /* Parse the ACPI iort data */
+    acpi_iort_init();
 
     xsm_dt_init();
 
xen/drivers/acpi/Makefile +1 -0
index 444b11d..e7ffd82 100644
@@ -1,5 +1,6 @@
 subdir-y += tables
 subdir-y += utilities
+subdir-$(CONFIG_ARM) += arm
 subdir-$(CONFIG_X86) += apei
 
 obj-bin-y += tables.init.o
xen/drivers/acpi/arm/Makefile new file +1 -0
new file mode 100644
index 0000000..7c039bb
@@ -0,0 +1 @@
+obj-y += iort.o
xen/drivers/acpi/arm/iort.c +99 -74

311 lines not on this page · raw message

xen/drivers/passthrough/arm/smmu.c +1 -0
index 362d578..ad956d5 100644
@@ -181,6 +181,7 @@ static void __iomem *devm_ioremap_resource(struct device *dev,
  * Xen: PCI functions
  * TODO: It should be implemented when PCI will be supported
  */
+#undef to_pci_dev
 #define to_pci_dev(dev)	(NULL)
 static inline int pci_for_each_dma_alias(struct pci_dev *pdev,
 					 int (*fn) (struct pci_dev *pdev,
xen/include/acpi/acpi_iort.h +10 -7
index 77e0809..d4315a4 100644
@@ -19,27 +19,32 @@
 #ifndef __ACPI_IORT_H__
 #define __ACPI_IORT_H__
 
-#include <linux/acpi.h>
-#include <linux/fwnode.h>
-#include <linux/irqdomain.h>
+#include <xen/acpi.h>
+#include <asm/device.h>
 
+/* Xen: Not using IORT IRQ bindings */
+#if 0
 #define IORT_IRQ_MASK(irq)		(irq & 0xffffffffULL)
 #define IORT_IRQ_TRIGGER_MASK(irq)	((irq >> 32) & 0xffffffffULL)
 
 int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node);
 void iort_deregister_domain_token(int trans_id);
 struct fwnode_handle *iort_find_domain_token(int trans_id);
-#ifdef CONFIG_ACPI_IORT
+#endif
+#ifdef CONFIG_ARM_64
 void acpi_iort_init(void);
 bool iort_node_match(u8 type);
+#if 0
 u32 iort_msi_map_rid(struct device *dev, u32 req_id);
 struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id);
 /* IOMMU interface */
 void iort_set_dma_mask(struct device *dev);
+#endif
 const struct iommu_ops *iort_iommu_configure(struct device *dev);
 #else
 static inline void acpi_iort_init(void) { }
 static inline bool iort_node_match(u8 type) { return false; }
+#if 0
 static inline u32 iort_msi_map_rid(struct device *dev, u32 req_id)
 { return req_id; }
 static inline struct irq_domain *iort_get_device_domain(struct device *dev,
@@ -47,12 +52,10 @@ static inline struct irq_domain *iort_get_device_domain(struct device *dev,
 { return NULL; }
 /* IOMMU interface */
 static inline void iort_set_dma_mask(struct device *dev) { }
+#endif
 static inline
 const struct iommu_ops *iort_iommu_configure(struct device *dev)
 { return NULL; }
 #endif
 
-#define IORT_ACPI_DECLARE(name, table_id, fn)		\
-	ACPI_DECLARE_PROBE_ENTRY(iort, name, table_id, 0, NULL, 0, fn)
-
 #endif /* __ACPI_IORT_H__ */
xen/include/asm-arm/device.h +2 -0
index 5027c87..4eef9ce 100644
@@ -7,6 +7,7 @@
 enum device_type
 {
     DEV_DT,
+    DEV_ACPI,
 };
 
 struct dev_archdata {
@@ -20,6 +21,7 @@ struct device
 #ifdef CONFIG_HAS_DEVICE_TREE
     struct dt_device_node *of_node; /* Used by drivers imported from Linux */
 #endif
+    void *acpi_node; /*Current use case is acpi_iort_node */
     struct fwnode_handle *fwnode; /*fw device node identifier */
     struct iommu_fwspec *iommu_fwspec;
     struct dev_archdata archdata;
xen/include/xen/acpi.h +21 -0
index 9409350..2f6aae1 100644
@@ -32,6 +32,7 @@
 
 #include <acpi/acpi.h>
 #include <asm/acpi.h>
+#include <xen/fwnode.h>
 
 #define ACPI_MADT_GET_(fld, x) (((x) & ACPI_MADT_##fld##_MASK) / \
 	(ACPI_MADT_##fld##_MASK & -ACPI_MADT_##fld##_MASK))
@@ -49,6 +50,26 @@
                 (!(entry)) || (unsigned long)(entry) + sizeof(*(entry)) > (end) ||  \
                 (entry)->header.length < sizeof(*(entry)))
 
+static inline struct fwnode_handle *acpi_alloc_fwnode_static(void)
+{
+	struct fwnode_handle *fwnode;
+
+	fwnode = xzalloc(struct fwnode_handle);
+	if (!fwnode)
+		return NULL;
+
+	fwnode->type = FWNODE_ACPI_STATIC;
+
+	return fwnode;
+}
+
+static inline void acpi_free_fwnode_static(struct fwnode_handle *fwnode)
+{
+	if (!fwnode || fwnode->type != FWNODE_ACPI_STATIC)
+		return;
+
+	xfree(fwnode);
+}
 #ifdef CONFIG_ACPI
 
 enum acpi_interrupt_id {
xen/include/xen/pci.h +8 -0
index 43f2125..182b1a5 100644
@@ -92,8 +92,16 @@ struct pci_dev {
 #define PT_FAULT_THRESHOLD 10
     } fault;
     u64 vf_rlen[6];
+#ifdef CONFIG_ARM
+    struct device dev;
+#endif
 };
 
+#ifdef CONFIG_ARM
+#define to_pci_dev(p) container_of(p, struct pci_dev,dev)
+#define pci_domain_nr(dev) dev->seg
+#endif
+
 #define for_each_pdev(domain, pdev) \
     list_for_each_entry(pdev, &(domain->arch.pdev_list), domain_list)
 
Signature · 7 lines
Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.


_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#13 lore raw

Re: [RFC v2 5/7] acpi:arm64: Add support for parsing IORT table

Hi Sameer,

Quoted 6 lines · Sameer Goel

Add support for parsing IORT table to initialize SMMU devices.
* The code for creating an SMMU device has been modified, so that the SMMU device can be initialized.
* The NAMED NODE code has been commented out as this will need DOM0 kernel support.
* ITS code has been included but it has not been tested.

Could you please refactor this patch into another set of two patches. I am planning to rebase my IORT for Dom0 Hiding patch rework on this patch. Thanks,
Manish

Quoted 522 lines
---
  xen/arch/arm/setup.c               |   3 +
  xen/drivers/acpi/Makefile          |   1 +
  xen/drivers/acpi/arm/Makefile      |   1 +
  xen/drivers/acpi/arm/iort.c        | 173 +++++++++++++++++++++----------------
  xen/drivers/passthrough/arm/smmu.c |   1 +
  xen/include/acpi/acpi_iort.h       |  17 ++--
  xen/include/asm-arm/device.h       |   2 +
  xen/include/xen/acpi.h             |  21 +++++
  xen/include/xen/pci.h              |   8 ++
  9 files changed, 146 insertions(+), 81 deletions(-)

create mode 100644 xen/drivers/acpi/arm/Makefile

xen/arch/arm/setup.c +3 -0
index 92f173b..4ba09b2 100644
@@ -49,6 +49,7 @@
  #include <asm/setup.h>
  #include <xsm/xsm.h>
  #include <asm/acpi.h>
+#include <acpi/acpi_iort.h>
 
  struct bootinfo __initdata bootinfo;
 
@@ -796,6 +797,8 @@ void __init start_xen(unsigned long boot_phys_offset,
 
      tasklet_subsys_init();
 
+    /* Parse the ACPI iort data */
+    acpi_iort_init();
 
      xsm_dt_init();
 
xen/drivers/acpi/Makefile +1 -0
index 444b11d..e7ffd82 100644
@@ -1,5 +1,6 @@
  subdir-y += tables
  subdir-y += utilities
+subdir-$(CONFIG_ARM) += arm
  subdir-$(CONFIG_X86) += apei
 
  obj-bin-y += tables.init.o
xen/drivers/acpi/arm/Makefile new file +1 -0
new file mode 100644
index 0000000..7c039bb
@@ -0,0 +1 @@
+obj-y += iort.o
xen/drivers/acpi/arm/iort.c +99 -74
index 2e368a6..7f54062 100644
@@ -14,17 +14,47 @@
   * This file implements early detection/parsing of I/O mapping
   * reported to OS through firmware via I/O Remapping Table (IORT)
   * IORT document number: ARM DEN 0049A
+ *
+ * Based on Linux drivers/acpi/arm64/iort.c
+ * => commit ca78d3173cff3503bcd15723b049757f75762d15
+ *
+ * Xen modification:
+ * Sameer Goel <[email protected]>
+ * Copyright (C) 2017, The Linux Foundation, All rights reserved.
+ *
   */
 
-#define pr_fmt(fmt)	"ACPI: IORT: " fmt
-
-#include <linux/acpi_iort.h>
-#include <linux/iommu.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
+#include <xen/acpi.h>
+#include <acpi/acpi_iort.h>
+#include <xen/fwnode.h>
+#include <xen/iommu.h>
+#include <xen/lib.h>
+#include <xen/list.h>
+#include <xen/pci.h>
+
+#include <asm/device.h>
+
+/* Xen: Define compatibility functions */
+#define FW_BUG		"[Firmware Bug]: "
+#define pr_err(fmt, ...) printk(XENLOG_ERR fmt, ## __VA_ARGS__)
+#define pr_warn(fmt, ...) printk(XENLOG_WARNING fmt, ## __VA_ARGS__)
+
+/* Alias to Xen allocation helpers */
+#define kfree xfree
+#define kmalloc(size, flags)            _xmalloc(size, sizeof(void *))
+#define kzalloc(size, flags)            _xzalloc(size, sizeof(void *))
+
+/* Redefine WARN macros */
+#undef WARN
+#undef WARN_ON
+#define WARN(condition, format...) ({					\
+	int __ret_warn_on = !!(condition);				\
+	if (unlikely(__ret_warn_on))					\
+		printk(format);						\
+	unlikely(__ret_warn_on);					\
+})
+#define WARN_TAINT(cond, taint, format...) WARN(cond, format)
+#define WARN_ON(cond)                      (!!cond)
 
  #define IORT_TYPE_MASK(type)	(1 << (type))
  #define IORT_MSI_TYPE		(1 << ACPI_IORT_NODE_ITS_GROUP)
@@ -256,6 +286,13 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
  	acpi_status status;
 
  	if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
+		status = AE_NOT_IMPLEMENTED;
+/*
+ * We need the namespace object name from dsdt to match the iort node, this
+ * will need additions to the kernel xen bus notifiers.
+ * So, disabling the named node code till a proposal is approved.
+ */
+#if 0
  		struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
  		struct acpi_device *adev = to_acpi_device_node(dev->fwnode);
  		struct acpi_iort_named_component *ncomp;
@@ -275,11 +312,12 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
  		status = !strcmp(ncomp->device_name, buf.pointer) ?
  							AE_OK : AE_NOT_FOUND;
  		acpi_os_free(buf.pointer);
+#endif
  	} else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
  		struct acpi_iort_root_complex *pci_rc;
-		struct pci_bus *bus;
+		struct pci_dev *pci_dev;
 
-		bus = to_pci_bus(dev);
+		pci_dev = to_pci_dev(dev);
  		pci_rc = (struct acpi_iort_root_complex *)node->node_data;
 
  		/*
@@ -287,12 +325,11 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
  		 * with root complexes. Each segment number can represent only
  		 * one root complex.
  		 */
-		status = pci_rc->pci_segment_number == pci_domain_nr(bus) ?
+		status = pci_rc->pci_segment_number == pci_domain_nr(pci_dev) ?
  							AE_OK : AE_NOT_FOUND;
  	} else {
  		status = AE_NOT_FOUND;
  	}
-out:
  	return status;
  }
 
@@ -320,6 +357,11 @@ static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,
  	return 0;
  }
 
+/*
+ * Named components are not supported yet so we do not need the
+ * iort_node_get_id function
+ */
+#if 0
  static
  struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
  					u32 *id_out, u8 type_mask,
@@ -358,6 +400,7 @@ struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
 
  	return NULL;
  }
+#endif
 
  static struct acpi_iort_node *iort_node_map_rid(struct acpi_iort_node *node,
  						u32 rid_in, u32 *rid_out,
@@ -410,6 +453,10 @@ fail_map:
  	return NULL;
  }
 
+/* Xen: Comment out the NamedComponent and ITS mapping code till the support
+ * is available.
+ */
+#if 0
  static struct acpi_iort_node *iort_find_dev_node(struct device *dev)
  {
  	struct pci_bus *pbus;
@@ -481,7 +528,7 @@ static int iort_dev_find_its_id(struct device *dev, u32 req_id,
  	return 0;
  }
 
-/**
+/*
   * iort_get_device_domain() - Find MSI domain related to a device
   * @dev: The device.
   * @req_id: Requester ID for the device.
@@ -510,7 +557,7 @@ static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
  	*rid = alias;
  	return 0;
  }
-
+#endif
  static int arm_smmu_iort_xlate(struct device *dev, u32 streamid,
  			       struct fwnode_handle *fwnode,
  			       const struct iommu_ops *ops)
@@ -546,6 +593,7 @@ static const struct iommu_ops *iort_iommu_xlate(struct device *dev,
  	return ret ? NULL : ops;
  }
 
+#if 0 /* Xen: We do not need this function for Xen */
  /**
   * iort_set_dma_mask - Set-up dma mask for a device.
   *
@@ -567,7 +615,7 @@ void iort_set_dma_mask(struct device *dev)
  	if (!dev->dma_mask)
  		dev->dma_mask = &dev->coherent_dma_mask;
  }
-
+#endif
  /**
   * iort_iommu_configure - Set-up IOMMU configuration for a device.
   *
@@ -583,14 +631,13 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
  	u32 streamid = 0;
 
  	if (dev_is_pci(dev)) {
-		struct pci_bus *bus = to_pci_dev(dev)->bus;
+		struct pci_dev *pci_device = to_pci_dev(dev);
  		u32 rid;
 
-		pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
-				       &rid);
+		rid = PCI_BDF2(pci_device->bus, pci_device->devfn);
 
  		node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
-				      iort_match_node_callback, &bus->dev);
+				      iort_match_node_callback, dev);
  		if (!node)
  			return NULL;
 
@@ -600,6 +647,13 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
  		ops = iort_iommu_xlate(dev, parent, streamid);
 
  	} else {
+		return NULL;
+/*
+ * We need the namespace object name from dsdt to match the iort node, this
+ * will need additions to the kernel xen bus notifiers.
+ * So, disabling the named node code till a proposal is approved.
+ */
+#if 0
  		int i = 0;
 
  		node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
@@ -616,11 +670,17 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
  			parent = iort_node_get_id(node, &streamid,
  						  IORT_IOMMU_TYPE, i++);
  		}
+#endif
  	}
 
  	return ops;
  }
 
+/*
+ * Xen: Not using the parsing ops for now. Need to check and see if it will
+ * be useful to use these in some form, or let the driver parse IORT node.
+ */
+#if 0
  static void __init acpi_iort_register_irq(int hwirq, const char *name,
  					  int trigger,
  					  struct resource *res)
@@ -807,7 +867,7 @@ const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node)
  		return NULL;
  	}
  }
-
+#endif
  /**
   * iort_add_smmu_platform_device() - Allocate a platform device for SMMU
   * @node: Pointer to SMMU ACPI IORT node
@@ -817,78 +877,42 @@ const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node)
  static int __init iort_add_smmu_platform_device(struct acpi_iort_node *node)
  {
  	struct fwnode_handle *fwnode;
-	struct platform_device *pdev;
-	struct resource *r;
-	enum dev_dma_attr attr;
-	int ret, count;
-	const struct iort_iommu_config *ops = iort_get_iommu_cfg(node);
-
-	if (!ops)
-		return -ENODEV;
-
-	pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO);
-	if (!pdev)
-		return -ENOMEM;
-
-	count = ops->iommu_count_resources(node);
-
-	r = kcalloc(count, sizeof(*r), GFP_KERNEL);
-	if (!r) {
-		ret = -ENOMEM;
-		goto dev_put;
-	}
-
-	ops->iommu_init_resources(r, node);
+	struct device *dev;
+	int ret;
 
-	ret = platform_device_add_resources(pdev, r, count);
  	/*
-	 * Resources are duplicated in platform_device_add_resources,
-	 * free their allocated memory
+	 * Not enabling the parsing ops for now. The corresponding driver
+	 * can parse this information as needed, so deleting relevant code as
+	 * compared to base revision.
  	 */
-	kfree(r);
 
-	if (ret)
-		goto dev_put;
+	dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
 
  	/*
  	 * Add a copy of IORT node pointer to platform_data to
  	 * be used to retrieve IORT data information.
  	 */
-	ret = platform_device_add_data(pdev, &node, sizeof(node));
-	if (ret)
-		goto dev_put;
-
-	/*
-	 * We expect the dma masks to be equivalent for
-	 * all SMMUs set-ups
-	 */
-	pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+	dev->type = DEV_ACPI;
+	dev->acpi_node = node;
 
  	fwnode = iort_get_fwnode(node);
 
  	if (!fwnode) {
  		ret = -ENODEV;
-		goto dev_put;
+		goto error;
  	}
 
-	pdev->dev.fwnode = fwnode;
-
-	attr = ops->iommu_is_coherent(node) ?
-			     DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT;
-
-	/* Configure DMA for the page table walker */
-	acpi_dma_configure(&pdev->dev, attr);
+	dev->fwnode = fwnode;
 
-	ret = platform_device_add(pdev);
-	if (ret)
-		goto dma_deconfigure;
+	/* Call the acpi init functions for IOMMU devices */
+	ret = acpi_device_init(DEVICE_IOMMU, (void *)dev, node->type);
 
  	return 0;
 
-dma_deconfigure:
-	acpi_dma_deconfigure(&pdev->dev);
-dev_put:
-	platform_device_put(pdev);
+error:
+	kfree(dev);
 
  	return ret;
  }
@@ -957,5 +981,6 @@ void __init acpi_iort_init(void)
 
  	iort_init_platform_devices();
 
-	acpi_probe_device_table(iort);
+	/* Xen; Do not need a device table probe */
+	/* acpi_probe_device_table(iort);*/
  }
xen/drivers/passthrough/arm/smmu.c +1 -0
index 362d578..ad956d5 100644
@@ -181,6 +181,7 @@ static void __iomem *devm_ioremap_resource(struct device *dev,
   * Xen: PCI functions
   * TODO: It should be implemented when PCI will be supported
   */
+#undef to_pci_dev
  #define to_pci_dev(dev)	(NULL)
  static inline int pci_for_each_dma_alias(struct pci_dev *pdev,
  					 int (*fn) (struct pci_dev *pdev,
xen/include/acpi/acpi_iort.h +10 -7
index 77e0809..d4315a4 100644
@@ -19,27 +19,32 @@
  #ifndef __ACPI_IORT_H__
  #define __ACPI_IORT_H__
 
-#include <linux/acpi.h>
-#include <linux/fwnode.h>
-#include <linux/irqdomain.h>
+#include <xen/acpi.h>
+#include <asm/device.h>
 
+/* Xen: Not using IORT IRQ bindings */
+#if 0
  #define IORT_IRQ_MASK(irq)		(irq & 0xffffffffULL)
  #define IORT_IRQ_TRIGGER_MASK(irq)	((irq >> 32) & 0xffffffffULL)
 
  int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node);
  void iort_deregister_domain_token(int trans_id);
  struct fwnode_handle *iort_find_domain_token(int trans_id);
-#ifdef CONFIG_ACPI_IORT
+#endif
+#ifdef CONFIG_ARM_64
  void acpi_iort_init(void);
  bool iort_node_match(u8 type);
+#if 0
  u32 iort_msi_map_rid(struct device *dev, u32 req_id);
  struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id);
  /* IOMMU interface */
  void iort_set_dma_mask(struct device *dev);
+#endif
  const struct iommu_ops *iort_iommu_configure(struct device *dev);
  #else
  static inline void acpi_iort_init(void) { }
  static inline bool iort_node_match(u8 type) { return false; }
+#if 0
  static inline u32 iort_msi_map_rid(struct device *dev, u32 req_id)
  { return req_id; }
  static inline struct irq_domain *iort_get_device_domain(struct device *dev,
@@ -47,12 +52,10 @@ static inline struct irq_domain *iort_get_device_domain(struct device *dev,
  { return NULL; }
  /* IOMMU interface */
  static inline void iort_set_dma_mask(struct device *dev) { }
+#endif
  static inline
  const struct iommu_ops *iort_iommu_configure(struct device *dev)
  { return NULL; }
  #endif
 
-#define IORT_ACPI_DECLARE(name, table_id, fn)		\
-	ACPI_DECLARE_PROBE_ENTRY(iort, name, table_id, 0, NULL, 0, fn)
-
  #endif /* __ACPI_IORT_H__ */
xen/include/asm-arm/device.h +2 -0
index 5027c87..4eef9ce 100644
@@ -7,6 +7,7 @@
  enum device_type
  {
      DEV_DT,
+    DEV_ACPI,
  };
 
  struct dev_archdata {
@@ -20,6 +21,7 @@ struct device
  #ifdef CONFIG_HAS_DEVICE_TREE
      struct dt_device_node *of_node; /* Used by drivers imported from Linux */
  #endif
+    void *acpi_node; /*Current use case is acpi_iort_node */
      struct fwnode_handle *fwnode; /*fw device node identifier */
      struct iommu_fwspec *iommu_fwspec;
      struct dev_archdata archdata;
xen/include/xen/acpi.h +21 -0
index 9409350..2f6aae1 100644
@@ -32,6 +32,7 @@
 
  #include <acpi/acpi.h>
  #include <asm/acpi.h>
+#include <xen/fwnode.h>
 
  #define ACPI_MADT_GET_(fld, x) (((x) & ACPI_MADT_##fld##_MASK) / \
  	(ACPI_MADT_##fld##_MASK & -ACPI_MADT_##fld##_MASK))
@@ -49,6 +50,26 @@
                  (!(entry)) || (unsigned long)(entry) + sizeof(*(entry)) > (end) ||  \
                  (entry)->header.length < sizeof(*(entry)))
 
+static inline struct fwnode_handle *acpi_alloc_fwnode_static(void)
+{
+	struct fwnode_handle *fwnode;
+
+	fwnode = xzalloc(struct fwnode_handle);
+	if (!fwnode)
+		return NULL;
+
+	fwnode->type = FWNODE_ACPI_STATIC;
+
+	return fwnode;
+}
+
+static inline void acpi_free_fwnode_static(struct fwnode_handle *fwnode)
+{
+	if (!fwnode || fwnode->type != FWNODE_ACPI_STATIC)
+		return;
+
+	xfree(fwnode);
+}
  #ifdef CONFIG_ACPI
 
  enum acpi_interrupt_id {
xen/include/xen/pci.h +3 -0
index 43f2125..182b1a5 100644
@@ -92,8 +92,16 @@ struct pci_dev {
  #define PT_FAULT_THRESHOLD 10
      } fault;
      u64 vf_rlen[6];
+#ifdef CONFIG_ARM
+    struct device dev;
+#endif

xen/include/xen/pci.h struct pci_dev { · line 107

Quoted 10 lines
xen/include/xen/pci.h +5 -0
  };
 
+#ifdef CONFIG_ARM
+#define to_pci_dev(p) container_of(p, struct pci_dev,dev)
+#define pci_domain_nr(dev) dev->seg
+#endif
+
  #define for_each_pdev(domain, pdev) \
      list_for_each_entry(pdev, &(domain->arch.pdev_list), domain_list)
 

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel

#14 lore raw

Re: [RFC v2 5/7] acpi:arm64: Add support for parsing IORT table

Quoted 10 lines · Manish Jaggi

Hi Sameer,

Quoted 6 lines · Sameer Goel

Add support for parsing IORT table to initialize SMMU devices.
* The code for creating an SMMU device has been modified, so that the SMMU device can be initialized.
* The NAMED NODE code has been commented out as this will need DOM0 kernel support.
* ITS code has been included but it has not been tested.

Could you please refactor this patch into another set of two patches. I am planning to rebase my IORT for Dom0 Hiding patch rework on this patch.

I will try to break this up. Lets discuss this a bit more next week.

Quoted 491 lines

Thanks,
Manish

Quoted 480 lines

---
  xen/arch/arm/setup.c               |   3 + xen/drivers/acpi/Makefile          |   1 + xen/drivers/acpi/arm/Makefile      |   1 + xen/drivers/acpi/arm/iort.c        | 173 +++++++++++++++++++++---------------- xen/drivers/passthrough/arm/smmu.c |   1 + xen/include/acpi/acpi_iort.h       |  17 ++-- xen/include/asm-arm/device.h       |   2 +
  xen/include/xen/acpi.h             |  21 +++++ xen/include/xen/pci.h              |   8 ++

  9 files changed, 146 insertions(+), 81 deletions(-)

  create mode 100644 xen/drivers/acpi/arm/Makefile

xen/arch/arm/setup.c +1 -0
index 92f173b..4ba09b2 100644
@@ -49,6 +49,7 @@
   #include <asm/setup.h>
   #include <xsm/xsm.h>
   #include <asm/acpi.h>
+#include <acpi/acpi_iort.h>
     struct bootinfo __initdata bootinfo;
   @@ -796,6 +797,8 @@ void __init start_xen(unsigned long boot_phys_offset,
         tasklet_subsys_init();

  +    /* Parse the ACPI iort data */
+    acpi_iort_init();
        xsm_dt_init();
  diff --git a/xen/drivers/acpi/Makefile b/xen/drivers/acpi/Makefile index 444b11d..e7ffd82 100644

xen/drivers/acpi/Makefile +0 -0
@@ -1,5 +1,6 @@

  subdir-y += tables
  subdir-y += utilities
+subdir-$(CONFIG_ARM) += arm
  subdir-$(CONFIG_X86) += apei
    obj-bin-y += tables.init.o

xen/drivers/acpi/arm/Makefile new file +1 -0
new file mode 100644
index 0000000..7c039bb
@@ -0,0 +1 @@
+obj-y += iort.o
xen/drivers/acpi/arm/iort.c +8 -0
index 2e368a6..7f54062 100644
@@ -14,17 +14,47 @@
    * This file implements early detection/parsing of I/O mapping
    * reported to OS through firmware via I/O Remapping Table (IORT)
    * IORT document number: ARM DEN 0049A
+ *
+ * Based on Linux drivers/acpi/arm64/iort.c
+ * => commit ca78d3173cff3503bcd15723b049757f75762d15
+ *
+ * Xen modification:
+ * Sameer Goel <[email protected]>
+ * Copyright (C) 2017, The Linux Foundation, All rights reserved.
+ *
    */

  -#define pr_fmt(fmt)    "ACPI: IORT: " fmt

xen/drivers/acpi/arm/iort.c +31 -8
-
-#include <linux/acpi_iort.h>
-#include <linux/iommu.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
+#include <xen/acpi.h>
+#include <acpi/acpi_iort.h>
+#include <xen/fwnode.h>
+#include <xen/iommu.h>
+#include <xen/lib.h>
+#include <xen/list.h>
+#include <xen/pci.h>
+
+#include <asm/device.h>
+
+/* Xen: Define compatibility functions */
+#define FW_BUG        "[Firmware Bug]: "
+#define pr_err(fmt, ...) printk(XENLOG_ERR fmt, ## __VA_ARGS__)
+#define pr_warn(fmt, ...) printk(XENLOG_WARNING fmt, ## __VA_ARGS__)
+
+/* Alias to Xen allocation helpers */
+#define kfree xfree
+#define kmalloc(size, flags)            _xmalloc(size, sizeof(void *))
+#define kzalloc(size, flags)            _xzalloc(size, sizeof(void *))
+
+/* Redefine WARN macros */
+#undef WARN
+#undef WARN_ON
+#define WARN(condition, format...) ({                    \
+    int __ret_warn_on = !!(condition);                \
+    if (unlikely(__ret_warn_on))                    \
+        printk(format);                        \
+    unlikely(__ret_warn_on);                    \
+})
+#define WARN_TAINT(cond, taint, format...) WARN(cond, format)
+#define WARN_ON(cond)                      (!!cond)

    #define IORT_TYPE_MASK(type)    (1 << (type)) #define IORT_MSI_TYPE        (1 << ACPI_IORT_NODE_ITS_GROUP)

xen/drivers/acpi/arm/iort.c +7 -0
@@ -256,6 +286,13 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
       acpi_status status;
         if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
+        status = AE_NOT_IMPLEMENTED;
+/*
+ * We need the namespace object name from dsdt to match the iort node, this
+ * will need additions to the kernel xen bus notifiers.
+ * So, disabling the named node code till a proposal is approved.
+ */
+#if 0
           struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
           struct acpi_device *adev = to_acpi_device_node(dev->fwnode);
           struct acpi_iort_named_component *ncomp;
@@ -275,11 +312,12 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,

          status = !strcmp(ncomp->device_name, buf.pointer) ? AE_OK : AE_NOT_FOUND; acpi_os_free(buf.pointer);
+#endif
      } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { struct acpi_iort_root_complex *pci_rc;
-        struct pci_bus *bus;
+        struct pci_dev *pci_dev;
  -        bus = to_pci_bus(dev);
+        pci_dev = to_pci_dev(dev);
          pci_rc = (struct acpi_iort_root_complex *)node->node_data; /*

xen/drivers/acpi/arm/iort.c +1 -2
@@ -287,12 +325,11 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
            * with root complexes. Each segment number can represent only
            * one root complex.
            */
-        status = pci_rc->pci_segment_number == pci_domain_nr(bus) ?
+        status = pci_rc->pci_segment_number == pci_domain_nr(pci_dev) ?
                               AE_OK : AE_NOT_FOUND;
       } else {
           status = AE_NOT_FOUND;
       }
-out:
       return status;
   }
   @@ -320,6 +357,11 @@ static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,

      return 0;
  }
  +/*
+ * Named components are not supported yet so we do not need the
+ * iort_node_get_id function
+ */
+#if 0
  static
  struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node, u32 *id_out, u8 type_mask,

xen/drivers/acpi/arm/iort.c +1 -0
@@ -358,6 +400,7 @@ struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
         return NULL;
   }
+#endif
     static struct acpi_iort_node *iort_node_map_rid(struct acpi_iort_node *node,
                           u32 rid_in, u32 *rid_out,
@@ -410,6 +453,10 @@ fail_map:
       return NULL;
   }

  +/* Xen: Comment out the NamedComponent and ITS mapping code till the support
+ * is available.
+ */
+#if 0
  static struct acpi_iort_node *iort_find_dev_node(struct device *dev) {
      struct pci_bus *pbus;

xen/drivers/acpi/arm/iort.c +0 -0
@@ -481,7 +528,7 @@ static int iort_dev_find_its_id(struct device *dev, u32 req_id,
       return 0;
   }

  -/**
+/*
   * iort_get_device_domain() - Find MSI domain related to a device
   * @dev: The device.
   * @req_id: Requester ID for the device.

xen/drivers/acpi/arm/iort.c +1 -1
@@ -510,7 +557,7 @@ static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
       *rid = alias;
       return 0;
   }
-
+#endif
   static int arm_smmu_iort_xlate(struct device *dev, u32 streamid,
                      struct fwnode_handle *fwnode,
                      const struct iommu_ops *ops)
@@ -546,6 +593,7 @@ static const struct iommu_ops *iort_iommu_xlate(struct device *dev,
       return ret ? NULL : ops;
   }

  +#if 0 /* Xen: We do not need this function for Xen */ /**
   * iort_set_dma_mask - Set-up dma mask for a device. *

xen/drivers/acpi/arm/iort.c +11 -4
@@ -567,7 +615,7 @@ void iort_set_dma_mask(struct device *dev)
       if (!dev->dma_mask)
           dev->dma_mask = &dev->coherent_dma_mask;
   }
-
+#endif
   /**
    * iort_iommu_configure - Set-up IOMMU configuration for a device.
    *
@@ -583,14 +631,13 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
       u32 streamid = 0;
         if (dev_is_pci(dev)) {
-        struct pci_bus *bus = to_pci_dev(dev)->bus;
+        struct pci_dev *pci_device = to_pci_dev(dev);
           u32 rid;
   -        pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
-                       &rid);
+        rid = PCI_BDF2(pci_device->bus, pci_device->devfn);
             node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
-                      iort_match_node_callback, &bus->dev);
+                      iort_match_node_callback, dev);
           if (!node)
               return NULL;
   @@ -600,6 +647,13 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
           ops = iort_iommu_xlate(dev, parent, streamid);
         } else {
+        return NULL;
+/*
+ * We need the namespace object name from dsdt to match the iort node, this
+ * will need additions to the kernel xen bus notifiers.
+ * So, disabling the named node code till a proposal is approved.
+ */
+#if 0

          int i = 0;
            node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,

xen/drivers/acpi/arm/iort.c +1 -0
@@ -616,11 +670,17 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
               parent = iort_node_get_id(node, &streamid,
                             IORT_IOMMU_TYPE, i++);
           }
+#endif
       }
         return ops;
   }

  +/*
+ * Xen: Not using the parsing ops for now. Need to check and see if it will
+ * be useful to use these in some form, or let the driver parse IORT node.
+ */
+#if 0
  static void __init acpi_iort_register_irq(int hwirq, const char *name, int trigger,
                        struct resource *res)

xen/drivers/acpi/arm/iort.c +15 -45
@@ -807,7 +867,7 @@ const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node)
           return NULL;
       }
   }
-
+#endif
   /**
    * iort_add_smmu_platform_device() - Allocate a platform device for SMMU
    * @node: Pointer to SMMU ACPI IORT node
@@ -817,78 +877,42 @@ const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node)
   static int __init iort_add_smmu_platform_device(struct acpi_iort_node *node)
   {
       struct fwnode_handle *fwnode;
-    struct platform_device *pdev;
-    struct resource *r;
-    enum dev_dma_attr attr;
-    int ret, count;
-    const struct iort_iommu_config *ops = iort_get_iommu_cfg(node);
-
-    if (!ops)
-        return -ENODEV;
-
-    pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO);
-    if (!pdev)
-        return -ENOMEM;
-
-    count = ops->iommu_count_resources(node);
-
-    r = kcalloc(count, sizeof(*r), GFP_KERNEL);
-    if (!r) {
-        ret = -ENOMEM;
-        goto dev_put;
-    }
-
-    ops->iommu_init_resources(r, node);
+    struct device *dev;
+    int ret;
   -    ret = platform_device_add_resources(pdev, r, count);
       /*
-     * Resources are duplicated in platform_device_add_resources,
-     * free their allocated memory
+     * Not enabling the parsing ops for now. The corresponding driver
+     * can parse this information as needed, so deleting relevant code as
+     * compared to base revision.
        */
-    kfree(r);
   -    if (ret)
-        goto dev_put;
+    dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+    if (!dev)
+        return -ENOMEM;
         /*
        * Add a copy of IORT node pointer to platform_data to
        * be used to retrieve IORT data information.
        */
-    ret = platform_device_add_data(pdev, &node, sizeof(node));
-    if (ret)
-        goto dev_put;
-
-    /*
-     * We expect the dma masks to be equivalent for
-     * all SMMUs set-ups
-     */
-    pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+    dev->type = DEV_ACPI;
+    dev->acpi_node = node;
         fwnode = iort_get_fwnode(node);
         if (!fwnode) {
           ret = -ENODEV;
-        goto dev_put;
+        goto error;
       }
   -    pdev->dev.fwnode = fwnode;
-
-    attr = ops->iommu_is_coherent(node) ?
-                 DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT;
-
-    /* Configure DMA for the page table walker */
-    acpi_dma_configure(&pdev->dev, attr);
+    dev->fwnode = fwnode;
   -    ret = platform_device_add(pdev);
-    if (ret)
-        goto dma_deconfigure;
+    /* Call the acpi init functions for IOMMU devices */
+    ret = acpi_device_init(DEVICE_IOMMU, (void *)dev, node->type);
         return 0;

  -dma_deconfigure:
-    acpi_dma_deconfigure(&pdev->dev);

xen/drivers/acpi/arm/iort.c +2 -2
-dev_put:
-    platform_device_put(pdev);
+error:
+    kfree(dev);

        return ret;
  }

xen/drivers/acpi/arm/iort.c +2 -0
@@ -957,5 +981,6 @@ void __init acpi_iort_init(void)
         iort_init_platform_devices();
   -    acpi_probe_device_table(iort);
+    /* Xen; Do not need a device table probe */
+    /* acpi_probe_device_table(iort);*/
   }
xen/drivers/passthrough/arm/smmu.c +1 -0
index 362d578..ad956d5 100644
@@ -181,6 +181,7 @@ static void __iomem *devm_ioremap_resource(struct device *dev,
    * Xen: PCI functions
    * TODO: It should be implemented when PCI will be supported
    */
+#undef to_pci_dev
   #define to_pci_dev(dev)    (NULL)
   static inline int pci_for_each_dma_alias(struct pci_dev *pdev,
                        int (*fn) (struct pci_dev *pdev,
xen/include/acpi/acpi_iort.h +0 -0
index 77e0809..d4315a4 100644
@@ -19,27 +19,32 @@
   #ifndef __ACPI_IORT_H__
   #define __ACPI_IORT_H__

  -#include <linux/acpi.h>

xen/include/acpi/acpi_iort.h +2 -2
-#include <linux/fwnode.h>
-#include <linux/irqdomain.h>
+#include <xen/acpi.h>
+#include <asm/device.h>

  +/* Xen: Not using IORT IRQ bindings */
+#if 0
  #define IORT_IRQ_MASK(irq)        (irq & 0xffffffffULL)
  #define IORT_IRQ_TRIGGER_MASK(irq)    ((irq >> 32) & 0xffffffffULL) int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node); void iort_deregister_domain_token(int trans_id);
  struct fwnode_handle *iort_find_domain_token(int trans_id);

xen/include/acpi/acpi_iort.h +2 -1
-#ifdef CONFIG_ACPI_IORT
+#endif
+#ifdef CONFIG_ARM_64

  void acpi_iort_init(void);
  bool iort_node_match(u8 type);
+#if 0
  u32 iort_msi_map_rid(struct device *dev, u32 req_id);
  struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id); /* IOMMU interface */
  void iort_set_dma_mask(struct device *dev);
+#endif
  const struct iommu_ops *iort_iommu_configure(struct device *dev); #else
  static inline void acpi_iort_init(void) { }
  static inline bool iort_node_match(u8 type) { return false; } +#if 0
  static inline u32 iort_msi_map_rid(struct device *dev, u32 req_id) { return req_id; }
  static inline struct irq_domain *iort_get_device_domain(struct device *dev,

xen/include/acpi/acpi_iort.h +1 -0
@@ -47,12 +52,10 @@ static inline struct irq_domain *iort_get_device_domain(struct device *dev,
   { return NULL; }
   /* IOMMU interface */
   static inline void iort_set_dma_mask(struct device *dev) { }
+#endif

  static inline
  const struct iommu_ops *iort_iommu_configure(struct device *dev) { return NULL; }
  #endif
  -#define IORT_ACPI_DECLARE(name, table_id, fn)        \
-    ACPI_DECLARE_PROBE_ENTRY(iort, name, table_id, 0, NULL, 0, fn) -
  #endif /* __ACPI_IORT_H__ */

xen/include/asm-arm/device.h +0 -0
index 5027c87..4eef9ce 100644
@@ -7,6 +7,7 @@

  enum device_type
  {
      DEV_DT,
+    DEV_ACPI,
  };
    struct dev_archdata {

xen/include/asm-arm/device.h +0 -0
@@ -20,6 +21,7 @@ struct device
   #ifdef CONFIG_HAS_DEVICE_TREE

      struct dt_device_node *of_node; /* Used by drivers imported from Linux */ #endif
+    void *acpi_node; /*Current use case is acpi_iort_node */ struct fwnode_handle *fwnode; /*fw device node identifier */ struct iommu_fwspec *iommu_fwspec;
      struct dev_archdata archdata;

xen/include/xen/acpi.h +20 -0
index 9409350..2f6aae1 100644
@@ -32,6 +32,7 @@
     #include <acpi/acpi.h>
   #include <asm/acpi.h>
+#include <xen/fwnode.h>
     #define ACPI_MADT_GET_(fld, x) (((x) & ACPI_MADT_##fld##_MASK) / \
       (ACPI_MADT_##fld##_MASK & -ACPI_MADT_##fld##_MASK))
@@ -49,6 +50,26 @@
                   (!(entry)) || (unsigned long)(entry) + sizeof(*(entry)) > (end) ||  \
                   (entry)->header.length < sizeof(*(entry)))
   +static inline struct fwnode_handle *acpi_alloc_fwnode_static(void)
+{
+    struct fwnode_handle *fwnode;
+
+    fwnode = xzalloc(struct fwnode_handle);
+    if (!fwnode)
+        return NULL;
+
+    fwnode->type = FWNODE_ACPI_STATIC;
+
+    return fwnode;
+}
+
+static inline void acpi_free_fwnode_static(struct fwnode_handle *fwnode)
+{
+    if (!fwnode || fwnode->type != FWNODE_ACPI_STATIC)
+        return;
+
+    xfree(fwnode);
+}
   #ifdef CONFIG_ACPI
     enum acpi_interrupt_id {
xen/include/xen/pci.h +3 -0
index 43f2125..182b1a5 100644
@@ -92,8 +92,16 @@ struct pci_dev {
   #define PT_FAULT_THRESHOLD 10
       } fault;
       u64 vf_rlen[6];
+#ifdef CONFIG_ARM
+    struct device dev;
+#endif
   };

  +#ifdef CONFIG_ARM

xen/include/xen/pci.h +4 -0
+#define to_pci_dev(p) container_of(p, struct pci_dev,dev)
+#define pci_domain_nr(dev) dev->seg
+#endif
+
Quoted 3 lines

  #define for_each_pdev(domain, pdev) \
      list_for_each_entry(pdev, &(domain->arch.pdev_list), domain_list)

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel

Signature · 6 lines
 Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#15 lore raw

Re: [RFC v2 5/7] acpi:arm64: Add support for parsing IORT table

Quoted 9 lines · Sameer
Quoted 8 lines · Manish Jaggi

Hi Sameer,

Quoted 6 lines · Sameer Goel

Add support for parsing IORT table to initialize SMMU devices.
* The code for creating an SMMU device has been modified, so that the SMMU device can be initialized.
* The NAMED NODE code has been commented out as this will need DOM0 kernel support.
* ITS code has been included but it has not been tested.

Quoted 3 lines
Quoted 2 lines

Could you please refactor this patch into another set of two patches. I am planning to rebase my IORT for Dom0 Hiding patch rework on this patch.

I will try to break this up. Lets discuss this a bit more next week.

Please have a look at the draft design. [1]
[1] https://www.mail-archive.com/[email protected]/msg125951.html

Quoted 482 lines
Quoted 482 lines

Thanks,
Manish

Quoted 480 lines
---
   xen/arch/arm/setup.c               |   3 +
   xen/drivers/acpi/Makefile          |   1 +
   xen/drivers/acpi/arm/Makefile      |   1 +
   xen/drivers/acpi/arm/iort.c        | 173 +++++++++++++++++++++----------------
   xen/drivers/passthrough/arm/smmu.c |   1 +
   xen/include/acpi/acpi_iort.h       |  17 ++--
   xen/include/asm-arm/device.h       |   2 +
   xen/include/xen/acpi.h             |  21 +++++
   xen/include/xen/pci.h              |   8 ++
   9 files changed, 146 insertions(+), 81 deletions(-)

create mode 100644 xen/drivers/acpi/arm/Makefile

xen/arch/arm/setup.c +1 -0
index 92f173b..4ba09b2 100644
@@ -49,6 +49,7 @@
   #include <asm/setup.h>
   #include <xsm/xsm.h>
   #include <asm/acpi.h>
+#include <acpi/acpi_iort.h>
     struct bootinfo __initdata bootinfo;
   @@ -796,6 +797,8 @@ void __init start_xen(unsigned long boot_phys_offset,
         tasklet_subsys_init();

+ /* Parse the ACPI iort data */

+    acpi_iort_init();
         xsm_dt_init();

diff --git a/xen/drivers/acpi/Makefile b/xen/drivers/acpi/Makefile
index 444b11d..e7ffd82 100644

xen/drivers/acpi/Makefile +1 -0
@@ -1,5 +1,6 @@
   subdir-y += tables
   subdir-y += utilities
+subdir-$(CONFIG_ARM) += arm
   subdir-$(CONFIG_X86) += apei
     obj-bin-y += tables.init.o
xen/drivers/acpi/arm/Makefile new file +1 -0
new file mode 100644
index 0000000..7c039bb
@@ -0,0 +1 @@
+obj-y += iort.o
xen/drivers/acpi/arm/iort.c +50 -11
index 2e368a6..7f54062 100644
@@ -14,17 +14,47 @@
    * This file implements early detection/parsing of I/O mapping
    * reported to OS through firmware via I/O Remapping Table (IORT)
    * IORT document number: ARM DEN 0049A
+ *
+ * Based on Linux drivers/acpi/arm64/iort.c
+ * => commit ca78d3173cff3503bcd15723b049757f75762d15
+ *
+ * Xen modification:
+ * Sameer Goel <[email protected]>
+ * Copyright (C) 2017, The Linux Foundation, All rights reserved.
+ *
    */
   -#define pr_fmt(fmt)    "ACPI: IORT: " fmt
-
-#include <linux/acpi_iort.h>
-#include <linux/iommu.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
+#include <xen/acpi.h>
+#include <acpi/acpi_iort.h>
+#include <xen/fwnode.h>
+#include <xen/iommu.h>
+#include <xen/lib.h>
+#include <xen/list.h>
+#include <xen/pci.h>
+
+#include <asm/device.h>
+
+/* Xen: Define compatibility functions */
+#define FW_BUG        "[Firmware Bug]: "
+#define pr_err(fmt, ...) printk(XENLOG_ERR fmt, ## __VA_ARGS__)
+#define pr_warn(fmt, ...) printk(XENLOG_WARNING fmt, ## __VA_ARGS__)
+
+/* Alias to Xen allocation helpers */
+#define kfree xfree
+#define kmalloc(size, flags)            _xmalloc(size, sizeof(void *))
+#define kzalloc(size, flags)            _xzalloc(size, sizeof(void *))
+
+/* Redefine WARN macros */
+#undef WARN
+#undef WARN_ON
+#define WARN(condition, format...) ({                    \
+    int __ret_warn_on = !!(condition);                \
+    if (unlikely(__ret_warn_on))                    \
+        printk(format);                        \
+    unlikely(__ret_warn_on);                    \
+})
+#define WARN_TAINT(cond, taint, format...) WARN(cond, format)
+#define WARN_ON(cond)                      (!!cond)
     #define IORT_TYPE_MASK(type)    (1 << (type))
   #define IORT_MSI_TYPE        (1 << ACPI_IORT_NODE_ITS_GROUP)
@@ -256,6 +286,13 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
       acpi_status status;
         if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
+        status = AE_NOT_IMPLEMENTED;
+/*
+ * We need the namespace object name from dsdt to match the iort node, this
+ * will need additions to the kernel xen bus notifiers.
+ * So, disabling the named node code till a proposal is approved.
+ */
+#if 0
           struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
           struct acpi_device *adev = to_acpi_device_node(dev->fwnode);
           struct acpi_iort_named_component *ncomp;
@@ -275,11 +312,12 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
           status = !strcmp(ncomp->device_name, buf.pointer) ?
                               AE_OK : AE_NOT_FOUND;
           acpi_os_free(buf.pointer);
+#endif
       } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
           struct acpi_iort_root_complex *pci_rc;
-        struct pci_bus *bus;
+        struct pci_dev *pci_dev;
   -        bus = to_pci_bus(dev);
+        pci_dev = to_pci_dev(dev);
           pci_rc = (struct acpi_iort_root_complex *)node->node_data;
             /*
@@ -287,12 +325,11 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
            * with root complexes. Each segment number can represent only
            * one root complex.
            */
-        status = pci_rc->pci_segment_number == pci_domain_nr(bus) ?
+        status = pci_rc->pci_segment_number == pci_domain_nr(pci_dev) ?
                               AE_OK : AE_NOT_FOUND;
       } else {
           status = AE_NOT_FOUND;
       }
-out:
       return status;
   }
   @@ -320,6 +357,11 @@ static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,
       return 0;

}
+/*
+ * Named components are not supported yet so we do not need the
+ * iort_node_get_id function
+ */

xen/drivers/acpi/arm/iort.c +42 -53
+#if 0
   static
   struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
                       u32 *id_out, u8 type_mask,
@@ -358,6 +400,7 @@ struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
         return NULL;
   }
+#endif
     static struct acpi_iort_node *iort_node_map_rid(struct acpi_iort_node *node,
                           u32 rid_in, u32 *rid_out,
@@ -410,6 +453,10 @@ fail_map:
       return NULL;
   }
   +/* Xen: Comment out the NamedComponent and ITS mapping code till the support
+ * is available.
+ */
+#if 0
   static struct acpi_iort_node *iort_find_dev_node(struct device *dev)
   {
       struct pci_bus *pbus;
@@ -481,7 +528,7 @@ static int iort_dev_find_its_id(struct device *dev, u32 req_id,
       return 0;
   }
   -/**
+/*
    * iort_get_device_domain() - Find MSI domain related to a device
    * @dev: The device.
    * @req_id: Requester ID for the device.
@@ -510,7 +557,7 @@ static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
       *rid = alias;
       return 0;
   }
-
+#endif
   static int arm_smmu_iort_xlate(struct device *dev, u32 streamid,
                      struct fwnode_handle *fwnode,
                      const struct iommu_ops *ops)
@@ -546,6 +593,7 @@ static const struct iommu_ops *iort_iommu_xlate(struct device *dev,
       return ret ? NULL : ops;
   }
   +#if 0 /* Xen: We do not need this function for Xen */
   /**
    * iort_set_dma_mask - Set-up dma mask for a device.
    *
@@ -567,7 +615,7 @@ void iort_set_dma_mask(struct device *dev)
       if (!dev->dma_mask)
           dev->dma_mask = &dev->coherent_dma_mask;
   }
-
+#endif
   /**
    * iort_iommu_configure - Set-up IOMMU configuration for a device.
    *
@@ -583,14 +631,13 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
       u32 streamid = 0;
         if (dev_is_pci(dev)) {
-        struct pci_bus *bus = to_pci_dev(dev)->bus;
+        struct pci_dev *pci_device = to_pci_dev(dev);
           u32 rid;
   -        pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
-                       &rid);
+        rid = PCI_BDF2(pci_device->bus, pci_device->devfn);
             node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
-                      iort_match_node_callback, &bus->dev);
+                      iort_match_node_callback, dev);
           if (!node)
               return NULL;
   @@ -600,6 +647,13 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
           ops = iort_iommu_xlate(dev, parent, streamid);
         } else {
+        return NULL;
+/*
+ * We need the namespace object name from dsdt to match the iort node, this
+ * will need additions to the kernel xen bus notifiers.
+ * So, disabling the named node code till a proposal is approved.
+ */
+#if 0
           int i = 0;
             node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
@@ -616,11 +670,17 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
               parent = iort_node_get_id(node, &streamid,
                             IORT_IOMMU_TYPE, i++);
           }
+#endif
       }
         return ops;
   }
   +/*
+ * Xen: Not using the parsing ops for now. Need to check and see if it will
+ * be useful to use these in some form, or let the driver parse IORT node.
+ */
+#if 0
   static void __init acpi_iort_register_irq(int hwirq, const char *name,
                         int trigger,
                         struct resource *res)
@@ -807,7 +867,7 @@ const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node)
           return NULL;
       }
   }
-
+#endif
   /**
    * iort_add_smmu_platform_device() - Allocate a platform device for SMMU
    * @node: Pointer to SMMU ACPI IORT node
@@ -817,78 +877,42 @@ const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node)
   static int __init iort_add_smmu_platform_device(struct acpi_iort_node *node)
   {
       struct fwnode_handle *fwnode;
-    struct platform_device *pdev;
-    struct resource *r;
-    enum dev_dma_attr attr;
-    int ret, count;
-    const struct iort_iommu_config *ops = iort_get_iommu_cfg(node);
-
-    if (!ops)
-        return -ENODEV;
-
-    pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO);
-    if (!pdev)
-        return -ENOMEM;
-
-    count = ops->iommu_count_resources(node);
-
-    r = kcalloc(count, sizeof(*r), GFP_KERNEL);
-    if (!r) {
-        ret = -ENOMEM;
-        goto dev_put;
-    }
-
-    ops->iommu_init_resources(r, node);
+    struct device *dev;
+    int ret;
   -    ret = platform_device_add_resources(pdev, r, count);
       /*
-     * Resources are duplicated in platform_device_add_resources,
-     * free their allocated memory
+     * Not enabling the parsing ops for now. The corresponding driver
+     * can parse this information as needed, so deleting relevant code as
+     * compared to base revision.
        */
-    kfree(r);
   -    if (ret)
-        goto dev_put;
+    dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+    if (!dev)
+        return -ENOMEM;
         /*
        * Add a copy of IORT node pointer to platform_data to
        * be used to retrieve IORT data information.
        */
-    ret = platform_device_add_data(pdev, &node, sizeof(node));
-    if (ret)
-        goto dev_put;
-
-    /*
-     * We expect the dma masks to be equivalent for
-     * all SMMUs set-ups
-     */
-    pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+    dev->type = DEV_ACPI;
+    dev->acpi_node = node;
         fwnode = iort_get_fwnode(node);
         if (!fwnode) {
           ret = -ENODEV;
-        goto dev_put;
+        goto error;
       }
   -    pdev->dev.fwnode = fwnode;
-
-    attr = ops->iommu_is_coherent(node) ?
-                 DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT;
-
-    /* Configure DMA for the page table walker */
-    acpi_dma_configure(&pdev->dev, attr);
+    dev->fwnode = fwnode;
   -    ret = platform_device_add(pdev);
-    if (ret)
-        goto dma_deconfigure;
+    /* Call the acpi init functions for IOMMU devices */
+    ret = acpi_device_init(DEVICE_IOMMU, (void *)dev, node->type);
         return 0;
   -dma_deconfigure:
-    acpi_dma_deconfigure(&pdev->dev);
-dev_put:
-    platform_device_put(pdev);
+error:
+    kfree(dev);
         return ret;
   }
@@ -957,5 +981,6 @@ void __init acpi_iort_init(void)
         iort_init_platform_devices();
   -    acpi_probe_device_table(iort);
+    /* Xen; Do not need a device table probe */
+    /* acpi_probe_device_table(iort);*/
   }
xen/drivers/passthrough/arm/smmu.c +1 -0
index 362d578..ad956d5 100644
@@ -181,6 +181,7 @@ static void __iomem *devm_ioremap_resource(struct device *dev,
    * Xen: PCI functions
    * TODO: It should be implemented when PCI will be supported
    */
+#undef to_pci_dev
   #define to_pci_dev(dev)    (NULL)
   static inline int pci_for_each_dma_alias(struct pci_dev *pdev,
                        int (*fn) (struct pci_dev *pdev,
xen/include/acpi/acpi_iort.h +9 -5
index 77e0809..d4315a4 100644
@@ -19,27 +19,32 @@
   #ifndef __ACPI_IORT_H__
   #define __ACPI_IORT_H__
   -#include <linux/acpi.h>
-#include <linux/fwnode.h>
-#include <linux/irqdomain.h>
+#include <xen/acpi.h>
+#include <asm/device.h>
   +/* Xen: Not using IORT IRQ bindings */
+#if 0
   #define IORT_IRQ_MASK(irq)        (irq & 0xffffffffULL)
   #define IORT_IRQ_TRIGGER_MASK(irq)    ((irq >> 32) & 0xffffffffULL)
     int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node);
   void iort_deregister_domain_token(int trans_id);
   struct fwnode_handle *iort_find_domain_token(int trans_id);
-#ifdef CONFIG_ACPI_IORT
+#endif
+#ifdef CONFIG_ARM_64
   void acpi_iort_init(void);
   bool iort_node_match(u8 type);
+#if 0
   u32 iort_msi_map_rid(struct device *dev, u32 req_id);
   struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id);
   /* IOMMU interface */
   void iort_set_dma_mask(struct device *dev);
+#endif
   const struct iommu_ops *iort_iommu_configure(struct device *dev);
   #else
   static inline void acpi_iort_init(void) { }
   static inline bool iort_node_match(u8 type) { return false; }
+#if 0
   static inline u32 iort_msi_map_rid(struct device *dev, u32 req_id)
   { return req_id; }
   static inline struct irq_domain *iort_get_device_domain(struct device *dev,
@@ -47,12 +52,10 @@ static inline struct irq_domain *iort_get_device_domain(struct device *dev,
   { return NULL; }
   /* IOMMU interface */
   static inline void iort_set_dma_mask(struct device *dev) { }
+#endif
   static inline
   const struct iommu_ops *iort_iommu_configure(struct device *dev)
   { return NULL; }
   #endif
   -#define IORT_ACPI_DECLARE(name, table_id, fn)        \
-    ACPI_DECLARE_PROBE_ENTRY(iort, name, table_id, 0, NULL, 0, fn)
-
   #endif /* __ACPI_IORT_H__ */
xen/include/asm-arm/device.h +2 -0
index 5027c87..4eef9ce 100644
@@ -7,6 +7,7 @@
   enum device_type
   {
       DEV_DT,
+    DEV_ACPI,
   };
     struct dev_archdata {
@@ -20,6 +21,7 @@ struct device
   #ifdef CONFIG_HAS_DEVICE_TREE
       struct dt_device_node *of_node; /* Used by drivers imported from Linux */
   #endif
+    void *acpi_node; /*Current use case is acpi_iort_node */
       struct fwnode_handle *fwnode; /*fw device node identifier */
       struct iommu_fwspec *iommu_fwspec;
       struct dev_archdata archdata;
xen/include/xen/acpi.h +20 -0
index 9409350..2f6aae1 100644
@@ -32,6 +32,7 @@
     #include <acpi/acpi.h>
   #include <asm/acpi.h>
+#include <xen/fwnode.h>
     #define ACPI_MADT_GET_(fld, x) (((x) & ACPI_MADT_##fld##_MASK) / \
       (ACPI_MADT_##fld##_MASK & -ACPI_MADT_##fld##_MASK))
@@ -49,6 +50,26 @@
                   (!(entry)) || (unsigned long)(entry) + sizeof(*(entry)) > (end) ||  \
                   (entry)->header.length < sizeof(*(entry)))
   +static inline struct fwnode_handle *acpi_alloc_fwnode_static(void)
+{
+    struct fwnode_handle *fwnode;
+
+    fwnode = xzalloc(struct fwnode_handle);
+    if (!fwnode)
+        return NULL;
+
+    fwnode->type = FWNODE_ACPI_STATIC;
+
+    return fwnode;
+}
+
+static inline void acpi_free_fwnode_static(struct fwnode_handle *fwnode)
+{
+    if (!fwnode || fwnode->type != FWNODE_ACPI_STATIC)
+        return;
+
+    xfree(fwnode);
+}
   #ifdef CONFIG_ACPI
     enum acpi_interrupt_id {
xen/include/xen/pci.h +7 -0
index 43f2125..182b1a5 100644
@@ -92,8 +92,16 @@ struct pci_dev {
   #define PT_FAULT_THRESHOLD 10
       } fault;
       u64 vf_rlen[6];
+#ifdef CONFIG_ARM
+    struct device dev;
+#endif
   };
   +#ifdef CONFIG_ARM
+#define to_pci_dev(p) container_of(p, struct pci_dev,dev)
+#define pci_domain_nr(dev) dev->seg
+#endif
+
Quoted 8 lines
Quoted 8 lines

xen/include/xen/pci.h struct pci_dev { · line 107

Quoted 3 lines
xen/include/xen/pci.h +0 -0
   #define for_each_pdev(domain, pdev) \
       list_for_each_entry(pdev, &(domain->arch.pdev_list), domain_list)
 

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel

#16 lore raw

Re: [RFC v2 5/7] acpi:arm64: Add support for parsing IORT table

In reply to Sameer Goel

Hi Sameer,

Quoted 79 lines · Sameer Goel

Add support for parsing IORT table to initialize SMMU devices.
* The code for creating an SMMU device has been modified, so that the SMMU device can be initialized.
* The NAMED NODE code has been commented out as this will need DOM0 kernel support.
* ITS code has been included but it has not been tested.

---
  xen/arch/arm/setup.c               |   3 +
  xen/drivers/acpi/Makefile          |   1 +
  xen/drivers/acpi/arm/Makefile      |   1 +
  xen/drivers/acpi/arm/iort.c        | 173 +++++++++++++++++++++----------------
  xen/drivers/passthrough/arm/smmu.c |   1 +
  xen/include/acpi/acpi_iort.h       |  17 ++--
  xen/include/asm-arm/device.h       |   2 +
  xen/include/xen/acpi.h             |  21 +++++
  xen/include/xen/pci.h              |   8 ++
  9 files changed, 146 insertions(+), 81 deletions(-)

create mode 100644 xen/drivers/acpi/arm/Makefile

xen/arch/arm/setup.c +3 -0
index 92f173b..4ba09b2 100644
@@ -49,6 +49,7 @@
  #include <asm/setup.h>
  #include <xsm/xsm.h>
  #include <asm/acpi.h>
+#include <acpi/acpi_iort.h>
 
  struct bootinfo __initdata bootinfo;
 
@@ -796,6 +797,8 @@ void __init start_xen(unsigned long boot_phys_offset,
 
      tasklet_subsys_init();
 
+    /* Parse the ACPI iort data */
+    acpi_iort_init();
 
      xsm_dt_init();
 
xen/drivers/acpi/Makefile +1 -0
index 444b11d..e7ffd82 100644
@@ -1,5 +1,6 @@
  subdir-y += tables
  subdir-y += utilities
+subdir-$(CONFIG_ARM) += arm
  subdir-$(CONFIG_X86) += apei
 
  obj-bin-y += tables.init.o
xen/drivers/acpi/arm/Makefile new file +1 -0
new file mode 100644
index 0000000..7c039bb
@@ -0,0 +1 @@
+obj-y += iort.o
xen/drivers/acpi/arm/iort.c +8 -1
index 2e368a6..7f54062 100644
@@ -14,17 +14,47 @@
   * This file implements early detection/parsing of I/O mapping
   * reported to OS through firmware via I/O Remapping Table (IORT)
   * IORT document number: ARM DEN 0049A
+ *
+ * Based on Linux drivers/acpi/arm64/iort.c
+ * => commit ca78d3173cff3503bcd15723b049757f75762d15
+ *
+ * Xen modification:
+ * Sameer Goel <[email protected]>
+ * Copyright (C) 2017, The Linux Foundation, All rights reserved.
+ *
   */
 
-#define pr_fmt(fmt)	"ACPI: IORT: " fmt

xen/drivers/acpi/arm/iort.c line 28

Quoted 10 lines
xen/drivers/acpi/arm/iort.c +2 -8
-
-#include <linux/acpi_iort.h>
-#include <linux/iommu.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
+#include <xen/acpi.h>
+#include <acpi/acpi_iort.h>

Why do you need to include there? Can't this be done after all the <xen/> ?

Quoted 7 lines
xen/drivers/acpi/arm/iort.c +7 -0
+#include <xen/fwnode.h>
+#include <xen/iommu.h>
+#include <xen/lib.h>
+#include <xen/list.h>
+#include <xen/pci.h>
+
+#include <asm/device.h>

xen/drivers/acpi/arm/iort.c line 45

Quoted 10 lines
xen/drivers/acpi/arm/iort.c +10 -0
+
+/* Xen: Define compatibility functions */
+#define FW_BUG		"[Firmware Bug]: "
+#define pr_err(fmt, ...) printk(XENLOG_ERR fmt, ## __VA_ARGS__)
+#define pr_warn(fmt, ...) printk(XENLOG_WARNING fmt, ## __VA_ARGS__)
+
+/* Alias to Xen allocation helpers */
+#define kfree xfree
+#define kmalloc(size, flags)            _xmalloc(size, sizeof(void *))
+#define kzalloc(size, flags)            _xzalloc(size, sizeof(void *))

Likely you would need the same macros in the SMMUv3 driver. Could we think of a common headers implementing the Linux compat layer?

xen/drivers/acpi/arm/iort.c line 55

Quoted 10 lines
xen/drivers/acpi/arm/iort.c +10 -0
+
+/* Redefine WARN macros */
+#undef WARN
+#undef WARN_ON
+#define WARN(condition, format...) ({					\
+	int __ret_warn_on = !!(condition);				\
+	if (unlikely(__ret_warn_on))					\
+		printk(format);						\
+	unlikely(__ret_warn_on);					\
+})

Again, you should at least try to modify the common code version before deciding to redefine it here.

xen/drivers/acpi/arm/iort.c static acpi_status iort_match_node_callback(struct acpi_iort_node *node, · line 291

Quoted 12 lines
xen/drivers/acpi/arm/iort.c +5 -0
+#define WARN_TAINT(cond, taint, format...) WARN(cond, format)
+#define WARN_ON(cond)                      (!!cond)
 
  #define IORT_TYPE_MASK(type)	(1 << (type))
  #define IORT_MSI_TYPE		(1 << ACPI_IORT_NODE_ITS_GROUP)
@@ -256,6 +286,13 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
  	acpi_status status;
 
  	if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
+		status = AE_NOT_IMPLEMENTED;
+/*
+ * We need the namespace object name from dsdt to match the iort node, this

Please add a "Xen: TODO:" in front.

Quoted 8 lines
xen/drivers/acpi/arm/iort.c +4 -0
+ * will need additions to the kernel xen bus notifiers.
+ * So, disabling the named node code till a proposal is approved.
+ */
+#if 0
  		struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
  		struct acpi_device *adev = to_acpi_device_node(dev->fwnode);
  		struct acpi_iort_named_component *ncomp;

xen/drivers/acpi/arm/iort.c static acpi_status iort_match_node_callback(struct acpi_iort_node *node, · line 318

Quoted 8 lines
xen/drivers/acpi/arm/iort.c +2 -1
@@ -275,11 +312,12 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
  		status = !strcmp(ncomp->device_name, buf.pointer) ?
  							AE_OK : AE_NOT_FOUND;
  		acpi_os_free(buf.pointer);
+#endif
  	} else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
  		struct acpi_iort_root_complex *pci_rc;
-		struct pci_bus *bus;
+		struct pci_dev *pci_dev;

Do you really need to modify the code? Wouldn't it be possible to do

#define pci_bus pci_dev

With an explanation why you do that on top.

xen/drivers/acpi/arm/iort.c static acpi_status iort_match_node_callback(struct acpi_iort_node *node, · line 320

Quoted 3 lines
xen/drivers/acpi/arm/iort.c +1 -1
-		bus = to_pci_bus(dev);
+		pci_dev = to_pci_dev(dev);

Same here?

Quoted 18 lines
xen/drivers/acpi/arm/iort.c +1 -2
  		pci_rc = (struct acpi_iort_root_complex *)node->node_data;
 
  		/*
@@ -287,12 +325,11 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
  		 * with root complexes. Each segment number can represent only
  		 * one root complex.
  		 */
-		status = pci_rc->pci_segment_number == pci_domain_nr(bus) ?
+		status = pci_rc->pci_segment_number == pci_domain_nr(pci_dev) ?
  							AE_OK : AE_NOT_FOUND;
  	} else {
  		status = AE_NOT_FOUND;
  	}
-out:
  	return status;
  }
 

xen/drivers/acpi/arm/iort.c static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, · line 362

Quoted 6 lines
xen/drivers/acpi/arm/iort.c +3 -0
@@ -320,6 +357,11 @@ static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,
  	return 0;
  }
 
+/*
+ * Named components are not supported yet so we do not need the
+ * iort_node_get_id function

Missing full stop + TODO.

Quoted 14 lines
xen/drivers/acpi/arm/iort.c +3 -0
+ */
+#if 0
  static
  struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
  					u32 *id_out, u8 type_mask,
@@ -358,6 +400,7 @@ struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
 
  	return NULL;
  }
+#endif
 
  static struct acpi_iort_node *iort_node_map_rid(struct acpi_iort_node *node,
  						u32 rid_in, u32 *rid_out,

xen/drivers/acpi/arm/iort.c fail_map: · line 456

Quoted 4 lines
xen/drivers/acpi/arm/iort.c +1 -0
@@ -410,6 +453,10 @@ fail_map:
  	return NULL;
  }
 
+/* Xen: Comment out the NamedComponent and ITS mapping code till the support

+ TODO here please.

xen/drivers/acpi/arm/iort.c static int iort_dev_find_its_id(struct device *dev, u32 req_id, · line 531

Quoted 12 lines
xen/drivers/acpi/arm/iort.c +4 -1
+ * is available.
+ */
+#if 0
  static struct acpi_iort_node *iort_find_dev_node(struct device *dev)
  {
  	struct pci_bus *pbus;
@@ -481,7 +528,7 @@ static int iort_dev_find_its_id(struct device *dev, u32 req_id,
  	return 0;
  }
 
-/**
+/*

Why this change?

xen/drivers/acpi/arm/iort.c static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data) · line 560

Quoted 9 lines
xen/drivers/acpi/arm/iort.c +1 -1
   * iort_get_device_domain() - Find MSI domain related to a device
   * @dev: The device.
   * @req_id: Requester ID for the device.
@@ -510,7 +557,7 @@ static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
  	*rid = alias;
  	return 0;
  }
-
+#endif

Please avoid dropping newline.

Quoted 12 lines
xen/drivers/acpi/arm/iort.c +1 -0
  static int arm_smmu_iort_xlate(struct device *dev, u32 streamid,
  			       struct fwnode_handle *fwnode,
  			       const struct iommu_ops *ops)
@@ -546,6 +593,7 @@ static const struct iommu_ops *iort_iommu_xlate(struct device *dev,
  	return ret ? NULL : ops;
  }
 
+#if 0 /* Xen: We do not need this function for Xen */
  /**
   * iort_set_dma_mask - Set-up dma mask for a device.
   *

xen/drivers/acpi/arm/iort.c void iort_set_dma_mask(struct device *dev) · line 618

Quoted 5 lines
xen/drivers/acpi/arm/iort.c +1 -1
@@ -567,7 +615,7 @@ void iort_set_dma_mask(struct device *dev)
  	if (!dev->dma_mask)
  		dev->dma_mask = &dev->coherent_dma_mask;
  }
-
+#endif

Same here.

xen/drivers/acpi/arm/iort.c const struct iommu_ops *iort_iommu_configure(struct device *dev) · line 634

Quoted 9 lines
xen/drivers/acpi/arm/iort.c +1 -1
  /**
   * iort_iommu_configure - Set-up IOMMU configuration for a device.
   *
@@ -583,14 +631,13 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
  	u32 streamid = 0;
 
  	if (dev_is_pci(dev)) {
-		struct pci_bus *bus = to_pci_dev(dev)->bus;
+		struct pci_dev *pci_device = to_pci_dev(dev);

See above.

xen/drivers/acpi/arm/iort.c const struct iommu_ops *iort_iommu_configure(struct device *dev) · line 637

Quoted 5 lines
xen/drivers/acpi/arm/iort.c +1 -2
  		u32 rid;
 
-		pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
-				       &rid);
+		rid = PCI_BDF2(pci_device->bus, pci_device->devfn);

I believe we had a discussion on v1 explaining why this is wrong. So I don't understand why

Quoted 8 lines
xen/drivers/acpi/arm/iort.c +1 -1
  		node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
-				      iort_match_node_callback, &bus->dev);
+				      iort_match_node_callback, dev);
  		if (!node)
  			return NULL;
 

xen/drivers/acpi/arm/iort.c const struct iommu_ops *iort_iommu_configure(struct device *dev) · line 652

Quoted 6 lines
xen/drivers/acpi/arm/iort.c +3 -0
@@ -600,6 +647,13 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
  		ops = iort_iommu_xlate(dev, parent, streamid);
 
  	} else {
+		return NULL;
+/*
+ * We need the namespace object name from dsdt to match the iort node, this

Xen:

Quoted 26 lines
xen/drivers/acpi/arm/iort.c +10 -0
+ * will need additions to the kernel xen bus notifiers.
+ * So, disabling the named node code till a proposal is approved.
+ */
+#if 0
  		int i = 0;
 
  		node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
@@ -616,11 +670,17 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
  			parent = iort_node_get_id(node, &streamid,
  						  IORT_IOMMU_TYPE, i++);
  		}
+#endif
  	}
 
  	return ops;
  }
 
+/*
+ * Xen: Not using the parsing ops for now. Need to check and see if it will
+ * be useful to use these in some form, or let the driver parse IORT node.
+ */
+#if 0
  static void __init acpi_iort_register_irq(int hwirq, const char *name,
  					  int trigger,
  					  struct resource *res)

xen/drivers/acpi/arm/iort.c const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node) · line 870

Quoted 5 lines
xen/drivers/acpi/arm/iort.c +1 -1
@@ -807,7 +867,7 @@ const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node)
  		return NULL;
  	}
  }
-
+#endif

Please avoid dropping newline.

xen/drivers/acpi/arm/iort.c const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node) · line 877

Quoted 5 lines
xen/drivers/acpi/arm/iort.c +0 -0
  /**
   * iort_add_smmu_platform_device() - Allocate a platform device for SMMU
   * @node: Pointer to SMMU ACPI IORT node
@@ -817,78 +877,42 @@ const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node)
  static int __init iort_add_smmu_platform_device(struct acpi_iort_node *node)

Looking at the changes in this function. You basically rewrite everything. I would prefer if you comment the current one and implement from scratch the Xen version.

Quoted 94 lines
xen/drivers/acpi/arm/iort.c +16 -52
  {
  	struct fwnode_handle *fwnode;
-	struct platform_device *pdev;
-	struct resource *r;
-	enum dev_dma_attr attr;
-	int ret, count;
-	const struct iort_iommu_config *ops = iort_get_iommu_cfg(node);
-
-	if (!ops)
-		return -ENODEV;
-
-	pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO);
-	if (!pdev)
-		return -ENOMEM;
-
-	count = ops->iommu_count_resources(node);
-
-	r = kcalloc(count, sizeof(*r), GFP_KERNEL);
-	if (!r) {
-		ret = -ENOMEM;
-		goto dev_put;
-	}
-
-	ops->iommu_init_resources(r, node);
+	struct device *dev;
+	int ret;
 
-	ret = platform_device_add_resources(pdev, r, count);
  	/*
-	 * Resources are duplicated in platform_device_add_resources,
-	 * free their allocated memory
+	 * Not enabling the parsing ops for now. The corresponding driver
+	 * can parse this information as needed, so deleting relevant code as
+	 * compared to base revision.
  	 */
-	kfree(r);
 
-	if (ret)
-		goto dev_put;
+	dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
 
  	/*
  	 * Add a copy of IORT node pointer to platform_data to
  	 * be used to retrieve IORT data information.
  	 */
-	ret = platform_device_add_data(pdev, &node, sizeof(node));
-	if (ret)
-		goto dev_put;
-
-	/*
-	 * We expect the dma masks to be equivalent for
-	 * all SMMUs set-ups
-	 */
-	pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+	dev->type = DEV_ACPI;
+	dev->acpi_node = node;
 
  	fwnode = iort_get_fwnode(node);
 
  	if (!fwnode) {
  		ret = -ENODEV;
-		goto dev_put;
+		goto error;
  	}
 
-	pdev->dev.fwnode = fwnode;
-
-	attr = ops->iommu_is_coherent(node) ?
-			     DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT;
-
-	/* Configure DMA for the page table walker */
-	acpi_dma_configure(&pdev->dev, attr);
+	dev->fwnode = fwnode;
 
-	ret = platform_device_add(pdev);
-	if (ret)
-		goto dma_deconfigure;
+	/* Call the acpi init functions for IOMMU devices */
+	ret = acpi_device_init(DEVICE_IOMMU, (void *)dev, node->type);
 
  	return 0;
 
-dma_deconfigure:
-	acpi_dma_deconfigure(&pdev->dev);
-dev_put:
-	platform_device_put(pdev);
+error:
+	kfree(dev);
 
  	return ret;
  }

xen/drivers/acpi/arm/iort.c void __init acpi_iort_init(void) · line 985

Quoted 6 lines
xen/drivers/acpi/arm/iort.c +2 -1
@@ -957,5 +981,6 @@ void __init acpi_iort_init(void)
 
  	iort_init_platform_devices();
 
-	acpi_probe_device_table(iort);
+	/* Xen; Do not need a device table probe */
+	/* acpi_probe_device_table(iort);*/

Please use either

#if 0

#endif

or introduce a dummy acpi_probe_device_table(...) at the start.

xen/drivers/passthrough/arm/smmu.c static void __iomem *devm_ioremap_resource(struct device *dev, · line 184

Quoted 10 lines
xen/drivers/acpi/arm/iort.c +0 -0
  }
xen/drivers/passthrough/arm/smmu.c +1 -0
index 362d578..ad956d5 100644
@@ -181,6 +181,7 @@ static void __iomem *devm_ioremap_resource(struct device *dev,
   * Xen: PCI functions
   * TODO: It should be implemented when PCI will be supported
   */
+#undef to_pci_dev

Why this change?

xen/include/acpi/acpi_iort.h

Quoted 7 lines
xen/drivers/passthrough/arm/smmu.c +0 -0
  #define to_pci_dev(dev)	(NULL)
  static inline int pci_for_each_dma_alias(struct pci_dev *pdev,
  					 int (*fn) (struct pci_dev *pdev,
xen/include/acpi/acpi_iort.h +0 -0
index 77e0809..d4315a4 100644

You probably want to re-sync this headers as it changed quite a bit and would avoid some specific #if 0 for Xen.

Quoted 11 lines
xen/include/acpi/acpi_iort.h +3 -3
@@ -19,27 +19,32 @@
  #ifndef __ACPI_IORT_H__
  #define __ACPI_IORT_H__
 
-#include <linux/acpi.h>
-#include <linux/fwnode.h>
-#include <linux/irqdomain.h>
+#include <xen/acpi.h>
+#include <asm/device.h>
 
+/* Xen: Not using IORT IRQ bindings */

xen/include/acpi/acpi_iort.h line 34

Quoted 10 lines
xen/include/acpi/acpi_iort.h +3 -1
+#if 0
  #define IORT_IRQ_MASK(irq)		(irq & 0xffffffffULL)
  #define IORT_IRQ_TRIGGER_MASK(irq)	((irq >> 32) & 0xffffffffULL)
 
  int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node);
  void iort_deregister_domain_token(int trans_id);
  struct fwnode_handle *iort_find_domain_token(int trans_id);
-#ifdef CONFIG_ACPI_IORT
+#endif
+#ifdef CONFIG_ARM_64

As said in the first version, I see no point of replacing CONFIG_ACPI_IORT with CONFIG_ARM_64. You should instead take advantage of the Kconfig to add a new config ACPI_IORT and select on Arm64 with ACPI.

Quoted 43 lines
xen/include/acpi/acpi_iort.h +4 -3
  void acpi_iort_init(void);
  bool iort_node_match(u8 type);
+#if 0
  u32 iort_msi_map_rid(struct device *dev, u32 req_id);
  struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id);
  /* IOMMU interface */
  void iort_set_dma_mask(struct device *dev);
+#endif
  const struct iommu_ops *iort_iommu_configure(struct device *dev);
  #else
  static inline void acpi_iort_init(void) { }
  static inline bool iort_node_match(u8 type) { return false; }
+#if 0
  static inline u32 iort_msi_map_rid(struct device *dev, u32 req_id)
  { return req_id; }
  static inline struct irq_domain *iort_get_device_domain(struct device *dev,
@@ -47,12 +52,10 @@ static inline struct irq_domain *iort_get_device_domain(struct device *dev,
  { return NULL; }
  /* IOMMU interface */
  static inline void iort_set_dma_mask(struct device *dev) { }
+#endif
  static inline
  const struct iommu_ops *iort_iommu_configure(struct device *dev)
  { return NULL; }
  #endif
 
-#define IORT_ACPI_DECLARE(name, table_id, fn)		\
-	ACPI_DECLARE_PROBE_ENTRY(iort, name, table_id, 0, NULL, 0, fn)
-
  #endif /* __ACPI_IORT_H__ */
xen/include/asm-arm/device.h +1 -0
index 5027c87..4eef9ce 100644
@@ -7,6 +7,7 @@
  enum device_type
  {
      DEV_DT,
+    DEV_ACPI,
  };
 
  struct dev_archdata {

xen/include/asm-arm/device.h struct device · line 24

Quoted 4 lines
xen/include/asm-arm/device.h +1 -0
@@ -20,6 +21,7 @@ struct device
  #ifdef CONFIG_HAS_DEVICE_TREE
      struct dt_device_node *of_node; /* Used by drivers imported from Linux */
  #endif
+    void *acpi_node; /*Current use case is acpi_iort_node */

Can you explain why you need that? After the creation of fwnode, I was expecting of_node to disappear. So I don't really fancy see acpi_node here more it does not exist in Linux.

xen/include/xen/acpi.h line 35

Quoted 12 lines
xen/include/asm-arm/device.h +0 -0
      struct fwnode_handle *fwnode; /*fw device node identifier */
      struct iommu_fwspec *iommu_fwspec;
      struct dev_archdata archdata;
xen/include/xen/acpi.h +1 -0
index 9409350..2f6aae1 100644
@@ -32,6 +32,7 @@
 
  #include <acpi/acpi.h>
  #include <asm/acpi.h>
+#include <xen/fwnode.h>

I think this and ...

Quoted 17 lines
xen/include/xen/acpi.h +10 -0
  #define ACPI_MADT_GET_(fld, x) (((x) & ACPI_MADT_##fld##_MASK) / \
  	(ACPI_MADT_##fld##_MASK & -ACPI_MADT_##fld##_MASK))
@@ -49,6 +50,26 @@
                  (!(entry)) || (unsigned long)(entry) + sizeof(*(entry)) > (end) ||  \
                  (entry)->header.length < sizeof(*(entry)))
 
+static inline struct fwnode_handle *acpi_alloc_fwnode_static(void)
+{
+	struct fwnode_handle *fwnode;
+
+	fwnode = xzalloc(struct fwnode_handle);
+	if (!fwnode)
+		return NULL;
+
+	fwnode->type = FWNODE_ACPI_STATIC;
+

xen/include/xen/acpi.h line 72

Quoted 10 lines
xen/include/xen/acpi.h +10 -0
+	return fwnode;
+}
+
+static inline void acpi_free_fwnode_static(struct fwnode_handle *fwnode)
+{
+	if (!fwnode || fwnode->type != FWNODE_ACPI_STATIC)
+		return;
+
+	xfree(fwnode);
+}

... those 2 helpers should go in asm/acpi.h.

Quoted 8 lines
xen/include/xen/acpi.h +0 -0
  #ifdef CONFIG_ACPI
 
  enum acpi_interrupt_id {
xen/include/xen/pci.h +0 -0
index 43f2125..182b1a5 100644

xen/include/xen/pci.h struct pci_dev { · line 97

Quoted 6 lines
xen/include/xen/pci.h +3 -0
@@ -92,8 +92,16 @@ struct pci_dev {
  #define PT_FAULT_THRESHOLD 10
      } fault;
      u64 vf_rlen[6];
+#ifdef CONFIG_ARM
+    struct device dev;
+#endif

There are a part of PCI that is already per-arch. See arch_pci_dev in asm-arm/pci.h.

Please define the field dev in it rather than here.

xen/include/xen/pci.h struct pci_dev { · line 103

Quoted 6 lines
xen/include/xen/pci.h +4 -0
  };
 
+#ifdef CONFIG_ARM
+#define to_pci_dev(p) container_of(p, struct pci_dev,dev)
+#define pci_domain_nr(dev) dev->seg
+#endif

Similarly, this should be moved in asm-arm/pci.h.

xen/include/xen/pci.h struct pci_dev { · line 107

Quoted 5 lines
xen/include/xen/pci.h +1 -0
+
  #define for_each_pdev(domain, pdev) \
      list_for_each_entry(pdev, &(domain->arch.pdev_list), domain_list)
 

Cheers,

Signature · 6 lines
Julien Grall

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#17 lore raw

Re: [RFC v2 5/7] acpi:arm64: Add support for parsing IORT table

Quoted 78 lines · Julien Grall

Hi Sameer,

Quoted 75 lines · Sameer Goel

Add support for parsing IORT table to initialize SMMU devices.
* The code for creating an SMMU device has been modified, so that the SMMU device can be initialized.
* The NAMED NODE code has been commented out as this will need DOM0 kernel support.
* ITS code has been included but it has not been tested.

---
  xen/arch/arm/setup.c               |   3 + xen/drivers/acpi/Makefile          |   1 + xen/drivers/acpi/arm/Makefile      |   1 + xen/drivers/acpi/arm/iort.c        | 173 +++++++++++++++++++++---------------- xen/drivers/passthrough/arm/smmu.c |   1 + xen/include/acpi/acpi_iort.h       |  17 ++-- xen/include/asm-arm/device.h       |   2 +
  xen/include/xen/acpi.h             |  21 +++++ xen/include/xen/pci.h              |   8 ++

  9 files changed, 146 insertions(+), 81 deletions(-)

  create mode 100644 xen/drivers/acpi/arm/Makefile

xen/arch/arm/setup.c +1 -0
index 92f173b..4ba09b2 100644
@@ -49,6 +49,7 @@
   #include <asm/setup.h>
   #include <xsm/xsm.h>
   #include <asm/acpi.h>
+#include <acpi/acpi_iort.h>
     struct bootinfo __initdata bootinfo;
   @@ -796,6 +797,8 @@ void __init start_xen(unsigned long boot_phys_offset,
         tasklet_subsys_init();

  +    /* Parse the ACPI iort data */
+    acpi_iort_init();
        xsm_dt_init();
  diff --git a/xen/drivers/acpi/Makefile b/xen/drivers/acpi/Makefile index 444b11d..e7ffd82 100644

xen/drivers/acpi/Makefile +0 -0
@@ -1,5 +1,6 @@

  subdir-y += tables
  subdir-y += utilities
+subdir-$(CONFIG_ARM) += arm
  subdir-$(CONFIG_X86) += apei
    obj-bin-y += tables.init.o

xen/drivers/acpi/arm/Makefile new file +1 -0
new file mode 100644
index 0000000..7c039bb
@@ -0,0 +1 @@
+obj-y += iort.o
xen/drivers/acpi/arm/iort.c +8 -0
index 2e368a6..7f54062 100644
@@ -14,17 +14,47 @@
    * This file implements early detection/parsing of I/O mapping
    * reported to OS through firmware via I/O Remapping Table (IORT)
    * IORT document number: ARM DEN 0049A
+ *
+ * Based on Linux drivers/acpi/arm64/iort.c
+ * => commit ca78d3173cff3503bcd15723b049757f75762d15
+ *
+ * Xen modification:
+ * Sameer Goel <[email protected]>
+ * Copyright (C) 2017, The Linux Foundation, All rights reserved.
+ *
    */

  -#define pr_fmt(fmt)    "ACPI: IORT: " fmt

xen/drivers/acpi/arm/iort.c +0 -4
-
-#include <linux/acpi_iort.h>
-#include <linux/iommu.h>
-#include <linux/kernel.h>
Quoted 8 lines

xen/drivers/acpi/arm/iort.c line 28

Quoted 6 lines
xen/drivers/acpi/arm/iort.c +2 -4
-#include <linux/list.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
+#include <xen/acpi.h>
+#include <acpi/acpi_iort.h>

Why do you need to include there? Can't this be done after all the <xen/> ?

I was just clubbing the acpi includes. I can move this after all xen/

Quoted 12 lines
Quoted 12 lines
xen/drivers/acpi/arm/iort.c +11 -0
+#include <xen/fwnode.h>
+#include <xen/iommu.h>
+#include <xen/lib.h>
+#include <xen/list.h>
+#include <xen/pci.h>
+
+#include <asm/device.h>
+
+/* Xen: Define compatibility functions */
+#define FW_BUG        "[Firmware Bug]: "
+#define pr_err(fmt, ...) printk(XENLOG_ERR fmt, ## __VA_ARGS__)
Quoted 8 lines

xen/drivers/acpi/arm/iort.c line 45

Quoted 6 lines
xen/drivers/acpi/arm/iort.c +6 -0
+#define pr_warn(fmt, ...) printk(XENLOG_WARNING fmt, ## __VA_ARGS__)
+
+/* Alias to Xen allocation helpers */
+#define kfree xfree
+#define kmalloc(size, flags)            _xmalloc(size, sizeof(void *))
+#define kzalloc(size, flags)            _xzalloc(size, sizeof(void *))

Likely you would need the same macros in the SMMUv3 driver. Could we think of a common headers implementing the Linux compat layer?

I agree that this will be a very useful. i'll try to propose something.

Quoted 13 lines

xen/drivers/acpi/arm/iort.c line 55

Quoted 11 lines
xen/drivers/acpi/arm/iort.c +10 -0
+
+/* Redefine WARN macros */
+#undef WARN
+#undef WARN_ON
+#define WARN(condition, format...) ({                    \
+    int __ret_warn_on = !!(condition);                \
+    if (unlikely(__ret_warn_on))                    \
+        printk(format);                        \
+    unlikely(__ret_warn_on);                    \
+})

Again, you should at least try to modify the common code version before deciding to redefine it here.

The xen macro seems such that it explicitly tries to block a return by wrapping this macro in a loop. I had changed the common function in the last iteration and there seemed to be a pushback.

Quoted 13 lines

xen/drivers/acpi/arm/iort.c static acpi_status iort_match_node_callback(struct acpi_iort_node *node, · line 290

Quoted 11 lines
xen/drivers/acpi/arm/iort.c +2 -0
+#define WARN_TAINT(cond, taint, format...) WARN(cond, format)
+#define WARN_ON(cond)                      (!!cond)

    #define IORT_TYPE_MASK(type)    (1 << (type)) #define IORT_MSI_TYPE        (1 << ACPI_IORT_NODE_ITS_GROUP)

xen/drivers/acpi/arm/iort.c +3 -0
@@ -256,6 +286,13 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
       acpi_status status;
         if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
+        status = AE_NOT_IMPLEMENTED;
+/*
+ * We need the namespace object name from dsdt to match the iort node, this

Please add a "Xen: TODO:" in front.

Ok.

Quoted 20 lines
Quoted 9 lines
xen/drivers/acpi/arm/iort.c +4 -0
+ * will need additions to the kernel xen bus notifiers.
+ * So, disabling the named node code till a proposal is approved.
+ */
+#if 0
           struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
           struct acpi_device *adev = to_acpi_device_node(dev->fwnode);
           struct acpi_iort_named_component *ncomp;
@@ -275,11 +312,12 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
Quoted 8 lines

          status = !strcmp(ncomp->device_name, buf.pointer) ? AE_OK : AE_NOT_FOUND; acpi_os_free(buf.pointer);
+#endif
      } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { struct acpi_iort_root_complex *pci_rc;
-        struct pci_bus *bus;
+        struct pci_dev *pci_dev;

Do you really need to modify the code? Wouldn't it be possible to do

Quoted 1 line

#define pci_bus pci_dev

The pci_dev is the container for the generic device. I wanted to call this out explicitly here. We can do the above if you insist :).

Quoted 23 lines

With an explanation why you do that on top.

Quoted 2 lines

  -        bus = to_pci_bus(dev);
+        pci_dev = to_pci_dev(dev);

Same here?

Quoted 15 lines

          pci_rc = (struct acpi_iort_root_complex *)node->node_data; /*

xen/drivers/acpi/arm/iort.c +1 -2
@@ -287,12 +325,11 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
            * with root complexes. Each segment number can represent only
            * one root complex.
            */
-        status = pci_rc->pci_segment_number == pci_domain_nr(bus) ?
+        status = pci_rc->pci_segment_number == pci_domain_nr(pci_dev) ?
                               AE_OK : AE_NOT_FOUND;
       } else {
           status = AE_NOT_FOUND;
       }
-out:
       return status;
   }
Quoted 8 lines
Quoted 6 lines
xen/drivers/acpi/arm/iort.c +0 -0
   @@ -320,6 +357,11 @@ static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,

      return 0;
  }
  +/*
+ * Named components are not supported yet so we do not need the
+ * iort_node_get_id function

Missing full stop + TODO.

Ok.

Quoted 10 lines
Quoted 10 lines

+ */
+#if 0
  static
  struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node, u32 *id_out, u8 type_mask,

xen/drivers/acpi/arm/iort.c +1 -0
@@ -358,6 +400,7 @@ struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
         return NULL;
   }
+#endif
Quoted 9 lines
Quoted 6 lines
xen/drivers/acpi/arm/iort.c +0 -0
     static struct acpi_iort_node *iort_node_map_rid(struct acpi_iort_node *node,
                           u32 rid_in, u32 *rid_out,
@@ -410,6 +453,10 @@ fail_map:
       return NULL;
   }

  +/* Xen: Comment out the NamedComponent and ITS mapping code till the support

+ TODO here please.

Ok.

Quoted 5 lines
Quoted 5 lines

+ * is available.
+ */
+#if 0
  static struct acpi_iort_node *iort_find_dev_node(struct device *dev) {

Quoted 8 lines
Quoted 6 lines

      struct pci_bus *pbus;

xen/drivers/acpi/arm/iort.c +0 -0
@@ -481,7 +528,7 @@ static int iort_dev_find_its_id(struct device *dev, u32 req_id,
       return 0;
   }

  -/**
+/*

Why this change?

Oversight.

Quoted 43 lines

xen/drivers/acpi/arm/iort.c static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data) · line 560

Quoted 10 lines

   * iort_get_device_domain() - Find MSI domain related to a device
   * @dev: The device.
   * @req_id: Requester ID for the device.

xen/drivers/acpi/arm/iort.c +1 -1
@@ -510,7 +557,7 @@ static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
       *rid = alias;
       return 0;
   }
-
+#endif

Please avoid dropping newline.

Quoted 11 lines
xen/drivers/acpi/arm/iort.c +0 -0
   static int arm_smmu_iort_xlate(struct device *dev, u32 streamid,
                      struct fwnode_handle *fwnode,
                      const struct iommu_ops *ops)
@@ -546,6 +593,7 @@ static const struct iommu_ops *iort_iommu_xlate(struct device *dev,
       return ret ? NULL : ops;
   }

  +#if 0 /* Xen: We do not need this function for Xen */ /**
   * iort_set_dma_mask - Set-up dma mask for a device. *

xen/drivers/acpi/arm/iort.c void iort_set_dma_mask(struct device *dev) · line 618

Quoted 5 lines
xen/drivers/acpi/arm/iort.c +1 -1
@@ -567,7 +615,7 @@ void iort_set_dma_mask(struct device *dev)
       if (!dev->dma_mask)
           dev->dma_mask = &dev->coherent_dma_mask;
   }
-
+#endif

Same here.

xen/drivers/acpi/arm/iort.c const struct iommu_ops *iort_iommu_configure(struct device *dev) · line 633

Quoted 8 lines
xen/drivers/acpi/arm/iort.c +1 -1
   /**
    * iort_iommu_configure - Set-up IOMMU configuration for a device.
    *
@@ -583,14 +631,13 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
       u32 streamid = 0;
         if (dev_is_pci(dev)) {
-        struct pci_bus *bus = to_pci_dev(dev)->bus;
+        struct pci_dev *pci_device = to_pci_dev(dev);

See above.

Quoted 6 lines

xen/drivers/acpi/arm/iort.c const struct iommu_ops *iort_iommu_configure(struct device *dev) · line 637

Quoted 4 lines
xen/drivers/acpi/arm/iort.c +1 -1
           u32 rid;
   -        pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
-                       &rid);
+        rid = PCI_BDF2(pci_device->bus, pci_device->devfn);

I believe we had a discussion on v1 explaining why this is wrong. So I don't understand why

The intent here was to have some working support code to get the SMMUv3 driver ready for a first review. I still need to research on how to fix this correctly based on the v1 comments. I'll fix it in the next RFC.

Quoted 46 lines

xen/drivers/acpi/arm/iort.c const struct iommu_ops *iort_iommu_configure(struct device *dev)

Quoted 12 lines
xen/drivers/acpi/arm/iort.c +4 -1
             node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
-                      iort_match_node_callback, &bus->dev);
+                      iort_match_node_callback, dev);
           if (!node)
               return NULL;
   @@ -600,6 +647,13 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
           ops = iort_iommu_xlate(dev, parent, streamid);
         } else {
+        return NULL;
+/*
+ * We need the namespace object name from dsdt to match the iort node, this

Xen:

Quoted 23 lines

+ * will need additions to the kernel xen bus notifiers.
+ * So, disabling the named node code till a proposal is approved.
+ */
+#if 0
          int i = 0;
            node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,

xen/drivers/acpi/arm/iort.c +1 -0
@@ -616,11 +670,17 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
               parent = iort_node_get_id(node, &streamid,
                             IORT_IOMMU_TYPE, i++);
           }
+#endif
       }
         return ops;
   }

  +/*
+ * Xen: Not using the parsing ops for now. Need to check and see if it will
+ * be useful to use these in some form, or let the driver parse IORT node.
+ */
+#if 0
  static void __init acpi_iort_register_irq(int hwirq, const char *name, int trigger,
                        struct resource *res)

xen/drivers/acpi/arm/iort.c const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node) · line 870

Quoted 5 lines
xen/drivers/acpi/arm/iort.c +1 -1
@@ -807,7 +867,7 @@ const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node)
           return NULL;
       }
   }
-
+#endif

Please avoid dropping newline.

Quoted 7 lines

xen/drivers/acpi/arm/iort.c const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node) · line 877

Quoted 5 lines
xen/drivers/acpi/arm/iort.c +0 -0
   /**
    * iort_add_smmu_platform_device() - Allocate a platform device for SMMU
    * @node: Pointer to SMMU ACPI IORT node
@@ -817,78 +877,42 @@ const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node)
   static int __init iort_add_smmu_platform_device(struct acpi_iort_node *node)

Looking at the changes in this function. You basically rewrite everything. I would prefer if you comment the current one and implement from scratch the Xen version.

Agree.

Quoted 92 lines
Quoted 85 lines
xen/drivers/acpi/arm/iort.c +14 -44
   {
       struct fwnode_handle *fwnode;
-    struct platform_device *pdev;
-    struct resource *r;
-    enum dev_dma_attr attr;
-    int ret, count;
-    const struct iort_iommu_config *ops = iort_get_iommu_cfg(node);
-
-    if (!ops)
-        return -ENODEV;
-
-    pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO);
-    if (!pdev)
-        return -ENOMEM;
-
-    count = ops->iommu_count_resources(node);
-
-    r = kcalloc(count, sizeof(*r), GFP_KERNEL);
-    if (!r) {
-        ret = -ENOMEM;
-        goto dev_put;
-    }
-
-    ops->iommu_init_resources(r, node);
+    struct device *dev;
+    int ret;
   -    ret = platform_device_add_resources(pdev, r, count);
       /*
-     * Resources are duplicated in platform_device_add_resources,
-     * free their allocated memory
+     * Not enabling the parsing ops for now. The corresponding driver
+     * can parse this information as needed, so deleting relevant code as
+     * compared to base revision.
        */
-    kfree(r);
   -    if (ret)
-        goto dev_put;
+    dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+    if (!dev)
+        return -ENOMEM;
         /*
        * Add a copy of IORT node pointer to platform_data to
        * be used to retrieve IORT data information.
        */
-    ret = platform_device_add_data(pdev, &node, sizeof(node));
-    if (ret)
-        goto dev_put;
-
-    /*
-     * We expect the dma masks to be equivalent for
-     * all SMMUs set-ups
-     */
-    pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+    dev->type = DEV_ACPI;
+    dev->acpi_node = node;
         fwnode = iort_get_fwnode(node);
         if (!fwnode) {
           ret = -ENODEV;
-        goto dev_put;
+        goto error;
       }
   -    pdev->dev.fwnode = fwnode;
-
-    attr = ops->iommu_is_coherent(node) ?
-                 DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT;
-
-    /* Configure DMA for the page table walker */
-    acpi_dma_configure(&pdev->dev, attr);
+    dev->fwnode = fwnode;
   -    ret = platform_device_add(pdev);
-    if (ret)
-        goto dma_deconfigure;
+    /* Call the acpi init functions for IOMMU devices */
+    ret = acpi_device_init(DEVICE_IOMMU, (void *)dev, node->type);
         return 0;

  -dma_deconfigure:
-    acpi_dma_deconfigure(&pdev->dev);

xen/drivers/acpi/arm/iort.c +2 -2
-dev_put:
-    platform_device_put(pdev);
+error:
+    kfree(dev);

        return ret;
  }

xen/drivers/acpi/arm/iort.c void __init acpi_iort_init(void) · line 984

Quoted 4 lines
xen/drivers/acpi/arm/iort.c +2 -0
@@ -957,5 +981,6 @@ void __init acpi_iort_init(void)
         iort_init_platform_devices();
   -    acpi_probe_device_table(iort);
+    /* Xen; Do not need a device table probe */
+    /* acpi_probe_device_table(iort);*/

Please use either

Quoted 5 lines

#if 0

#endif

or introduce a dummy acpi_probe_device_table(...) at the start.

Will introduce a dummy acpi_probe.

Quoted 13 lines

xen/drivers/passthrough/arm/smmu.c static void __iomem *devm_ioremap_resource(struct device *dev, · line 184

Quoted 11 lines
xen/drivers/acpi/arm/iort.c +0 -0
   }
xen/drivers/passthrough/arm/smmu.c +1 -0
index 362d578..ad956d5 100644
@@ -181,6 +181,7 @@ static void __iomem *devm_ioremap_resource(struct device *dev,
    * Xen: PCI functions
    * TODO: It should be implemented when PCI will be supported
    */
+#undef to_pci_dev

Why this change?

I had redefine the to_pci_dev to get the actual pci_dev struct. smmu driver does not use pci yet.

Quoted 23 lines

xen/include/acpi/acpi_iort.h

Quoted 8 lines
xen/drivers/passthrough/arm/smmu.c +0 -0
   #define to_pci_dev(dev)    (NULL)
   static inline int pci_for_each_dma_alias(struct pci_dev *pdev,
                        int (*fn) (struct pci_dev *pdev,
xen/include/acpi/acpi_iort.h +0 -0
index 77e0809..d4315a4 100644

You probably want to re-sync this headers as it changed quite a bit and would avoid some specific #if 0 for Xen.

Quoted 12 lines
xen/include/acpi/acpi_iort.h +0 -0
@@ -19,27 +19,32 @@
   #ifndef __ACPI_IORT_H__
   #define __ACPI_IORT_H__

  -#include <linux/acpi.h>

xen/include/acpi/acpi_iort.h +2 -2
-#include <linux/fwnode.h>
-#include <linux/irqdomain.h>
+#include <xen/acpi.h>
+#include <asm/device.h>

  +/* Xen: Not using IORT IRQ bindings */
+#if 0
  #define IORT_IRQ_MASK(irq)        (irq & 0xffffffffULL) #define IORT_IRQ_TRIGGER_MASK(irq)    ((irq >> 32) & 0xffffffffULL)

Quoted 8 lines

xen/include/acpi/acpi_iort.h line 34

Quoted 6 lines

    int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node); void iort_deregister_domain_token(int trans_id);
  struct fwnode_handle *iort_find_domain_token(int trans_id);

xen/include/acpi/acpi_iort.h +2 -1
-#ifdef CONFIG_ACPI_IORT
+#endif
+#ifdef CONFIG_ARM_64

As said in the first version, I see no point of replacing CONFIG_ACPI_IORT with CONFIG_ARM_64. You should instead take advantage of the Kconfig to add a new config ACPI_IORT and select on Arm64 with ACPI.

I agree. I saw your comments a bit late for this RFC. I will add this to the next set.

Quoted 40 lines
Quoted 40 lines

  void acpi_iort_init(void);
  bool iort_node_match(u8 type);
+#if 0
  u32 iort_msi_map_rid(struct device *dev, u32 req_id);
  struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id); /* IOMMU interface */
  void iort_set_dma_mask(struct device *dev);
+#endif
  const struct iommu_ops *iort_iommu_configure(struct device *dev); #else
  static inline void acpi_iort_init(void) { }
  static inline bool iort_node_match(u8 type) { return false; } +#if 0
  static inline u32 iort_msi_map_rid(struct device *dev, u32 req_id) { return req_id; }
  static inline struct irq_domain *iort_get_device_domain(struct device *dev,

xen/include/acpi/acpi_iort.h +1 -0
@@ -47,12 +52,10 @@ static inline struct irq_domain *iort_get_device_domain(struct device *dev,
   { return NULL; }
   /* IOMMU interface */
   static inline void iort_set_dma_mask(struct device *dev) { }
+#endif

  static inline
  const struct iommu_ops *iort_iommu_configure(struct device *dev) { return NULL; }
  #endif
  -#define IORT_ACPI_DECLARE(name, table_id, fn)        \
-    ACPI_DECLARE_PROBE_ENTRY(iort, name, table_id, 0, NULL, 0, fn) -
  #endif /* __ACPI_IORT_H__ */

xen/include/asm-arm/device.h +0 -0
index 5027c87..4eef9ce 100644
@@ -7,6 +7,7 @@

  enum device_type
  {
      DEV_DT,
+    DEV_ACPI,
  };

Quoted 8 lines
Quoted 6 lines

    struct dev_archdata {

xen/include/asm-arm/device.h +0 -0
@@ -20,6 +21,7 @@ struct device
   #ifdef CONFIG_HAS_DEVICE_TREE

      struct dt_device_node *of_node; /* Used by drivers imported from Linux */ #endif
+    void *acpi_node; /*Current use case is acpi_iort_node */

Can you explain why you need that? After the creation of fwnode, I was expecting of_node to disappear. So I don't really fancy see acpi_node here more it does not exist in Linux.

of_node stays for backwards compatibility with smmu v2 driver. I will try to find a way to remove acpi_node.

Quoted 34 lines

xen/include/xen/acpi.h line 34

Quoted 12 lines

      struct fwnode_handle *fwnode; /*fw device node identifier */ struct iommu_fwspec *iommu_fwspec;
      struct dev_archdata archdata;

xen/include/xen/acpi.h +1 -0
index 9409350..2f6aae1 100644
@@ -32,6 +32,7 @@
     #include <acpi/acpi.h>
   #include <asm/acpi.h>
+#include <xen/fwnode.h>

I think this and ...

Quoted 19 lines
xen/include/xen/acpi.h +13 -0
     #define ACPI_MADT_GET_(fld, x) (((x) & ACPI_MADT_##fld##_MASK) / \
       (ACPI_MADT_##fld##_MASK & -ACPI_MADT_##fld##_MASK))
@@ -49,6 +50,26 @@
                   (!(entry)) || (unsigned long)(entry) + sizeof(*(entry)) > (end) ||  \
                   (entry)->header.length < sizeof(*(entry)))
   +static inline struct fwnode_handle *acpi_alloc_fwnode_static(void)
+{
+    struct fwnode_handle *fwnode;
+
+    fwnode = xzalloc(struct fwnode_handle);
+    if (!fwnode)
+        return NULL;
+
+    fwnode->type = FWNODE_ACPI_STATIC;
+
+    return fwnode;
+}
+
+static inline void acpi_free_fwnode_static(struct fwnode_handle *fwnode)
Quoted 8 lines

xen/include/xen/acpi.h line 72

Quoted 6 lines
xen/include/xen/acpi.h +6 -0
+{
+    if (!fwnode || fwnode->type != FWNODE_ACPI_STATIC)
+        return;
+
+    xfree(fwnode);
+}

... those 2 helpers should go in asm/acpi.h.

Ok.

Quoted 17 lines
Quoted 8 lines
xen/include/xen/acpi.h +0 -0
   #ifdef CONFIG_ACPI
     enum acpi_interrupt_id {
xen/include/xen/pci.h +0 -0
index 43f2125..182b1a5 100644

xen/include/xen/pci.h struct pci_dev { · line 97

Quoted 6 lines
xen/include/xen/pci.h +3 -0
@@ -92,8 +92,16 @@ struct pci_dev {
   #define PT_FAULT_THRESHOLD 10
       } fault;
       u64 vf_rlen[6];
+#ifdef CONFIG_ARM
+    struct device dev;
+#endif

There are a part of PCI that is already per-arch. See arch_pci_dev in asm-arm/pci.h.

Quoted 1 line

Please define the field dev in it rather than here.

Ok, I'll check if I can do this.

Quoted 8 lines

xen/include/xen/pci.h struct pci_dev { · line 103

Quoted 6 lines
xen/include/xen/pci.h +0 -0
   };

  +#ifdef CONFIG_ARM

xen/include/xen/pci.h +3 -0
+#define to_pci_dev(p) container_of(p, struct pci_dev,dev)
+#define pci_domain_nr(dev) dev->seg
+#endif

Similarly, this should be moved in asm-arm/pci.h.

Ok.

Quoted 8 lines
Quoted 5 lines

+
  #define for_each_pdev(domain, pdev) \
      list_for_each_entry(pdev, &(domain->arch.pdev_list), domain_list)

Cheers,

Signature · 6 lines
 Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#18 lore raw

Re: [RFC v2 5/7] acpi:arm64: Add support for parsing IORT table

Hi Sameer,

Quoted 13 lines · Sameer
Quoted 4 lines · Julien Grall
Quoted 4 lines
xen/drivers/acpi/arm/iort.c +4 -0
+
+/* Redefine WARN macros */
+#undef WARN
+#undef WARN_ON
Quoted 8 lines

xen/drivers/acpi/arm/iort.c line 55

Quoted 6 lines
xen/drivers/acpi/arm/iort.c +6 -0
+#define WARN(condition, format...) ({                    \
+    int __ret_warn_on = !!(condition);                \
+    if (unlikely(__ret_warn_on))                    \
+        printk(format);                        \
+    unlikely(__ret_warn_on);                    \
+})

Again, you should at least try to modify the common code version before deciding to redefine it here.

Quoted 1 line

The xen macro seems such that it explicitly tries to block a return by wrapping this macro in a loop. I had changed the common function in the last iteration and there seemed to be a pushback.

I don't think there was any pushback on changing the common code. Jan Beulich validly requested to move that change in a separate patch.

Quoted 34 lines
Quoted 13 lines

xen/drivers/acpi/arm/iort.c static acpi_status iort_match_node_callback(struct acpi_iort_node *node, · line 290

Quoted 11 lines
xen/drivers/acpi/arm/iort.c +5 -0
+#define WARN_TAINT(cond, taint, format...) WARN(cond, format)
+#define WARN_ON(cond)                      (!!cond)
     #define IORT_TYPE_MASK(type)    (1 << (type))
   #define IORT_MSI_TYPE        (1 << ACPI_IORT_NODE_ITS_GROUP)
@@ -256,6 +286,13 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
       acpi_status status;
         if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
+        status = AE_NOT_IMPLEMENTED;
+/*
+ * We need the namespace object name from dsdt to match the iort node, this

Please add a "Xen: TODO:" in front.

Ok.

Quoted 20 lines
Quoted 9 lines
xen/drivers/acpi/arm/iort.c +4 -0
+ * will need additions to the kernel xen bus notifiers.
+ * So, disabling the named node code till a proposal is approved.
+ */
+#if 0
           struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
           struct acpi_device *adev = to_acpi_device_node(dev->fwnode);
           struct acpi_iort_named_component *ncomp;

xen/drivers/acpi/arm/iort.c static acpi_status iort_match_node_callback(struct acpi_iort_node *node, · line 318

Quoted 8 lines
xen/drivers/acpi/arm/iort.c +2 -1
@@ -275,11 +312,12 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
           status = !strcmp(ncomp->device_name, buf.pointer) ?
                               AE_OK : AE_NOT_FOUND;
           acpi_os_free(buf.pointer);
+#endif
       } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
           struct acpi_iort_root_complex *pci_rc;
-        struct pci_bus *bus;
+        struct pci_dev *pci_dev;

Do you really need to modify the code? Wouldn't it be possible to do

Quoted 2 lines
Quoted 1 line

#define pci_bus pci_dev

The pci_dev is the container for the generic device. I wanted to call this out explicitly here. We can do the above if you insist :).

I agree this can be confusing to read. But that change is not justified for the goal you want to achieve. E.g importing the code from Linux and keep in sync in the future.

With a comment on top of the definitions, you could explain when pci_bus = pci_dev at the moment.

[...]

Quoted 11 lines
Quoted 11 lines

xen/drivers/passthrough/arm/smmu.c static void __iomem *devm_ioremap_resource(struct device *dev, · line 184

Quoted 9 lines
xen/drivers/passthrough/arm/smmu.c +1 -0
index 362d578..ad956d5 100644
@@ -181,6 +181,7 @@ static void __iomem *devm_ioremap_resource(struct device *dev,
    * Xen: PCI functions
    * TODO: It should be implemented when PCI will be supported
    */
+#undef to_pci_dev

Why this change?

Quoted 1 line

I had redefine the to_pci_dev to get the actual pci_dev struct. smmu driver does not use pci yet.

That should go in a separate commit in that case with probably all stub for PCI you added (see the pci.h).

The reason behind is those changes are not directly related to this patch. Patch should be kept simple and do one thing only. This makes easier to review.

Cheers,

Signature · 6 lines
Julien Grall

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#19 lore raw

Re: [RFC v2 5/7] acpi:arm64: Add support for parsing IORT table

In reply to Sameer Goel

Hi Sameer,

xen/drivers/acpi/arm/iort.c const struct iommu_ops *iort_iommu_configure(struct device *dev) · line 637

Quoted 11 lines · Sameer Goel
xen/drivers/acpi/arm/iort.c +2 -3
@@ -583,14 +631,13 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
  	u32 streamid = 0;
 
  	if (dev_is_pci(dev)) {
-		struct pci_bus *bus = to_pci_dev(dev)->bus;
+		struct pci_dev *pci_device = to_pci_dev(dev);
  		u32 rid;
 
-		pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
-				       &rid);
+		rid = PCI_BDF2(pci_device->bus, pci_device->devfn);

I forgot to answer on this bit. On the previous it was mentioned this was wrong, but still there.

Whilst I can understand that implementing pci_for_each_dma_alias could require some work in Xen, I don't want to see rid = PCI_BDF2(...) spreading the code. So what's the plan?

Cheers,

Signature · 6 lines
Julien Grall

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#20 lore raw

Re: [RFC v2 5/7] acpi:arm64: Add support for parsing IORT table

In reply to Sameer Goel

Hi Sameer

Quoted 8 lines · Sameer Goel

Add support for parsing IORT table to initialize SMMU devices.
* The code for creating an SMMU device has been modified, so that the SMMU device can be initialized.
* The NAMED NODE code has been commented out as this will need DOM0 kernel support.
* ITS code has been included but it has not been tested.

Followup of the discussions we had on iort parsing and querying streamID and deviceId based on RID. I have extended your patchset with a patch that provides an alternative
way of parsing iort into maps : {rid-streamid}, {rid-deviceID)
which can directly be looked up for searching streamId for a rid. This
will remove the need to traverse iort table again.

The test patch just describes the proposed flow and how the parsing and query code might fit in. I have not tested it.
The code only compiles.

https://github.com/mjaggi-cavium/xen-wip/commit/df006d64bdbb5c8344de5a710da8bf64c9e8edd5
(This repo has all 7 of your patches + test code patch merged.

Note: The commit text of the patch describes the basic flow /assumptions / usage of functions. Please see the code along with the v2 design draft.
[RFC] [Draft Design v2] ACPI/IORT Support in Xen.
https://lists.xen.org/archives/html/xen-devel/2017-11/msg00512.html

I seek your advice on this. Please provide your feedback.

Thanks
Manish

Quoted 521 lines
---
  xen/arch/arm/setup.c               |   3 +
  xen/drivers/acpi/Makefile          |   1 +
  xen/drivers/acpi/arm/Makefile      |   1 +
  xen/drivers/acpi/arm/iort.c        | 173 +++++++++++++++++++++----------------
  xen/drivers/passthrough/arm/smmu.c |   1 +
  xen/include/acpi/acpi_iort.h       |  17 ++--
  xen/include/asm-arm/device.h       |   2 +
  xen/include/xen/acpi.h             |  21 +++++
  xen/include/xen/pci.h              |   8 ++
  9 files changed, 146 insertions(+), 81 deletions(-)

create mode 100644 xen/drivers/acpi/arm/Makefile

xen/arch/arm/setup.c +3 -0
index 92f173b..4ba09b2 100644
@@ -49,6 +49,7 @@
  #include <asm/setup.h>
  #include <xsm/xsm.h>
  #include <asm/acpi.h>
+#include <acpi/acpi_iort.h>
 
  struct bootinfo __initdata bootinfo;
 
@@ -796,6 +797,8 @@ void __init start_xen(unsigned long boot_phys_offset,
 
      tasklet_subsys_init();
 
+    /* Parse the ACPI iort data */
+    acpi_iort_init();
 
      xsm_dt_init();
 
xen/drivers/acpi/Makefile +1 -0
index 444b11d..e7ffd82 100644
@@ -1,5 +1,6 @@
  subdir-y += tables
  subdir-y += utilities
+subdir-$(CONFIG_ARM) += arm
  subdir-$(CONFIG_X86) += apei
 
  obj-bin-y += tables.init.o
xen/drivers/acpi/arm/Makefile new file +1 -0
new file mode 100644
index 0000000..7c039bb
@@ -0,0 +1 @@
+obj-y += iort.o
xen/drivers/acpi/arm/iort.c +99 -74
index 2e368a6..7f54062 100644
@@ -14,17 +14,47 @@
   * This file implements early detection/parsing of I/O mapping
   * reported to OS through firmware via I/O Remapping Table (IORT)
   * IORT document number: ARM DEN 0049A
+ *
+ * Based on Linux drivers/acpi/arm64/iort.c
+ * => commit ca78d3173cff3503bcd15723b049757f75762d15
+ *
+ * Xen modification:
+ * Sameer Goel <[email protected]>
+ * Copyright (C) 2017, The Linux Foundation, All rights reserved.
+ *
   */
 
-#define pr_fmt(fmt)	"ACPI: IORT: " fmt
-
-#include <linux/acpi_iort.h>
-#include <linux/iommu.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
+#include <xen/acpi.h>
+#include <acpi/acpi_iort.h>
+#include <xen/fwnode.h>
+#include <xen/iommu.h>
+#include <xen/lib.h>
+#include <xen/list.h>
+#include <xen/pci.h>
+
+#include <asm/device.h>
+
+/* Xen: Define compatibility functions */
+#define FW_BUG		"[Firmware Bug]: "
+#define pr_err(fmt, ...) printk(XENLOG_ERR fmt, ## __VA_ARGS__)
+#define pr_warn(fmt, ...) printk(XENLOG_WARNING fmt, ## __VA_ARGS__)
+
+/* Alias to Xen allocation helpers */
+#define kfree xfree
+#define kmalloc(size, flags)            _xmalloc(size, sizeof(void *))
+#define kzalloc(size, flags)            _xzalloc(size, sizeof(void *))
+
+/* Redefine WARN macros */
+#undef WARN
+#undef WARN_ON
+#define WARN(condition, format...) ({					\
+	int __ret_warn_on = !!(condition);				\
+	if (unlikely(__ret_warn_on))					\
+		printk(format);						\
+	unlikely(__ret_warn_on);					\
+})
+#define WARN_TAINT(cond, taint, format...) WARN(cond, format)
+#define WARN_ON(cond)                      (!!cond)
 
  #define IORT_TYPE_MASK(type)	(1 << (type))
  #define IORT_MSI_TYPE		(1 << ACPI_IORT_NODE_ITS_GROUP)
@@ -256,6 +286,13 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
  	acpi_status status;
 
  	if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
+		status = AE_NOT_IMPLEMENTED;
+/*
+ * We need the namespace object name from dsdt to match the iort node, this
+ * will need additions to the kernel xen bus notifiers.
+ * So, disabling the named node code till a proposal is approved.
+ */
+#if 0
  		struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
  		struct acpi_device *adev = to_acpi_device_node(dev->fwnode);
  		struct acpi_iort_named_component *ncomp;
@@ -275,11 +312,12 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
  		status = !strcmp(ncomp->device_name, buf.pointer) ?
  							AE_OK : AE_NOT_FOUND;
  		acpi_os_free(buf.pointer);
+#endif
  	} else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
  		struct acpi_iort_root_complex *pci_rc;
-		struct pci_bus *bus;
+		struct pci_dev *pci_dev;
 
-		bus = to_pci_bus(dev);
+		pci_dev = to_pci_dev(dev);
  		pci_rc = (struct acpi_iort_root_complex *)node->node_data;
 
  		/*
@@ -287,12 +325,11 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
  		 * with root complexes. Each segment number can represent only
  		 * one root complex.
  		 */
-		status = pci_rc->pci_segment_number == pci_domain_nr(bus) ?
+		status = pci_rc->pci_segment_number == pci_domain_nr(pci_dev) ?
  							AE_OK : AE_NOT_FOUND;
  	} else {
  		status = AE_NOT_FOUND;
  	}
-out:
  	return status;
  }
 
@@ -320,6 +357,11 @@ static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,
  	return 0;
  }
 
+/*
+ * Named components are not supported yet so we do not need the
+ * iort_node_get_id function
+ */
+#if 0
  static
  struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
  					u32 *id_out, u8 type_mask,
@@ -358,6 +400,7 @@ struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
 
  	return NULL;
  }
+#endif
 
  static struct acpi_iort_node *iort_node_map_rid(struct acpi_iort_node *node,
  						u32 rid_in, u32 *rid_out,
@@ -410,6 +453,10 @@ fail_map:
  	return NULL;
  }
 
+/* Xen: Comment out the NamedComponent and ITS mapping code till the support
+ * is available.
+ */
+#if 0
  static struct acpi_iort_node *iort_find_dev_node(struct device *dev)
  {
  	struct pci_bus *pbus;
@@ -481,7 +528,7 @@ static int iort_dev_find_its_id(struct device *dev, u32 req_id,
  	return 0;
  }
 
-/**
+/*
   * iort_get_device_domain() - Find MSI domain related to a device
   * @dev: The device.
   * @req_id: Requester ID for the device.
@@ -510,7 +557,7 @@ static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
  	*rid = alias;
  	return 0;
  }
-
+#endif
  static int arm_smmu_iort_xlate(struct device *dev, u32 streamid,
  			       struct fwnode_handle *fwnode,
  			       const struct iommu_ops *ops)
@@ -546,6 +593,7 @@ static const struct iommu_ops *iort_iommu_xlate(struct device *dev,
  	return ret ? NULL : ops;
  }
 
+#if 0 /* Xen: We do not need this function for Xen */
  /**
   * iort_set_dma_mask - Set-up dma mask for a device.
   *
@@ -567,7 +615,7 @@ void iort_set_dma_mask(struct device *dev)
  	if (!dev->dma_mask)
  		dev->dma_mask = &dev->coherent_dma_mask;
  }
-
+#endif
  /**
   * iort_iommu_configure - Set-up IOMMU configuration for a device.
   *
@@ -583,14 +631,13 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
  	u32 streamid = 0;
 
  	if (dev_is_pci(dev)) {
-		struct pci_bus *bus = to_pci_dev(dev)->bus;
+		struct pci_dev *pci_device = to_pci_dev(dev);
  		u32 rid;
 
-		pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
-				       &rid);
+		rid = PCI_BDF2(pci_device->bus, pci_device->devfn);
 
  		node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
-				      iort_match_node_callback, &bus->dev);
+				      iort_match_node_callback, dev);
  		if (!node)
  			return NULL;
 
@@ -600,6 +647,13 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
  		ops = iort_iommu_xlate(dev, parent, streamid);
 
  	} else {
+		return NULL;
+/*
+ * We need the namespace object name from dsdt to match the iort node, this
+ * will need additions to the kernel xen bus notifiers.
+ * So, disabling the named node code till a proposal is approved.
+ */
+#if 0
  		int i = 0;
 
  		node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
@@ -616,11 +670,17 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
  			parent = iort_node_get_id(node, &streamid,
  						  IORT_IOMMU_TYPE, i++);
  		}
+#endif
  	}
 
  	return ops;
  }
 
+/*
+ * Xen: Not using the parsing ops for now. Need to check and see if it will
+ * be useful to use these in some form, or let the driver parse IORT node.
+ */
+#if 0
  static void __init acpi_iort_register_irq(int hwirq, const char *name,
  					  int trigger,
  					  struct resource *res)
@@ -807,7 +867,7 @@ const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node)
  		return NULL;
  	}
  }
-
+#endif
  /**
   * iort_add_smmu_platform_device() - Allocate a platform device for SMMU
   * @node: Pointer to SMMU ACPI IORT node
@@ -817,78 +877,42 @@ const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node)
  static int __init iort_add_smmu_platform_device(struct acpi_iort_node *node)
  {
  	struct fwnode_handle *fwnode;
-	struct platform_device *pdev;
-	struct resource *r;
-	enum dev_dma_attr attr;
-	int ret, count;
-	const struct iort_iommu_config *ops = iort_get_iommu_cfg(node);
-
-	if (!ops)
-		return -ENODEV;
-
-	pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO);
-	if (!pdev)
-		return -ENOMEM;
-
-	count = ops->iommu_count_resources(node);
-
-	r = kcalloc(count, sizeof(*r), GFP_KERNEL);
-	if (!r) {
-		ret = -ENOMEM;
-		goto dev_put;
-	}
-
-	ops->iommu_init_resources(r, node);
+	struct device *dev;
+	int ret;
 
-	ret = platform_device_add_resources(pdev, r, count);
  	/*
-	 * Resources are duplicated in platform_device_add_resources,
-	 * free their allocated memory
+	 * Not enabling the parsing ops for now. The corresponding driver
+	 * can parse this information as needed, so deleting relevant code as
+	 * compared to base revision.
  	 */
-	kfree(r);
 
-	if (ret)
-		goto dev_put;
+	dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
 
  	/*
  	 * Add a copy of IORT node pointer to platform_data to
  	 * be used to retrieve IORT data information.
  	 */
-	ret = platform_device_add_data(pdev, &node, sizeof(node));
-	if (ret)
-		goto dev_put;
-
-	/*
-	 * We expect the dma masks to be equivalent for
-	 * all SMMUs set-ups
-	 */
-	pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+	dev->type = DEV_ACPI;
+	dev->acpi_node = node;
 
  	fwnode = iort_get_fwnode(node);
 
  	if (!fwnode) {
  		ret = -ENODEV;
-		goto dev_put;
+		goto error;
  	}
 
-	pdev->dev.fwnode = fwnode;
-
-	attr = ops->iommu_is_coherent(node) ?
-			     DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT;
-
-	/* Configure DMA for the page table walker */
-	acpi_dma_configure(&pdev->dev, attr);
+	dev->fwnode = fwnode;
 
-	ret = platform_device_add(pdev);
-	if (ret)
-		goto dma_deconfigure;
+	/* Call the acpi init functions for IOMMU devices */
+	ret = acpi_device_init(DEVICE_IOMMU, (void *)dev, node->type);
 
  	return 0;
 
-dma_deconfigure:
-	acpi_dma_deconfigure(&pdev->dev);
-dev_put:
-	platform_device_put(pdev);
+error:
+	kfree(dev);
 
  	return ret;
  }
@@ -957,5 +981,6 @@ void __init acpi_iort_init(void)
 
  	iort_init_platform_devices();
 
-	acpi_probe_device_table(iort);
+	/* Xen; Do not need a device table probe */
+	/* acpi_probe_device_table(iort);*/
  }
xen/drivers/passthrough/arm/smmu.c +1 -0
index 362d578..ad956d5 100644
@@ -181,6 +181,7 @@ static void __iomem *devm_ioremap_resource(struct device *dev,
   * Xen: PCI functions
   * TODO: It should be implemented when PCI will be supported
   */
+#undef to_pci_dev
  #define to_pci_dev(dev)	(NULL)
  static inline int pci_for_each_dma_alias(struct pci_dev *pdev,
  					 int (*fn) (struct pci_dev *pdev,
xen/include/acpi/acpi_iort.h +10 -7
index 77e0809..d4315a4 100644
@@ -19,27 +19,32 @@
  #ifndef __ACPI_IORT_H__
  #define __ACPI_IORT_H__
 
-#include <linux/acpi.h>
-#include <linux/fwnode.h>
-#include <linux/irqdomain.h>
+#include <xen/acpi.h>
+#include <asm/device.h>
 
+/* Xen: Not using IORT IRQ bindings */
+#if 0
  #define IORT_IRQ_MASK(irq)		(irq & 0xffffffffULL)
  #define IORT_IRQ_TRIGGER_MASK(irq)	((irq >> 32) & 0xffffffffULL)
 
  int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node);
  void iort_deregister_domain_token(int trans_id);
  struct fwnode_handle *iort_find_domain_token(int trans_id);
-#ifdef CONFIG_ACPI_IORT
+#endif
+#ifdef CONFIG_ARM_64
  void acpi_iort_init(void);
  bool iort_node_match(u8 type);
+#if 0
  u32 iort_msi_map_rid(struct device *dev, u32 req_id);
  struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id);
  /* IOMMU interface */
  void iort_set_dma_mask(struct device *dev);
+#endif
  const struct iommu_ops *iort_iommu_configure(struct device *dev);
  #else
  static inline void acpi_iort_init(void) { }
  static inline bool iort_node_match(u8 type) { return false; }
+#if 0
  static inline u32 iort_msi_map_rid(struct device *dev, u32 req_id)
  { return req_id; }
  static inline struct irq_domain *iort_get_device_domain(struct device *dev,
@@ -47,12 +52,10 @@ static inline struct irq_domain *iort_get_device_domain(struct device *dev,
  { return NULL; }
  /* IOMMU interface */
  static inline void iort_set_dma_mask(struct device *dev) { }
+#endif
  static inline
  const struct iommu_ops *iort_iommu_configure(struct device *dev)
  { return NULL; }
  #endif
 
-#define IORT_ACPI_DECLARE(name, table_id, fn)		\
-	ACPI_DECLARE_PROBE_ENTRY(iort, name, table_id, 0, NULL, 0, fn)
-
  #endif /* __ACPI_IORT_H__ */
xen/include/asm-arm/device.h +2 -0
index 5027c87..4eef9ce 100644
@@ -7,6 +7,7 @@
  enum device_type
  {
      DEV_DT,
+    DEV_ACPI,
  };
 
  struct dev_archdata {
@@ -20,6 +21,7 @@ struct device
  #ifdef CONFIG_HAS_DEVICE_TREE
      struct dt_device_node *of_node; /* Used by drivers imported from Linux */
  #endif
+    void *acpi_node; /*Current use case is acpi_iort_node */
      struct fwnode_handle *fwnode; /*fw device node identifier */
      struct iommu_fwspec *iommu_fwspec;
      struct dev_archdata archdata;
xen/include/xen/acpi.h +21 -0
index 9409350..2f6aae1 100644
@@ -32,6 +32,7 @@
 
  #include <acpi/acpi.h>
  #include <asm/acpi.h>
+#include <xen/fwnode.h>
 
  #define ACPI_MADT_GET_(fld, x) (((x) & ACPI_MADT_##fld##_MASK) / \
  	(ACPI_MADT_##fld##_MASK & -ACPI_MADT_##fld##_MASK))
@@ -49,6 +50,26 @@
                  (!(entry)) || (unsigned long)(entry) + sizeof(*(entry)) > (end) ||  \
                  (entry)->header.length < sizeof(*(entry)))
 
+static inline struct fwnode_handle *acpi_alloc_fwnode_static(void)
+{
+	struct fwnode_handle *fwnode;
+
+	fwnode = xzalloc(struct fwnode_handle);
+	if (!fwnode)
+		return NULL;
+
+	fwnode->type = FWNODE_ACPI_STATIC;
+
+	return fwnode;
+}
+
+static inline void acpi_free_fwnode_static(struct fwnode_handle *fwnode)
+{
+	if (!fwnode || fwnode->type != FWNODE_ACPI_STATIC)
+		return;
+
+	xfree(fwnode);
+}
  #ifdef CONFIG_ACPI
 
  enum acpi_interrupt_id {
xen/include/xen/pci.h +3 -0
index 43f2125..182b1a5 100644
@@ -92,8 +92,16 @@ struct pci_dev {
  #define PT_FAULT_THRESHOLD 10
      } fault;
      u64 vf_rlen[6];
+#ifdef CONFIG_ARM
+    struct device dev;
+#endif

xen/include/xen/pci.h struct pci_dev { · line 107

Quoted 10 lines
xen/include/xen/pci.h +5 -0
  };
 
+#ifdef CONFIG_ARM
+#define to_pci_dev(p) container_of(p, struct pci_dev,dev)
+#define pci_domain_nr(dev) dev->seg
+#endif
+
  #define for_each_pdev(domain, pdev) \
      list_for_each_entry(pdev, &(domain->arch.pdev_list), domain_list)
 

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel

#21 lore raw

Re: [RFC v2 5/7] acpi:arm64: Add support for parsing IORT table

Quoted 24 lines · Manish Jaggi

Hi Sameer

Quoted 8 lines · Sameer Goel

Add support for parsing IORT table to initialize SMMU devices.
* The code for creating an SMMU device has been modified, so that the SMMU device can be initialized.
* The NAMED NODE code has been commented out as this will need DOM0 kernel support.
* ITS code has been included but it has not been tested.

Followup of the discussions we had on iort parsing and querying streamID and deviceId based on RID. I have extended your patchset with a patch that provides an alternative
way of parsing iort into maps : {rid-streamid}, {rid-deviceID)
which can directly be looked up for searching streamId for a rid. This
will remove the need to traverse iort table again.

The test patch just describes the proposed flow and how the parsing and query code might fit in. I have not tested it.
The code only compiles.

https://github.com/mjaggi-cavium/xen-wip/commit/df006d64bdbb5c8344de5a710da8bf64c9e8edd5
(This repo has all 7 of your patches + test code patch merged.

Quoted 6 lines

Note: The commit text of the patch describes the basic flow /assumptions / usage of functions. Please see the code along with the v2 design draft.
[RFC] [Draft Design v2] ACPI/IORT Support in Xen.
https://lists.xen.org/archives/html/xen-devel/2017-11/msg00512.html

I seek your advice on this. Please provide your feedback.

I responded back on the other thread. I think we are fixing something that is not broken. I will try to post a couple of new RFCs and let's discuss this with incremental changes on the mailing list.

Thanks,
Sameer

Quoted 493 lines

Thanks
Manish

Quoted 479 lines

---
  xen/arch/arm/setup.c               |   3 + xen/drivers/acpi/Makefile          |   1 + xen/drivers/acpi/arm/Makefile      |   1 + xen/drivers/acpi/arm/iort.c        | 173 +++++++++++++++++++++---------------- xen/drivers/passthrough/arm/smmu.c |   1 + xen/include/acpi/acpi_iort.h       |  17 ++-- xen/include/asm-arm/device.h       |   2 +
  xen/include/xen/acpi.h             |  21 +++++ xen/include/xen/pci.h              |   8 ++

  9 files changed, 146 insertions(+), 81 deletions(-)

  create mode 100644 xen/drivers/acpi/arm/Makefile

xen/arch/arm/setup.c +1 -0
index 92f173b..4ba09b2 100644
@@ -49,6 +49,7 @@
   #include <asm/setup.h>
   #include <xsm/xsm.h>
   #include <asm/acpi.h>
+#include <acpi/acpi_iort.h>
     struct bootinfo __initdata bootinfo;
   @@ -796,6 +797,8 @@ void __init start_xen(unsigned long boot_phys_offset,
         tasklet_subsys_init();

  +    /* Parse the ACPI iort data */
+    acpi_iort_init();
        xsm_dt_init();
  diff --git a/xen/drivers/acpi/Makefile b/xen/drivers/acpi/Makefile index 444b11d..e7ffd82 100644

xen/drivers/acpi/Makefile +0 -0
@@ -1,5 +1,6 @@

  subdir-y += tables
  subdir-y += utilities
+subdir-$(CONFIG_ARM) += arm
  subdir-$(CONFIG_X86) += apei
    obj-bin-y += tables.init.o

xen/drivers/acpi/arm/Makefile new file +1 -0
new file mode 100644
index 0000000..7c039bb
@@ -0,0 +1 @@
+obj-y += iort.o
xen/drivers/acpi/arm/iort.c +8 -0
index 2e368a6..7f54062 100644
@@ -14,17 +14,47 @@
    * This file implements early detection/parsing of I/O mapping
    * reported to OS through firmware via I/O Remapping Table (IORT)
    * IORT document number: ARM DEN 0049A
+ *
+ * Based on Linux drivers/acpi/arm64/iort.c
+ * => commit ca78d3173cff3503bcd15723b049757f75762d15
+ *
+ * Xen modification:
+ * Sameer Goel <[email protected]>
+ * Copyright (C) 2017, The Linux Foundation, All rights reserved.
+ *
    */

  -#define pr_fmt(fmt)    "ACPI: IORT: " fmt

xen/drivers/acpi/arm/iort.c +31 -8
-
-#include <linux/acpi_iort.h>
-#include <linux/iommu.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
+#include <xen/acpi.h>
+#include <acpi/acpi_iort.h>
+#include <xen/fwnode.h>
+#include <xen/iommu.h>
+#include <xen/lib.h>
+#include <xen/list.h>
+#include <xen/pci.h>
+
+#include <asm/device.h>
+
+/* Xen: Define compatibility functions */
+#define FW_BUG        "[Firmware Bug]: "
+#define pr_err(fmt, ...) printk(XENLOG_ERR fmt, ## __VA_ARGS__)
+#define pr_warn(fmt, ...) printk(XENLOG_WARNING fmt, ## __VA_ARGS__)
+
+/* Alias to Xen allocation helpers */
+#define kfree xfree
+#define kmalloc(size, flags)            _xmalloc(size, sizeof(void *))
+#define kzalloc(size, flags)            _xzalloc(size, sizeof(void *))
+
+/* Redefine WARN macros */
+#undef WARN
+#undef WARN_ON
+#define WARN(condition, format...) ({                    \
+    int __ret_warn_on = !!(condition);                \
+    if (unlikely(__ret_warn_on))                    \
+        printk(format);                        \
+    unlikely(__ret_warn_on);                    \
+})
+#define WARN_TAINT(cond, taint, format...) WARN(cond, format)
+#define WARN_ON(cond)                      (!!cond)

    #define IORT_TYPE_MASK(type)    (1 << (type)) #define IORT_MSI_TYPE        (1 << ACPI_IORT_NODE_ITS_GROUP)

xen/drivers/acpi/arm/iort.c +7 -0
@@ -256,6 +286,13 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
       acpi_status status;
         if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
+        status = AE_NOT_IMPLEMENTED;
+/*
+ * We need the namespace object name from dsdt to match the iort node, this
+ * will need additions to the kernel xen bus notifiers.
+ * So, disabling the named node code till a proposal is approved.
+ */
+#if 0
           struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
           struct acpi_device *adev = to_acpi_device_node(dev->fwnode);
           struct acpi_iort_named_component *ncomp;
@@ -275,11 +312,12 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,

          status = !strcmp(ncomp->device_name, buf.pointer) ? AE_OK : AE_NOT_FOUND; acpi_os_free(buf.pointer);
+#endif
      } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { struct acpi_iort_root_complex *pci_rc;
-        struct pci_bus *bus;
+        struct pci_dev *pci_dev;
  -        bus = to_pci_bus(dev);
+        pci_dev = to_pci_dev(dev);
          pci_rc = (struct acpi_iort_root_complex *)node->node_data; /*

xen/drivers/acpi/arm/iort.c +1 -2
@@ -287,12 +325,11 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
            * with root complexes. Each segment number can represent only
            * one root complex.
            */
-        status = pci_rc->pci_segment_number == pci_domain_nr(bus) ?
+        status = pci_rc->pci_segment_number == pci_domain_nr(pci_dev) ?
                               AE_OK : AE_NOT_FOUND;
       } else {
           status = AE_NOT_FOUND;
       }
-out:
       return status;
   }
   @@ -320,6 +357,11 @@ static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,

      return 0;
  }
  +/*
+ * Named components are not supported yet so we do not need the
+ * iort_node_get_id function
+ */
+#if 0
  static
  struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node, u32 *id_out, u8 type_mask,

xen/drivers/acpi/arm/iort.c +1 -0
@@ -358,6 +400,7 @@ struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
         return NULL;
   }
+#endif
     static struct acpi_iort_node *iort_node_map_rid(struct acpi_iort_node *node,
                           u32 rid_in, u32 *rid_out,
@@ -410,6 +453,10 @@ fail_map:
       return NULL;
   }

  +/* Xen: Comment out the NamedComponent and ITS mapping code till the support
+ * is available.
+ */
+#if 0
  static struct acpi_iort_node *iort_find_dev_node(struct device *dev) {
      struct pci_bus *pbus;

xen/drivers/acpi/arm/iort.c +0 -0
@@ -481,7 +528,7 @@ static int iort_dev_find_its_id(struct device *dev, u32 req_id,
       return 0;
   }

  -/**
+/*
   * iort_get_device_domain() - Find MSI domain related to a device
   * @dev: The device.
   * @req_id: Requester ID for the device.

xen/drivers/acpi/arm/iort.c +1 -1
@@ -510,7 +557,7 @@ static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
       *rid = alias;
       return 0;
   }
-
+#endif
   static int arm_smmu_iort_xlate(struct device *dev, u32 streamid,
                      struct fwnode_handle *fwnode,
                      const struct iommu_ops *ops)
@@ -546,6 +593,7 @@ static const struct iommu_ops *iort_iommu_xlate(struct device *dev,
       return ret ? NULL : ops;
   }

  +#if 0 /* Xen: We do not need this function for Xen */ /**
   * iort_set_dma_mask - Set-up dma mask for a device. *

xen/drivers/acpi/arm/iort.c +11 -4
@@ -567,7 +615,7 @@ void iort_set_dma_mask(struct device *dev)
       if (!dev->dma_mask)
           dev->dma_mask = &dev->coherent_dma_mask;
   }
-
+#endif
   /**
    * iort_iommu_configure - Set-up IOMMU configuration for a device.
    *
@@ -583,14 +631,13 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
       u32 streamid = 0;
         if (dev_is_pci(dev)) {
-        struct pci_bus *bus = to_pci_dev(dev)->bus;
+        struct pci_dev *pci_device = to_pci_dev(dev);
           u32 rid;
   -        pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
-                       &rid);
+        rid = PCI_BDF2(pci_device->bus, pci_device->devfn);
             node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
-                      iort_match_node_callback, &bus->dev);
+                      iort_match_node_callback, dev);
           if (!node)
               return NULL;
   @@ -600,6 +647,13 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
           ops = iort_iommu_xlate(dev, parent, streamid);
         } else {
+        return NULL;
+/*
+ * We need the namespace object name from dsdt to match the iort node, this
+ * will need additions to the kernel xen bus notifiers.
+ * So, disabling the named node code till a proposal is approved.
+ */
+#if 0

          int i = 0;
            node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,

xen/drivers/acpi/arm/iort.c +1 -0
@@ -616,11 +670,17 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)
               parent = iort_node_get_id(node, &streamid,
                             IORT_IOMMU_TYPE, i++);
           }
+#endif
       }
         return ops;
   }

  +/*
+ * Xen: Not using the parsing ops for now. Need to check and see if it will
+ * be useful to use these in some form, or let the driver parse IORT node.
+ */
+#if 0
  static void __init acpi_iort_register_irq(int hwirq, const char *name, int trigger,
                        struct resource *res)

xen/drivers/acpi/arm/iort.c +15 -45
@@ -807,7 +867,7 @@ const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node)
           return NULL;
       }
   }
-
+#endif
   /**
    * iort_add_smmu_platform_device() - Allocate a platform device for SMMU
    * @node: Pointer to SMMU ACPI IORT node
@@ -817,78 +877,42 @@ const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node)
   static int __init iort_add_smmu_platform_device(struct acpi_iort_node *node)
   {
       struct fwnode_handle *fwnode;
-    struct platform_device *pdev;
-    struct resource *r;
-    enum dev_dma_attr attr;
-    int ret, count;
-    const struct iort_iommu_config *ops = iort_get_iommu_cfg(node);
-
-    if (!ops)
-        return -ENODEV;
-
-    pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO);
-    if (!pdev)
-        return -ENOMEM;
-
-    count = ops->iommu_count_resources(node);
-
-    r = kcalloc(count, sizeof(*r), GFP_KERNEL);
-    if (!r) {
-        ret = -ENOMEM;
-        goto dev_put;
-    }
-
-    ops->iommu_init_resources(r, node);
+    struct device *dev;
+    int ret;
   -    ret = platform_device_add_resources(pdev, r, count);
       /*
-     * Resources are duplicated in platform_device_add_resources,
-     * free their allocated memory
+     * Not enabling the parsing ops for now. The corresponding driver
+     * can parse this information as needed, so deleting relevant code as
+     * compared to base revision.
        */
-    kfree(r);
   -    if (ret)
-        goto dev_put;
+    dev = kzalloc(sizeof(struct device), GFP_KERNEL);
+    if (!dev)
+        return -ENOMEM;
         /*
        * Add a copy of IORT node pointer to platform_data to
        * be used to retrieve IORT data information.
        */
-    ret = platform_device_add_data(pdev, &node, sizeof(node));
-    if (ret)
-        goto dev_put;
-
-    /*
-     * We expect the dma masks to be equivalent for
-     * all SMMUs set-ups
-     */
-    pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+    dev->type = DEV_ACPI;
+    dev->acpi_node = node;
         fwnode = iort_get_fwnode(node);
         if (!fwnode) {
           ret = -ENODEV;
-        goto dev_put;
+        goto error;
       }
   -    pdev->dev.fwnode = fwnode;
-
-    attr = ops->iommu_is_coherent(node) ?
-                 DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT;
-
-    /* Configure DMA for the page table walker */
-    acpi_dma_configure(&pdev->dev, attr);
+    dev->fwnode = fwnode;
   -    ret = platform_device_add(pdev);
-    if (ret)
-        goto dma_deconfigure;
+    /* Call the acpi init functions for IOMMU devices */
+    ret = acpi_device_init(DEVICE_IOMMU, (void *)dev, node->type);
         return 0;

  -dma_deconfigure:
-    acpi_dma_deconfigure(&pdev->dev);

xen/drivers/acpi/arm/iort.c +2 -2
-dev_put:
-    platform_device_put(pdev);
+error:
+    kfree(dev);

        return ret;
  }

xen/drivers/acpi/arm/iort.c +2 -0
@@ -957,5 +981,6 @@ void __init acpi_iort_init(void)
         iort_init_platform_devices();
   -    acpi_probe_device_table(iort);
+    /* Xen; Do not need a device table probe */
+    /* acpi_probe_device_table(iort);*/
   }
xen/drivers/passthrough/arm/smmu.c +1 -0
index 362d578..ad956d5 100644
@@ -181,6 +181,7 @@ static void __iomem *devm_ioremap_resource(struct device *dev,
    * Xen: PCI functions
    * TODO: It should be implemented when PCI will be supported
    */
+#undef to_pci_dev
   #define to_pci_dev(dev)    (NULL)
   static inline int pci_for_each_dma_alias(struct pci_dev *pdev,
                        int (*fn) (struct pci_dev *pdev,
xen/include/acpi/acpi_iort.h +0 -0
index 77e0809..d4315a4 100644
@@ -19,27 +19,32 @@
   #ifndef __ACPI_IORT_H__
   #define __ACPI_IORT_H__

  -#include <linux/acpi.h>

xen/include/acpi/acpi_iort.h +2 -2
-#include <linux/fwnode.h>
-#include <linux/irqdomain.h>
+#include <xen/acpi.h>
+#include <asm/device.h>

  +/* Xen: Not using IORT IRQ bindings */
+#if 0
  #define IORT_IRQ_MASK(irq)        (irq & 0xffffffffULL)
  #define IORT_IRQ_TRIGGER_MASK(irq)    ((irq >> 32) & 0xffffffffULL) int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node); void iort_deregister_domain_token(int trans_id);
  struct fwnode_handle *iort_find_domain_token(int trans_id);

xen/include/acpi/acpi_iort.h +2 -1
-#ifdef CONFIG_ACPI_IORT
+#endif
+#ifdef CONFIG_ARM_64

  void acpi_iort_init(void);
  bool iort_node_match(u8 type);
+#if 0
  u32 iort_msi_map_rid(struct device *dev, u32 req_id);
  struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id); /* IOMMU interface */
  void iort_set_dma_mask(struct device *dev);
+#endif
  const struct iommu_ops *iort_iommu_configure(struct device *dev); #else
  static inline void acpi_iort_init(void) { }
  static inline bool iort_node_match(u8 type) { return false; } +#if 0
  static inline u32 iort_msi_map_rid(struct device *dev, u32 req_id) { return req_id; }
  static inline struct irq_domain *iort_get_device_domain(struct device *dev,

xen/include/acpi/acpi_iort.h +1 -0
@@ -47,12 +52,10 @@ static inline struct irq_domain *iort_get_device_domain(struct device *dev,
   { return NULL; }
   /* IOMMU interface */
   static inline void iort_set_dma_mask(struct device *dev) { }
+#endif

  static inline
  const struct iommu_ops *iort_iommu_configure(struct device *dev) { return NULL; }
  #endif
  -#define IORT_ACPI_DECLARE(name, table_id, fn)        \
-    ACPI_DECLARE_PROBE_ENTRY(iort, name, table_id, 0, NULL, 0, fn) -
  #endif /* __ACPI_IORT_H__ */

xen/include/asm-arm/device.h +0 -0
index 5027c87..4eef9ce 100644
@@ -7,6 +7,7 @@

  enum device_type
  {
      DEV_DT,
+    DEV_ACPI,
  };
    struct dev_archdata {

xen/include/asm-arm/device.h +0 -0
@@ -20,6 +21,7 @@ struct device
   #ifdef CONFIG_HAS_DEVICE_TREE

      struct dt_device_node *of_node; /* Used by drivers imported from Linux */ #endif
+    void *acpi_node; /*Current use case is acpi_iort_node */ struct fwnode_handle *fwnode; /*fw device node identifier */ struct iommu_fwspec *iommu_fwspec;
      struct dev_archdata archdata;

xen/include/xen/acpi.h +20 -0
index 9409350..2f6aae1 100644
@@ -32,6 +32,7 @@
     #include <acpi/acpi.h>
   #include <asm/acpi.h>
+#include <xen/fwnode.h>
     #define ACPI_MADT_GET_(fld, x) (((x) & ACPI_MADT_##fld##_MASK) / \
       (ACPI_MADT_##fld##_MASK & -ACPI_MADT_##fld##_MASK))
@@ -49,6 +50,26 @@
                   (!(entry)) || (unsigned long)(entry) + sizeof(*(entry)) > (end) ||  \
                   (entry)->header.length < sizeof(*(entry)))
   +static inline struct fwnode_handle *acpi_alloc_fwnode_static(void)
+{
+    struct fwnode_handle *fwnode;
+
+    fwnode = xzalloc(struct fwnode_handle);
+    if (!fwnode)
+        return NULL;
+
+    fwnode->type = FWNODE_ACPI_STATIC;
+
+    return fwnode;
+}
+
+static inline void acpi_free_fwnode_static(struct fwnode_handle *fwnode)
+{
+    if (!fwnode || fwnode->type != FWNODE_ACPI_STATIC)
+        return;
+
+    xfree(fwnode);
+}
   #ifdef CONFIG_ACPI
     enum acpi_interrupt_id {
xen/include/xen/pci.h +3 -0
index 43f2125..182b1a5 100644
@@ -92,8 +92,16 @@ struct pci_dev {
   #define PT_FAULT_THRESHOLD 10
       } fault;
       u64 vf_rlen[6];
+#ifdef CONFIG_ARM
+    struct device dev;
+#endif
   };

  +#ifdef CONFIG_ARM

xen/include/xen/pci.h +4 -0
+#define to_pci_dev(p) container_of(p, struct pci_dev,dev)
+#define pci_domain_nr(dev) dev->seg
+#endif
+
Quoted 3 lines

  #define for_each_pdev(domain, pdev) \
      list_for_each_entry(pdev, &(domain->arch.pdev_list), domain_list)

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel

Signature · 6 lines
 Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#22 lore raw

Re: [RFC v2 5/7] acpi:arm64: Add support for parsing IORT table

Hi Sameer,

Quoted 27 lines · Sameer
Quoted 24 lines · Manish Jaggi

Hi Sameer

Quoted 8 lines · Sameer Goel

Add support for parsing IORT table to initialize SMMU devices.
* The code for creating an SMMU device has been modified, so that the SMMU device can be initialized.
* The NAMED NODE code has been commented out as this will need DOM0 kernel support.
* ITS code has been included but it has not been tested.

Followup of the discussions we had on iort parsing and querying streamID and deviceId based on RID. I have extended your patchset with a patch that provides an alternative
way of parsing iort into maps : {rid-streamid}, {rid-deviceID)
which can directly be looked up for searching streamId for a rid. This
will remove the need to traverse iort table again.

The test patch just describes the proposed flow and how the parsing and query code might fit in. I have not tested it.
The code only compiles.

https://github.com/mjaggi-cavium/xen-wip/commit/df006d64bdbb5c8344de5a710da8bf64c9e8edd5
(This repo has all 7 of your patches + test code patch merged.

Quoted 7 lines
Quoted 6 lines

Note: The commit text of the patch describes the basic flow /assumptions / usage of functions. Please see the code along with the v2 design draft.
[RFC] [Draft Design v2] ACPI/IORT Support in Xen.
https://lists.xen.org/archives/html/xen-devel/2017-11/msg00512.html

I seek your advice on this. Please provide your feedback.

I responded back on the other thread. I think we are fixing something that is not broken. I will try to post a couple of new RFCs and let's discuss this with incremental changes on the mailing list.

That other thread was a separate mailing list. For the benefits of the rest of the community it would be nice if you can post the summary here.

However, nobody said the code was broken. IORT will be used in various place in Xen and Manish is looking whether we can parse only once and for all the IORT. I think it is latest design is promising for that.

Cheers,

Signature · 6 lines
Julien Grall

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#23 lore raw

RFC v2 6/7 Add verbatim copy of arm-smmu-v3.c from Linux

In reply to Sameer Goel

Based on commit bdf95923086fb359ccb44c815724c3ace1611c90

This is a verbatim snapshot of arm-smmu-v3.c from Linux kernel source code.

No Xen code has been added and the file is not built.

---
 xen/drivers/passthrough/arm/smmu-v3.c | 2791 +++++++++++++++++++++++++++++++++
 1 file changed, 2791 insertions(+)
 create mode 100644 xen/drivers/passthrough/arm/smmu-v3.c
xen/drivers/passthrough/arm/smmu-v3.c new file +2791 -0
new file mode 100644
index 0000000..380969a
@@ -0,0 +1,2791 @@
+/*
+ * IOMMU API for ARM architected SMMUv3 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2015 ARM Limited
+ *
+ * Author: Will Deacon <[email protected]>
+ *
+ * This driver is powered by bad coffee and bombay mix.
+ */
+
+#include <linux/acpi.h>
+#include <linux/acpi_iort.h>
+#include <linux/delay.h>
+#include <linux/dma-iommu.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/iommu.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_iommu.h>
+#include <linux/of_platform.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+#include <linux/amba/bus.h>
+
+#include "io-pgtable.h"
+
+/* MMIO registers */
+#define ARM_SMMU_IDR0			0x0
+#define IDR0_ST_LVL_SHIFT		27
+#define IDR0_ST_LVL_MASK		0x3
+#define IDR0_ST_LVL_2LVL		(1 << IDR0_ST_LVL_SHIFT)
+#define IDR0_STALL_MODEL_SHIFT		24
+#define IDR0_STALL_MODEL_MASK		0x3
+#define IDR0_STALL_MODEL_STALL		(0 << IDR0_STALL_MODEL_SHIFT)
+#define IDR0_STALL_MODEL_FORCE		(2 << IDR0_STALL_MODEL_SHIFT)
+#define IDR0_TTENDIAN_SHIFT		21
+#define IDR0_TTENDIAN_MASK		0x3
+#define IDR0_TTENDIAN_LE		(2 << IDR0_TTENDIAN_SHIFT)
+#define IDR0_TTENDIAN_BE		(3 << IDR0_TTENDIAN_SHIFT)
+#define IDR0_TTENDIAN_MIXED		(0 << IDR0_TTENDIAN_SHIFT)
+#define IDR0_CD2L			(1 << 19)
+#define IDR0_VMID16			(1 << 18)
+#define IDR0_PRI			(1 << 16)
+#define IDR0_SEV			(1 << 14)
+#define IDR0_MSI			(1 << 13)
+#define IDR0_ASID16			(1 << 12)
+#define IDR0_ATS			(1 << 10)
+#define IDR0_HYP			(1 << 9)
+#define IDR0_COHACC			(1 << 4)
+#define IDR0_TTF_SHIFT			2
+#define IDR0_TTF_MASK			0x3
+#define IDR0_TTF_AARCH64		(2 << IDR0_TTF_SHIFT)
+#define IDR0_TTF_AARCH32_64		(3 << IDR0_TTF_SHIFT)
+#define IDR0_S1P			(1 << 1)
+#define IDR0_S2P			(1 << 0)
+
+#define ARM_SMMU_IDR1			0x4
+#define IDR1_TABLES_PRESET		(1 << 30)
+#define IDR1_QUEUES_PRESET		(1 << 29)
+#define IDR1_REL			(1 << 28)
+#define IDR1_CMDQ_SHIFT			21
+#define IDR1_CMDQ_MASK			0x1f
+#define IDR1_EVTQ_SHIFT			16
+#define IDR1_EVTQ_MASK			0x1f
+#define IDR1_PRIQ_SHIFT			11
+#define IDR1_PRIQ_MASK			0x1f
+#define IDR1_SSID_SHIFT			6
+#define IDR1_SSID_MASK			0x1f
+#define IDR1_SID_SHIFT			0
+#define IDR1_SID_MASK			0x3f
+
+#define ARM_SMMU_IDR5			0x14
+#define IDR5_STALL_MAX_SHIFT		16
+#define IDR5_STALL_MAX_MASK		0xffff
+#define IDR5_GRAN64K			(1 << 6)
+#define IDR5_GRAN16K			(1 << 5)
+#define IDR5_GRAN4K			(1 << 4)
+#define IDR5_OAS_SHIFT			0
+#define IDR5_OAS_MASK			0x7
+#define IDR5_OAS_32_BIT			(0 << IDR5_OAS_SHIFT)
+#define IDR5_OAS_36_BIT			(1 << IDR5_OAS_SHIFT)
+#define IDR5_OAS_40_BIT			(2 << IDR5_OAS_SHIFT)
+#define IDR5_OAS_42_BIT			(3 << IDR5_OAS_SHIFT)
+#define IDR5_OAS_44_BIT			(4 << IDR5_OAS_SHIFT)
+#define IDR5_OAS_48_BIT			(5 << IDR5_OAS_SHIFT)
+
+#define ARM_SMMU_CR0			0x20
+#define CR0_CMDQEN			(1 << 3)
+#define CR0_EVTQEN			(1 << 2)
+#define CR0_PRIQEN			(1 << 1)
+#define CR0_SMMUEN			(1 << 0)
+
+#define ARM_SMMU_CR0ACK			0x24
+
+#define ARM_SMMU_CR1			0x28
+#define CR1_SH_NSH			0
+#define CR1_SH_OSH			2
+#define CR1_SH_ISH			3
+#define CR1_CACHE_NC			0
+#define CR1_CACHE_WB			1
+#define CR1_CACHE_WT			2
+#define CR1_TABLE_SH_SHIFT		10
+#define CR1_TABLE_OC_SHIFT		8
+#define CR1_TABLE_IC_SHIFT		6
+#define CR1_QUEUE_SH_SHIFT		4
+#define CR1_QUEUE_OC_SHIFT		2
+#define CR1_QUEUE_IC_SHIFT		0
+
+#define ARM_SMMU_CR2			0x2c
+#define CR2_PTM				(1 << 2)
+#define CR2_RECINVSID			(1 << 1)
+#define CR2_E2H				(1 << 0)
+
+#define ARM_SMMU_GBPA			0x44
+#define GBPA_ABORT			(1 << 20)
+#define GBPA_UPDATE			(1 << 31)
+
+#define ARM_SMMU_IRQ_CTRL		0x50
+#define IRQ_CTRL_EVTQ_IRQEN		(1 << 2)
+#define IRQ_CTRL_PRIQ_IRQEN		(1 << 1)
+#define IRQ_CTRL_GERROR_IRQEN		(1 << 0)
+
+#define ARM_SMMU_IRQ_CTRLACK		0x54
+
+#define ARM_SMMU_GERROR			0x60
+#define GERROR_SFM_ERR			(1 << 8)
+#define GERROR_MSI_GERROR_ABT_ERR	(1 << 7)
+#define GERROR_MSI_PRIQ_ABT_ERR		(1 << 6)
+#define GERROR_MSI_EVTQ_ABT_ERR		(1 << 5)
+#define GERROR_MSI_CMDQ_ABT_ERR		(1 << 4)
+#define GERROR_PRIQ_ABT_ERR		(1 << 3)
+#define GERROR_EVTQ_ABT_ERR		(1 << 2)
+#define GERROR_CMDQ_ERR			(1 << 0)
+#define GERROR_ERR_MASK			0xfd
+
+#define ARM_SMMU_GERRORN		0x64
+
+#define ARM_SMMU_GERROR_IRQ_CFG0	0x68
+#define ARM_SMMU_GERROR_IRQ_CFG1	0x70
+#define ARM_SMMU_GERROR_IRQ_CFG2	0x74
+
+#define ARM_SMMU_STRTAB_BASE		0x80
+#define STRTAB_BASE_RA			(1UL << 62)
+#define STRTAB_BASE_ADDR_SHIFT		6
+#define STRTAB_BASE_ADDR_MASK		0x3ffffffffffUL
+
+#define ARM_SMMU_STRTAB_BASE_CFG	0x88
+#define STRTAB_BASE_CFG_LOG2SIZE_SHIFT	0
+#define STRTAB_BASE_CFG_LOG2SIZE_MASK	0x3f
+#define STRTAB_BASE_CFG_SPLIT_SHIFT	6
+#define STRTAB_BASE_CFG_SPLIT_MASK	0x1f
+#define STRTAB_BASE_CFG_FMT_SHIFT	16
+#define STRTAB_BASE_CFG_FMT_MASK	0x3
+#define STRTAB_BASE_CFG_FMT_LINEAR	(0 << STRTAB_BASE_CFG_FMT_SHIFT)
+#define STRTAB_BASE_CFG_FMT_2LVL	(1 << STRTAB_BASE_CFG_FMT_SHIFT)
+
+#define ARM_SMMU_CMDQ_BASE		0x90
+#define ARM_SMMU_CMDQ_PROD		0x98
+#define ARM_SMMU_CMDQ_CONS		0x9c
+
+#define ARM_SMMU_EVTQ_BASE		0xa0
+#define ARM_SMMU_EVTQ_PROD		0x100a8
+#define ARM_SMMU_EVTQ_CONS		0x100ac
+#define ARM_SMMU_EVTQ_IRQ_CFG0		0xb0
+#define ARM_SMMU_EVTQ_IRQ_CFG1		0xb8
+#define ARM_SMMU_EVTQ_IRQ_CFG2		0xbc
+
+#define ARM_SMMU_PRIQ_BASE		0xc0
+#define ARM_SMMU_PRIQ_PROD		0x100c8
+#define ARM_SMMU_PRIQ_CONS		0x100cc
+#define ARM_SMMU_PRIQ_IRQ_CFG0		0xd0
+#define ARM_SMMU_PRIQ_IRQ_CFG1		0xd8
+#define ARM_SMMU_PRIQ_IRQ_CFG2		0xdc
+
+/* Common MSI config fields */
+#define MSI_CFG0_ADDR_SHIFT		2
+#define MSI_CFG0_ADDR_MASK		0x3fffffffffffUL
+#define MSI_CFG2_SH_SHIFT		4
+#define MSI_CFG2_SH_NSH			(0UL << MSI_CFG2_SH_SHIFT)
+#define MSI_CFG2_SH_OSH			(2UL << MSI_CFG2_SH_SHIFT)
+#define MSI_CFG2_SH_ISH			(3UL << MSI_CFG2_SH_SHIFT)
+#define MSI_CFG2_MEMATTR_SHIFT		0
+#define MSI_CFG2_MEMATTR_DEVICE_nGnRE	(0x1 << MSI_CFG2_MEMATTR_SHIFT)
+
+#define Q_IDX(q, p)			((p) & ((1 << (q)->max_n_shift) - 1))
+#define Q_WRP(q, p)			((p) & (1 << (q)->max_n_shift))
+#define Q_OVERFLOW_FLAG			(1 << 31)
+#define Q_OVF(q, p)			((p) & Q_OVERFLOW_FLAG)
+#define Q_ENT(q, p)			((q)->base +			\
+					 Q_IDX(q, p) * (q)->ent_dwords)
+
+#define Q_BASE_RWA			(1UL << 62)
+#define Q_BASE_ADDR_SHIFT		5
+#define Q_BASE_ADDR_MASK		0xfffffffffffUL
+#define Q_BASE_LOG2SIZE_SHIFT		0
+#define Q_BASE_LOG2SIZE_MASK		0x1fUL
+
+/*
+ * Stream table.
+ *
+ * Linear: Enough to cover 1 << IDR1.SIDSIZE entries
+ * 2lvl: 128k L1 entries,
+ *       256 lazy entries per table (each table covers a PCI bus)
+ */
+#define STRTAB_L1_SZ_SHIFT		20
+#define STRTAB_SPLIT			8
+
+#define STRTAB_L1_DESC_DWORDS		1
+#define STRTAB_L1_DESC_SPAN_SHIFT	0
+#define STRTAB_L1_DESC_SPAN_MASK	0x1fUL
+#define STRTAB_L1_DESC_L2PTR_SHIFT	6
+#define STRTAB_L1_DESC_L2PTR_MASK	0x3ffffffffffUL
+
+#define STRTAB_STE_DWORDS		8
+#define STRTAB_STE_0_V			(1UL << 0)
+#define STRTAB_STE_0_CFG_SHIFT		1
+#define STRTAB_STE_0_CFG_MASK		0x7UL
+#define STRTAB_STE_0_CFG_ABORT		(0UL << STRTAB_STE_0_CFG_SHIFT)
+#define STRTAB_STE_0_CFG_BYPASS		(4UL << STRTAB_STE_0_CFG_SHIFT)
+#define STRTAB_STE_0_CFG_S1_TRANS	(5UL << STRTAB_STE_0_CFG_SHIFT)
+#define STRTAB_STE_0_CFG_S2_TRANS	(6UL << STRTAB_STE_0_CFG_SHIFT)
+
+#define STRTAB_STE_0_S1FMT_SHIFT	4
+#define STRTAB_STE_0_S1FMT_LINEAR	(0UL << STRTAB_STE_0_S1FMT_SHIFT)
+#define STRTAB_STE_0_S1CTXPTR_SHIFT	6
+#define STRTAB_STE_0_S1CTXPTR_MASK	0x3ffffffffffUL
+#define STRTAB_STE_0_S1CDMAX_SHIFT	59
+#define STRTAB_STE_0_S1CDMAX_MASK	0x1fUL
+
+#define STRTAB_STE_1_S1C_CACHE_NC	0UL
+#define STRTAB_STE_1_S1C_CACHE_WBRA	1UL
+#define STRTAB_STE_1_S1C_CACHE_WT	2UL
+#define STRTAB_STE_1_S1C_CACHE_WB	3UL
+#define STRTAB_STE_1_S1C_SH_NSH		0UL
+#define STRTAB_STE_1_S1C_SH_OSH		2UL
+#define STRTAB_STE_1_S1C_SH_ISH		3UL
+#define STRTAB_STE_1_S1CIR_SHIFT	2
+#define STRTAB_STE_1_S1COR_SHIFT	4
+#define STRTAB_STE_1_S1CSH_SHIFT	6
+
+#define STRTAB_STE_1_S1STALLD		(1UL << 27)
+
+#define STRTAB_STE_1_EATS_ABT		0UL
+#define STRTAB_STE_1_EATS_TRANS		1UL
+#define STRTAB_STE_1_EATS_S1CHK		2UL
+#define STRTAB_STE_1_EATS_SHIFT		28
+
+#define STRTAB_STE_1_STRW_NSEL1		0UL
+#define STRTAB_STE_1_STRW_EL2		2UL
+#define STRTAB_STE_1_STRW_SHIFT		30
+
+#define STRTAB_STE_1_SHCFG_INCOMING	1UL
+#define STRTAB_STE_1_SHCFG_SHIFT	44
+
+#define STRTAB_STE_2_S2VMID_SHIFT	0
+#define STRTAB_STE_2_S2VMID_MASK	0xffffUL
+#define STRTAB_STE_2_VTCR_SHIFT		32
+#define STRTAB_STE_2_VTCR_MASK		0x7ffffUL
+#define STRTAB_STE_2_S2AA64		(1UL << 51)
+#define STRTAB_STE_2_S2ENDI		(1UL << 52)
+#define STRTAB_STE_2_S2PTW		(1UL << 54)
+#define STRTAB_STE_2_S2R		(1UL << 58)
+
+#define STRTAB_STE_3_S2TTB_SHIFT	4
+#define STRTAB_STE_3_S2TTB_MASK		0xfffffffffffUL
+
+/* Context descriptor (stage-1 only) */
+#define CTXDESC_CD_DWORDS		8
+#define CTXDESC_CD_0_TCR_T0SZ_SHIFT	0
+#define ARM64_TCR_T0SZ_SHIFT		0
+#define ARM64_TCR_T0SZ_MASK		0x1fUL
+#define CTXDESC_CD_0_TCR_TG0_SHIFT	6
+#define ARM64_TCR_TG0_SHIFT		14
+#define ARM64_TCR_TG0_MASK		0x3UL
+#define CTXDESC_CD_0_TCR_IRGN0_SHIFT	8
+#define ARM64_TCR_IRGN0_SHIFT		8
+#define ARM64_TCR_IRGN0_MASK		0x3UL
+#define CTXDESC_CD_0_TCR_ORGN0_SHIFT	10
+#define ARM64_TCR_ORGN0_SHIFT		10
+#define ARM64_TCR_ORGN0_MASK		0x3UL
+#define CTXDESC_CD_0_TCR_SH0_SHIFT	12
+#define ARM64_TCR_SH0_SHIFT		12
+#define ARM64_TCR_SH0_MASK		0x3UL
+#define CTXDESC_CD_0_TCR_EPD0_SHIFT	14
+#define ARM64_TCR_EPD0_SHIFT		7
+#define ARM64_TCR_EPD0_MASK		0x1UL
+#define CTXDESC_CD_0_TCR_EPD1_SHIFT	30
+#define ARM64_TCR_EPD1_SHIFT		23
+#define ARM64_TCR_EPD1_MASK		0x1UL
+
+#define CTXDESC_CD_0_ENDI		(1UL << 15)
+#define CTXDESC_CD_0_V			(1UL << 31)
+
+#define CTXDESC_CD_0_TCR_IPS_SHIFT	32
+#define ARM64_TCR_IPS_SHIFT		32
+#define ARM64_TCR_IPS_MASK		0x7UL
+#define CTXDESC_CD_0_TCR_TBI0_SHIFT	38
+#define ARM64_TCR_TBI0_SHIFT		37
+#define ARM64_TCR_TBI0_MASK		0x1UL
+
+#define CTXDESC_CD_0_AA64		(1UL << 41)
+#define CTXDESC_CD_0_R			(1UL << 45)
+#define CTXDESC_CD_0_A			(1UL << 46)
+#define CTXDESC_CD_0_ASET_SHIFT		47
+#define CTXDESC_CD_0_ASET_SHARED	(0UL << CTXDESC_CD_0_ASET_SHIFT)
+#define CTXDESC_CD_0_ASET_PRIVATE	(1UL << CTXDESC_CD_0_ASET_SHIFT)
+#define CTXDESC_CD_0_ASID_SHIFT		48
+#define CTXDESC_CD_0_ASID_MASK		0xffffUL
+
+#define CTXDESC_CD_1_TTB0_SHIFT		4
+#define CTXDESC_CD_1_TTB0_MASK		0xfffffffffffUL
+
+#define CTXDESC_CD_3_MAIR_SHIFT		0
+
+/* Convert between AArch64 (CPU) TCR format and SMMU CD format */
+#define ARM_SMMU_TCR2CD(tcr, fld)					\
+	(((tcr) >> ARM64_TCR_##fld##_SHIFT & ARM64_TCR_##fld##_MASK)	\
+	 << CTXDESC_CD_0_TCR_##fld##_SHIFT)
+
+/* Command queue */
+#define CMDQ_ENT_DWORDS			2
+#define CMDQ_MAX_SZ_SHIFT		8
+
+#define CMDQ_ERR_SHIFT			24
+#define CMDQ_ERR_MASK			0x7f
+#define CMDQ_ERR_CERROR_NONE_IDX	0
+#define CMDQ_ERR_CERROR_ILL_IDX		1
+#define CMDQ_ERR_CERROR_ABT_IDX		2
+
+#define CMDQ_0_OP_SHIFT			0
+#define CMDQ_0_OP_MASK			0xffUL
+#define CMDQ_0_SSV			(1UL << 11)
+
+#define CMDQ_PREFETCH_0_SID_SHIFT	32
+#define CMDQ_PREFETCH_1_SIZE_SHIFT	0
+#define CMDQ_PREFETCH_1_ADDR_MASK	~0xfffUL
+
+#define CMDQ_CFGI_0_SID_SHIFT		32
+#define CMDQ_CFGI_0_SID_MASK		0xffffffffUL
+#define CMDQ_CFGI_1_LEAF		(1UL << 0)
+#define CMDQ_CFGI_1_RANGE_SHIFT		0
+#define CMDQ_CFGI_1_RANGE_MASK		0x1fUL
+
+#define CMDQ_TLBI_0_VMID_SHIFT		32
+#define CMDQ_TLBI_0_ASID_SHIFT		48
+#define CMDQ_TLBI_1_LEAF		(1UL << 0)
+#define CMDQ_TLBI_1_VA_MASK		~0xfffUL
+#define CMDQ_TLBI_1_IPA_MASK		0xfffffffff000UL
+
+#define CMDQ_PRI_0_SSID_SHIFT		12
+#define CMDQ_PRI_0_SSID_MASK		0xfffffUL
+#define CMDQ_PRI_0_SID_SHIFT		32
+#define CMDQ_PRI_0_SID_MASK		0xffffffffUL
+#define CMDQ_PRI_1_GRPID_SHIFT		0
+#define CMDQ_PRI_1_GRPID_MASK		0x1ffUL
+#define CMDQ_PRI_1_RESP_SHIFT		12
+#define CMDQ_PRI_1_RESP_DENY		(0UL << CMDQ_PRI_1_RESP_SHIFT)
+#define CMDQ_PRI_1_RESP_FAIL		(1UL << CMDQ_PRI_1_RESP_SHIFT)
+#define CMDQ_PRI_1_RESP_SUCC		(2UL << CMDQ_PRI_1_RESP_SHIFT)
+
+#define CMDQ_SYNC_0_CS_SHIFT		12
+#define CMDQ_SYNC_0_CS_NONE		(0UL << CMDQ_SYNC_0_CS_SHIFT)
+#define CMDQ_SYNC_0_CS_SEV		(2UL << CMDQ_SYNC_0_CS_SHIFT)
+
+/* Event queue */
+#define EVTQ_ENT_DWORDS			4
+#define EVTQ_MAX_SZ_SHIFT		7
+
+#define EVTQ_0_ID_SHIFT			0
+#define EVTQ_0_ID_MASK			0xffUL
+
+/* PRI queue */
+#define PRIQ_ENT_DWORDS			2
+#define PRIQ_MAX_SZ_SHIFT		8
+
+#define PRIQ_0_SID_SHIFT		0
+#define PRIQ_0_SID_MASK			0xffffffffUL
+#define PRIQ_0_SSID_SHIFT		32
+#define PRIQ_0_SSID_MASK		0xfffffUL
+#define PRIQ_0_PERM_PRIV		(1UL << 58)
+#define PRIQ_0_PERM_EXEC		(1UL << 59)
+#define PRIQ_0_PERM_READ		(1UL << 60)
+#define PRIQ_0_PERM_WRITE		(1UL << 61)
+#define PRIQ_0_PRG_LAST			(1UL << 62)
+#define PRIQ_0_SSID_V			(1UL << 63)
+
+#define PRIQ_1_PRG_IDX_SHIFT		0
+#define PRIQ_1_PRG_IDX_MASK		0x1ffUL
+#define PRIQ_1_ADDR_SHIFT		12
+#define PRIQ_1_ADDR_MASK		0xfffffffffffffUL
+
+/* High-level queue structures */
+#define ARM_SMMU_POLL_TIMEOUT_US	100
+
+#define MSI_IOVA_BASE			0x8000000
+#define MSI_IOVA_LENGTH			0x100000
+
+static bool disable_bypass;
+module_param_named(disable_bypass, disable_bypass, bool, S_IRUGO);
+MODULE_PARM_DESC(disable_bypass,
+	"Disable bypass streams such that incoming transactions from devices that are not attached to an iommu domain will report an abort back to the device and will not be allowed to pass through the SMMU.");
+
+enum pri_resp {
+	PRI_RESP_DENY,
+	PRI_RESP_FAIL,
+	PRI_RESP_SUCC,
+};
+
+enum arm_smmu_msi_index {
+	EVTQ_MSI_INDEX,
+	GERROR_MSI_INDEX,
+	PRIQ_MSI_INDEX,
+	ARM_SMMU_MAX_MSIS,
+};
+
+static phys_addr_t arm_smmu_msi_cfg[ARM_SMMU_MAX_MSIS][3] = {
+	[EVTQ_MSI_INDEX] = {
+		ARM_SMMU_EVTQ_IRQ_CFG0,
+		ARM_SMMU_EVTQ_IRQ_CFG1,
+		ARM_SMMU_EVTQ_IRQ_CFG2,
+	},
+	[GERROR_MSI_INDEX] = {
+		ARM_SMMU_GERROR_IRQ_CFG0,
+		ARM_SMMU_GERROR_IRQ_CFG1,
+		ARM_SMMU_GERROR_IRQ_CFG2,
+	},
+	[PRIQ_MSI_INDEX] = {
+		ARM_SMMU_PRIQ_IRQ_CFG0,
+		ARM_SMMU_PRIQ_IRQ_CFG1,
+		ARM_SMMU_PRIQ_IRQ_CFG2,
+	},
+};
+
+struct arm_smmu_cmdq_ent {
+	/* Common fields */
+	u8				opcode;
+	bool				substream_valid;
+
+	/* Command-specific fields */
+	union {
+		#define CMDQ_OP_PREFETCH_CFG	0x1
+		struct {
+			u32			sid;
+			u8			size;
+			u64			addr;
+		} prefetch;
+
+		#define CMDQ_OP_CFGI_STE	0x3
+		#define CMDQ_OP_CFGI_ALL	0x4
+		struct {
+			u32			sid;
+			union {
+				bool		leaf;
+				u8		span;
+			};
+		} cfgi;
+
+		#define CMDQ_OP_TLBI_NH_ASID	0x11
+		#define CMDQ_OP_TLBI_NH_VA	0x12
+		#define CMDQ_OP_TLBI_EL2_ALL	0x20
+		#define CMDQ_OP_TLBI_S12_VMALL	0x28
+		#define CMDQ_OP_TLBI_S2_IPA	0x2a
+		#define CMDQ_OP_TLBI_NSNH_ALL	0x30
+		struct {
+			u16			asid;
+			u16			vmid;
+			bool			leaf;
+			u64			addr;
+		} tlbi;
+
+		#define CMDQ_OP_PRI_RESP	0x41
+		struct {
+			u32			sid;
+			u32			ssid;
+			u16			grpid;
+			enum pri_resp		resp;
+		} pri;
+
+		#define CMDQ_OP_CMD_SYNC	0x46
+	};
+};
+
+struct arm_smmu_queue {
+	int				irq; /* Wired interrupt */
+
+	__le64				*base;
+	dma_addr_t			base_dma;
+	u64				q_base;
+
+	size_t				ent_dwords;
+	u32				max_n_shift;
+	u32				prod;
+	u32				cons;
+
+	u32 __iomem			*prod_reg;
+	u32 __iomem			*cons_reg;
+};
+
+struct arm_smmu_cmdq {
+	struct arm_smmu_queue		q;
+	spinlock_t			lock;
+};
+
+struct arm_smmu_evtq {
+	struct arm_smmu_queue		q;
+	u32				max_stalls;
+};
+
+struct arm_smmu_priq {
+	struct arm_smmu_queue		q;
+};
+
+/* High-level stream table and context descriptor structures */
+struct arm_smmu_strtab_l1_desc {
+	u8				span;
+
+	__le64				*l2ptr;
+	dma_addr_t			l2ptr_dma;
+};
+
+struct arm_smmu_s1_cfg {
+	__le64				*cdptr;
+	dma_addr_t			cdptr_dma;
+
+	struct arm_smmu_ctx_desc {
+		u16	asid;
+		u64	ttbr;
+		u64	tcr;
+		u64	mair;
+	}				cd;
+};
+
+struct arm_smmu_s2_cfg {
+	u16				vmid;
+	u64				vttbr;
+	u64				vtcr;
+};
+
+struct arm_smmu_strtab_ent {
+	/*
+	 * An STE is "assigned" if the master emitting the corresponding SID
+	 * is attached to a domain. The behaviour of an unassigned STE is
+	 * determined by the disable_bypass parameter, whereas an assigned
+	 * STE behaves according to s1_cfg/s2_cfg, which themselves are
+	 * configured according to the domain type.
+	 */
+	bool				assigned;
+	struct arm_smmu_s1_cfg		*s1_cfg;
+	struct arm_smmu_s2_cfg		*s2_cfg;
+};
+
+struct arm_smmu_strtab_cfg {
+	__le64				*strtab;
+	dma_addr_t			strtab_dma;
+	struct arm_smmu_strtab_l1_desc	*l1_desc;
+	unsigned int			num_l1_ents;
+
+	u64				strtab_base;
+	u32				strtab_base_cfg;
+};
+
+/* An SMMUv3 instance */
+struct arm_smmu_device {
+	struct device			*dev;
+	void __iomem			*base;
+
+#define ARM_SMMU_FEAT_2_LVL_STRTAB	(1 << 0)
+#define ARM_SMMU_FEAT_2_LVL_CDTAB	(1 << 1)
+#define ARM_SMMU_FEAT_TT_LE		(1 << 2)
+#define ARM_SMMU_FEAT_TT_BE		(1 << 3)
+#define ARM_SMMU_FEAT_PRI		(1 << 4)
+#define ARM_SMMU_FEAT_ATS		(1 << 5)
+#define ARM_SMMU_FEAT_SEV		(1 << 6)
+#define ARM_SMMU_FEAT_MSI		(1 << 7)
+#define ARM_SMMU_FEAT_COHERENCY		(1 << 8)
+#define ARM_SMMU_FEAT_TRANS_S1		(1 << 9)
+#define ARM_SMMU_FEAT_TRANS_S2		(1 << 10)
+#define ARM_SMMU_FEAT_STALLS		(1 << 11)
+#define ARM_SMMU_FEAT_HYP		(1 << 12)
+	u32				features;
+
+#define ARM_SMMU_OPT_SKIP_PREFETCH	(1 << 0)
+	u32				options;
+
+	struct arm_smmu_cmdq		cmdq;
+	struct arm_smmu_evtq		evtq;
+	struct arm_smmu_priq		priq;
+
+	int				gerr_irq;
+
+	unsigned long			ias; /* IPA */
+	unsigned long			oas; /* PA */
+	unsigned long			pgsize_bitmap;
+
+#define ARM_SMMU_MAX_ASIDS		(1 << 16)
+	unsigned int			asid_bits;
+	DECLARE_BITMAP(asid_map, ARM_SMMU_MAX_ASIDS);
+
+#define ARM_SMMU_MAX_VMIDS		(1 << 16)
+	unsigned int			vmid_bits;
+	DECLARE_BITMAP(vmid_map, ARM_SMMU_MAX_VMIDS);
+
+	unsigned int			ssid_bits;
+	unsigned int			sid_bits;
+
+	struct arm_smmu_strtab_cfg	strtab_cfg;
+
+	/* IOMMU core code handle */
+	struct iommu_device		iommu;
+};
+
+/* SMMU private data for each master */
+struct arm_smmu_master_data {
+	struct arm_smmu_device		*smmu;
+	struct arm_smmu_strtab_ent	ste;
+};
+
+/* SMMU private data for an IOMMU domain */
+enum arm_smmu_domain_stage {
+	ARM_SMMU_DOMAIN_S1 = 0,
+	ARM_SMMU_DOMAIN_S2,
+	ARM_SMMU_DOMAIN_NESTED,
+	ARM_SMMU_DOMAIN_BYPASS,
+};
+
+struct arm_smmu_domain {
+	struct arm_smmu_device		*smmu;
+	struct mutex			init_mutex; /* Protects smmu pointer */
+
+	struct io_pgtable_ops		*pgtbl_ops;
+	spinlock_t			pgtbl_lock;
+
+	enum arm_smmu_domain_stage	stage;
+	union {
+		struct arm_smmu_s1_cfg	s1_cfg;
+		struct arm_smmu_s2_cfg	s2_cfg;
+	};
+
+	struct iommu_domain		domain;
+};
+
+struct arm_smmu_option_prop {
+	u32 opt;
+	const char *prop;
+};
+
+static struct arm_smmu_option_prop arm_smmu_options[] = {
+	{ ARM_SMMU_OPT_SKIP_PREFETCH, "hisilicon,broken-prefetch-cmd" },
+	{ 0, NULL},
+};
+
+static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom)
+{
+	return container_of(dom, struct arm_smmu_domain, domain);
+}
+
+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);
+}
+
+/* Low-level queue manipulation functions */
+static bool queue_full(struct arm_smmu_queue *q)
+{
+	return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) &&
+	       Q_WRP(q, q->prod) != Q_WRP(q, q->cons);
+}
+
+static bool queue_empty(struct arm_smmu_queue *q)
+{
+	return Q_IDX(q, q->prod) == Q_IDX(q, q->cons) &&
+	       Q_WRP(q, q->prod) == Q_WRP(q, q->cons);
+}
+
+static void queue_sync_cons(struct arm_smmu_queue *q)
+{
+	q->cons = readl_relaxed(q->cons_reg);
+}
+
+static void queue_inc_cons(struct arm_smmu_queue *q)
+{
+	u32 cons = (Q_WRP(q, q->cons) | Q_IDX(q, q->cons)) + 1;
+
+	q->cons = Q_OVF(q, q->cons) | Q_WRP(q, cons) | Q_IDX(q, cons);
+	writel(q->cons, q->cons_reg);
+}
+
+static int queue_sync_prod(struct arm_smmu_queue *q)
+{
+	int ret = 0;
+	u32 prod = readl_relaxed(q->prod_reg);
+
+	if (Q_OVF(q, prod) != Q_OVF(q, q->prod))
+		ret = -EOVERFLOW;
+
+	q->prod = prod;
+	return ret;
+}
+
+static void queue_inc_prod(struct arm_smmu_queue *q)
+{
+	u32 prod = (Q_WRP(q, q->prod) | Q_IDX(q, q->prod)) + 1;
+
+	q->prod = Q_OVF(q, q->prod) | Q_WRP(q, prod) | Q_IDX(q, prod);
+	writel(q->prod, q->prod_reg);
+}
+
+/*
+ * Wait for the SMMU to consume items. If drain is true, wait until the queue
+ * is empty. Otherwise, wait until there is at least one free slot.
+ */
+static int queue_poll_cons(struct arm_smmu_queue *q, bool drain, bool wfe)
+{
+	ktime_t timeout = ktime_add_us(ktime_get(), ARM_SMMU_POLL_TIMEOUT_US);
+
+	while (queue_sync_cons(q), (drain ? !queue_empty(q) : queue_full(q))) {
+		if (ktime_compare(ktime_get(), timeout) > 0)
+			return -ETIMEDOUT;
+
+		if (wfe) {
+			wfe();
+		} else {
+			cpu_relax();
+			udelay(1);
+		}
+	}
+
+	return 0;
+}
+
+static void queue_write(__le64 *dst, u64 *src, size_t n_dwords)
+{
+	int i;
+
+	for (i = 0; i < n_dwords; ++i)
+		*dst++ = cpu_to_le64(*src++);
+}
+
+static int queue_insert_raw(struct arm_smmu_queue *q, u64 *ent)
+{
+	if (queue_full(q))
+		return -ENOSPC;
+
+	queue_write(Q_ENT(q, q->prod), ent, q->ent_dwords);
+	queue_inc_prod(q);
+	return 0;
+}
+
+static void queue_read(__le64 *dst, u64 *src, size_t n_dwords)
+{
+	int i;
+
+	for (i = 0; i < n_dwords; ++i)
+		*dst++ = le64_to_cpu(*src++);
+}
+
+static int queue_remove_raw(struct arm_smmu_queue *q, u64 *ent)
+{
+	if (queue_empty(q))
+		return -EAGAIN;
+
+	queue_read(ent, Q_ENT(q, q->cons), q->ent_dwords);
+	queue_inc_cons(q);
+	return 0;
+}
+
+/* High-level queue accessors */
+static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)
+{
+	memset(cmd, 0, CMDQ_ENT_DWORDS << 3);
+	cmd[0] |= (ent->opcode & CMDQ_0_OP_MASK) << CMDQ_0_OP_SHIFT;
+
+	switch (ent->opcode) {
+	case CMDQ_OP_TLBI_EL2_ALL:
+	case CMDQ_OP_TLBI_NSNH_ALL:
+		break;
+	case CMDQ_OP_PREFETCH_CFG:
+		cmd[0] |= (u64)ent->prefetch.sid << CMDQ_PREFETCH_0_SID_SHIFT;
+		cmd[1] |= ent->prefetch.size << CMDQ_PREFETCH_1_SIZE_SHIFT;
+		cmd[1] |= ent->prefetch.addr & CMDQ_PREFETCH_1_ADDR_MASK;
+		break;
+	case CMDQ_OP_CFGI_STE:
+		cmd[0] |= (u64)ent->cfgi.sid << CMDQ_CFGI_0_SID_SHIFT;
+		cmd[1] |= ent->cfgi.leaf ? CMDQ_CFGI_1_LEAF : 0;
+		break;
+	case CMDQ_OP_CFGI_ALL:
+		/* Cover the entire SID range */
+		cmd[1] |= CMDQ_CFGI_1_RANGE_MASK << CMDQ_CFGI_1_RANGE_SHIFT;
+		break;
+	case CMDQ_OP_TLBI_NH_VA:
+		cmd[0] |= (u64)ent->tlbi.asid << CMDQ_TLBI_0_ASID_SHIFT;
+		cmd[1] |= ent->tlbi.leaf ? CMDQ_TLBI_1_LEAF : 0;
+		cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_VA_MASK;
+		break;
+	case CMDQ_OP_TLBI_S2_IPA:
+		cmd[0] |= (u64)ent->tlbi.vmid << CMDQ_TLBI_0_VMID_SHIFT;
+		cmd[1] |= ent->tlbi.leaf ? CMDQ_TLBI_1_LEAF : 0;
+		cmd[1] |= ent->tlbi.addr & CMDQ_TLBI_1_IPA_MASK;
+		break;
+	case CMDQ_OP_TLBI_NH_ASID:
+		cmd[0] |= (u64)ent->tlbi.asid << CMDQ_TLBI_0_ASID_SHIFT;
+		/* Fallthrough */
+	case CMDQ_OP_TLBI_S12_VMALL:
+		cmd[0] |= (u64)ent->tlbi.vmid << CMDQ_TLBI_0_VMID_SHIFT;
+		break;
+	case CMDQ_OP_PRI_RESP:
+		cmd[0] |= ent->substream_valid ? CMDQ_0_SSV : 0;
+		cmd[0] |= ent->pri.ssid << CMDQ_PRI_0_SSID_SHIFT;
+		cmd[0] |= (u64)ent->pri.sid << CMDQ_PRI_0_SID_SHIFT;
+		cmd[1] |= ent->pri.grpid << CMDQ_PRI_1_GRPID_SHIFT;
+		switch (ent->pri.resp) {
+		case PRI_RESP_DENY:
+			cmd[1] |= CMDQ_PRI_1_RESP_DENY;
+			break;
+		case PRI_RESP_FAIL:
+			cmd[1] |= CMDQ_PRI_1_RESP_FAIL;
+			break;
+		case PRI_RESP_SUCC:
+			cmd[1] |= CMDQ_PRI_1_RESP_SUCC;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case CMDQ_OP_CMD_SYNC:
+		cmd[0] |= CMDQ_SYNC_0_CS_SEV;
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+static void arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu)
+{
+	static const char *cerror_str[] = {
+		[CMDQ_ERR_CERROR_NONE_IDX]	= "No error",
+		[CMDQ_ERR_CERROR_ILL_IDX]	= "Illegal command",
+		[CMDQ_ERR_CERROR_ABT_IDX]	= "Abort on command fetch",
+	};
+
+	int i;
+	u64 cmd[CMDQ_ENT_DWORDS];
+	struct arm_smmu_queue *q = &smmu->cmdq.q;
+	u32 cons = readl_relaxed(q->cons_reg);
+	u32 idx = cons >> CMDQ_ERR_SHIFT & CMDQ_ERR_MASK;
+	struct arm_smmu_cmdq_ent cmd_sync = {
+		.opcode = CMDQ_OP_CMD_SYNC,
+	};
+
+	dev_err(smmu->dev, "CMDQ error (cons 0x%08x): %s\n", cons,
+		idx < ARRAY_SIZE(cerror_str) ?  cerror_str[idx] : "Unknown");
+
+	switch (idx) {
+	case CMDQ_ERR_CERROR_ABT_IDX:
+		dev_err(smmu->dev, "retrying command fetch\n");
+	case CMDQ_ERR_CERROR_NONE_IDX:
+		return;
+	case CMDQ_ERR_CERROR_ILL_IDX:
+		/* Fallthrough */
+	default:
+		break;
+	}
+
+	/*
+	 * We may have concurrent producers, so we need to be careful
+	 * not to touch any of the shadow cmdq state.
+	 */
+	queue_read(cmd, Q_ENT(q, cons), q->ent_dwords);
+	dev_err(smmu->dev, "skipping command in error state:\n");
+	for (i = 0; i < ARRAY_SIZE(cmd); ++i)
+		dev_err(smmu->dev, "\t0x%016llx\n", (unsigned long long)cmd[i]);
+
+	/* Convert the erroneous command into a CMD_SYNC */
+	if (arm_smmu_cmdq_build_cmd(cmd, &cmd_sync)) {
+		dev_err(smmu->dev, "failed to convert to CMD_SYNC\n");
+		return;
+	}
+
+	queue_write(Q_ENT(q, cons), cmd, q->ent_dwords);
+}
+
+static void arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
+				    struct arm_smmu_cmdq_ent *ent)
+{
+	u64 cmd[CMDQ_ENT_DWORDS];
+	unsigned long flags;
+	bool wfe = !!(smmu->features & ARM_SMMU_FEAT_SEV);
+	struct arm_smmu_queue *q = &smmu->cmdq.q;
+
+	if (arm_smmu_cmdq_build_cmd(cmd, ent)) {
+		dev_warn(smmu->dev, "ignoring unknown CMDQ opcode 0x%x\n",
+			 ent->opcode);
+		return;
+	}
+
+	spin_lock_irqsave(&smmu->cmdq.lock, flags);
+	while (queue_insert_raw(q, cmd) == -ENOSPC) {
+		if (queue_poll_cons(q, false, wfe))
+			dev_err_ratelimited(smmu->dev, "CMDQ timeout\n");
+	}
+
+	if (ent->opcode == CMDQ_OP_CMD_SYNC && queue_poll_cons(q, true, wfe))
+		dev_err_ratelimited(smmu->dev, "CMD_SYNC timeout\n");
+	spin_unlock_irqrestore(&smmu->cmdq.lock, flags);
+}
+
+/* Context descriptor manipulation functions */
+static u64 arm_smmu_cpu_tcr_to_cd(u64 tcr)
+{
+	u64 val = 0;
+
+	/* Repack the TCR. Just care about TTBR0 for now */
+	val |= ARM_SMMU_TCR2CD(tcr, T0SZ);
+	val |= ARM_SMMU_TCR2CD(tcr, TG0);
+	val |= ARM_SMMU_TCR2CD(tcr, IRGN0);
+	val |= ARM_SMMU_TCR2CD(tcr, ORGN0);
+	val |= ARM_SMMU_TCR2CD(tcr, SH0);
+	val |= ARM_SMMU_TCR2CD(tcr, EPD0);
+	val |= ARM_SMMU_TCR2CD(tcr, EPD1);
+	val |= ARM_SMMU_TCR2CD(tcr, IPS);
+	val |= ARM_SMMU_TCR2CD(tcr, TBI0);
+
+	return val;
+}
+
+static void arm_smmu_write_ctx_desc(struct arm_smmu_device *smmu,
+				    struct arm_smmu_s1_cfg *cfg)
+{
+	u64 val;
+
+	/*
+	 * We don't need to issue any invalidation here, as we'll invalidate
+	 * the STE when installing the new entry anyway.
+	 */
+	val = arm_smmu_cpu_tcr_to_cd(cfg->cd.tcr) |
+#ifdef __BIG_ENDIAN
+	      CTXDESC_CD_0_ENDI |
+#endif
+	      CTXDESC_CD_0_R | CTXDESC_CD_0_A | CTXDESC_CD_0_ASET_PRIVATE |
+	      CTXDESC_CD_0_AA64 | (u64)cfg->cd.asid << CTXDESC_CD_0_ASID_SHIFT |
+	      CTXDESC_CD_0_V;
+	cfg->cdptr[0] = cpu_to_le64(val);
+
+	val = cfg->cd.ttbr & CTXDESC_CD_1_TTB0_MASK << CTXDESC_CD_1_TTB0_SHIFT;
+	cfg->cdptr[1] = cpu_to_le64(val);
+
+	cfg->cdptr[3] = cpu_to_le64(cfg->cd.mair << CTXDESC_CD_3_MAIR_SHIFT);
+}
+
+/* Stream table manipulation functions */
+static void
+arm_smmu_write_strtab_l1_desc(__le64 *dst, struct arm_smmu_strtab_l1_desc *desc)
+{
+	u64 val = 0;
+
+	val |= (desc->span & STRTAB_L1_DESC_SPAN_MASK)
+		<< STRTAB_L1_DESC_SPAN_SHIFT;
+	val |= desc->l2ptr_dma &
+	       STRTAB_L1_DESC_L2PTR_MASK << STRTAB_L1_DESC_L2PTR_SHIFT;
+
+	*dst = cpu_to_le64(val);
+}
+
+static void arm_smmu_sync_ste_for_sid(struct arm_smmu_device *smmu, u32 sid)
+{
+	struct arm_smmu_cmdq_ent cmd = {
+		.opcode	= CMDQ_OP_CFGI_STE,
+		.cfgi	= {
+			.sid	= sid,
+			.leaf	= true,
+		},
+	};
+
+	arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+	cmd.opcode = CMDQ_OP_CMD_SYNC;
+	arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+}
+
+static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
+				      __le64 *dst, struct arm_smmu_strtab_ent *ste)
+{
+	/*
+	 * This is hideously complicated, but we only really care about
+	 * three cases at the moment:
+	 *
+	 * 1. Invalid (all zero) -> bypass/fault (init)
+	 * 2. Bypass/fault -> translation/bypass (attach)
+	 * 3. Translation/bypass -> bypass/fault (detach)
+	 *
+	 * Given that we can't update the STE atomically and the SMMU
+	 * doesn't read the thing in a defined order, that leaves us
+	 * with the following maintenance requirements:
+	 *
+	 * 1. Update Config, return (init time STEs aren't live)
+	 * 2. Write everything apart from dword 0, sync, write dword 0, sync
+	 * 3. Update Config, sync
+	 */
+	u64 val = le64_to_cpu(dst[0]);
+	bool ste_live = false;
+	struct arm_smmu_cmdq_ent prefetch_cmd = {
+		.opcode		= CMDQ_OP_PREFETCH_CFG,
+		.prefetch	= {
+			.sid	= sid,
+		},
+	};
+
+	if (val & STRTAB_STE_0_V) {
+		u64 cfg;
+
+		cfg = val & STRTAB_STE_0_CFG_MASK << STRTAB_STE_0_CFG_SHIFT;
+		switch (cfg) {
+		case STRTAB_STE_0_CFG_BYPASS:
+			break;
+		case STRTAB_STE_0_CFG_S1_TRANS:
+		case STRTAB_STE_0_CFG_S2_TRANS:
+			ste_live = true;
+			break;
+		case STRTAB_STE_0_CFG_ABORT:
+			if (disable_bypass)
+				break;
+		default:
+			BUG(); /* STE corruption */
+		}
+	}
+
+	/* Nuke the existing STE_0 value, as we're going to rewrite it */
+	val = STRTAB_STE_0_V;
+
+	/* Bypass/fault */
+	if (!ste->assigned || !(ste->s1_cfg || ste->s2_cfg)) {
+		if (!ste->assigned && disable_bypass)
+			val |= STRTAB_STE_0_CFG_ABORT;
+		else
+			val |= STRTAB_STE_0_CFG_BYPASS;
+
+		dst[0] = cpu_to_le64(val);
+		dst[1] = cpu_to_le64(STRTAB_STE_1_SHCFG_INCOMING
+			 << STRTAB_STE_1_SHCFG_SHIFT);
+		dst[2] = 0; /* Nuke the VMID */
+		if (ste_live)
+			arm_smmu_sync_ste_for_sid(smmu, sid);
+		return;
+	}
+
+	if (ste->s1_cfg) {
+		BUG_ON(ste_live);
+		dst[1] = cpu_to_le64(
+			 STRTAB_STE_1_S1C_CACHE_WBRA
+			 << STRTAB_STE_1_S1CIR_SHIFT |
+			 STRTAB_STE_1_S1C_CACHE_WBRA
+			 << STRTAB_STE_1_S1COR_SHIFT |
+			 STRTAB_STE_1_S1C_SH_ISH << STRTAB_STE_1_S1CSH_SHIFT |
+#ifdef CONFIG_PCI_ATS
+			 STRTAB_STE_1_EATS_TRANS << STRTAB_STE_1_EATS_SHIFT |
+#endif
+			 STRTAB_STE_1_STRW_NSEL1 << STRTAB_STE_1_STRW_SHIFT);
+
+		if (smmu->features & ARM_SMMU_FEAT_STALLS)
+			dst[1] |= cpu_to_le64(STRTAB_STE_1_S1STALLD);
+
+		val |= (ste->s1_cfg->cdptr_dma & STRTAB_STE_0_S1CTXPTR_MASK
+		        << STRTAB_STE_0_S1CTXPTR_SHIFT) |
+			STRTAB_STE_0_CFG_S1_TRANS;
+	}
+
+	if (ste->s2_cfg) {
+		BUG_ON(ste_live);
+		dst[2] = cpu_to_le64(
+			 ste->s2_cfg->vmid << STRTAB_STE_2_S2VMID_SHIFT |
+			 (ste->s2_cfg->vtcr & STRTAB_STE_2_VTCR_MASK)
+			  << STRTAB_STE_2_VTCR_SHIFT |
+#ifdef __BIG_ENDIAN
+			 STRTAB_STE_2_S2ENDI |
+#endif
+			 STRTAB_STE_2_S2PTW | STRTAB_STE_2_S2AA64 |
+			 STRTAB_STE_2_S2R);
+
+		dst[3] = cpu_to_le64(ste->s2_cfg->vttbr &
+			 STRTAB_STE_3_S2TTB_MASK << STRTAB_STE_3_S2TTB_SHIFT);
+
+		val |= STRTAB_STE_0_CFG_S2_TRANS;
+	}
+
+	arm_smmu_sync_ste_for_sid(smmu, sid);
+	dst[0] = cpu_to_le64(val);
+	arm_smmu_sync_ste_for_sid(smmu, sid);
+
+	/* It's likely that we'll want to use the new STE soon */
+	if (!(smmu->options & ARM_SMMU_OPT_SKIP_PREFETCH))
+		arm_smmu_cmdq_issue_cmd(smmu, &prefetch_cmd);
+}
+
+static void arm_smmu_init_bypass_stes(u64 *strtab, unsigned int nent)
+{
+	unsigned int i;
+	struct arm_smmu_strtab_ent ste = { .assigned = false };
+
+	for (i = 0; i < nent; ++i) {
+		arm_smmu_write_strtab_ent(NULL, -1, strtab, &ste);
+		strtab += STRTAB_STE_DWORDS;
+	}
+}
+
+static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid)
+{
+	size_t size;
+	void *strtab;
+	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+	struct arm_smmu_strtab_l1_desc *desc = &cfg->l1_desc[sid >> STRTAB_SPLIT];
+
+	if (desc->l2ptr)
+		return 0;
+
+	size = 1 << (STRTAB_SPLIT + ilog2(STRTAB_STE_DWORDS) + 3);
+	strtab = &cfg->strtab[(sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS];
+
+	desc->span = STRTAB_SPLIT + 1;
+	desc->l2ptr = dmam_alloc_coherent(smmu->dev, size, &desc->l2ptr_dma,
+					  GFP_KERNEL | __GFP_ZERO);
+	if (!desc->l2ptr) {
+		dev_err(smmu->dev,
+			"failed to allocate l2 stream table for SID %u\n",
+			sid);
+		return -ENOMEM;
+	}
+
+	arm_smmu_init_bypass_stes(desc->l2ptr, 1 << STRTAB_SPLIT);
+	arm_smmu_write_strtab_l1_desc(strtab, desc);
+	return 0;
+}
+
+/* IRQ and event handlers */
+static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
+{
+	int i;
+	struct arm_smmu_device *smmu = dev;
+	struct arm_smmu_queue *q = &smmu->evtq.q;
+	u64 evt[EVTQ_ENT_DWORDS];
+
+	do {
+		while (!queue_remove_raw(q, evt)) {
+			u8 id = evt[0] >> EVTQ_0_ID_SHIFT & EVTQ_0_ID_MASK;
+
+			dev_info(smmu->dev, "event 0x%02x received:\n", id);
+			for (i = 0; i < ARRAY_SIZE(evt); ++i)
+				dev_info(smmu->dev, "\t0x%016llx\n",
+					 (unsigned long long)evt[i]);
+
+		}
+
+		/*
+		 * Not much we can do on overflow, so scream and pretend we're
+		 * trying harder.
+		 */
+		if (queue_sync_prod(q) == -EOVERFLOW)
+			dev_err(smmu->dev, "EVTQ overflow detected -- events lost\n");
+	} while (!queue_empty(q));
+
+	/* Sync our overflow flag, as we believe we're up to speed */
+	q->cons = Q_OVF(q, q->prod) | Q_WRP(q, q->cons) | Q_IDX(q, q->cons);
+	return IRQ_HANDLED;
+}
+
+static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)
+{
+	u32 sid, ssid;
+	u16 grpid;
+	bool ssv, last;
+
+	sid = evt[0] >> PRIQ_0_SID_SHIFT & PRIQ_0_SID_MASK;
+	ssv = evt[0] & PRIQ_0_SSID_V;
+	ssid = ssv ? evt[0] >> PRIQ_0_SSID_SHIFT & PRIQ_0_SSID_MASK : 0;
+	last = evt[0] & PRIQ_0_PRG_LAST;
+	grpid = evt[1] >> PRIQ_1_PRG_IDX_SHIFT & PRIQ_1_PRG_IDX_MASK;
+
+	dev_info(smmu->dev, "unexpected PRI request received:\n");
+	dev_info(smmu->dev,
+		 "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016llx\n",
+		 sid, ssid, grpid, last ? "L" : "",
+		 evt[0] & PRIQ_0_PERM_PRIV ? "" : "un",
+		 evt[0] & PRIQ_0_PERM_READ ? "R" : "",
+		 evt[0] & PRIQ_0_PERM_WRITE ? "W" : "",
+		 evt[0] & PRIQ_0_PERM_EXEC ? "X" : "",
+		 evt[1] & PRIQ_1_ADDR_MASK << PRIQ_1_ADDR_SHIFT);
+
+	if (last) {
+		struct arm_smmu_cmdq_ent cmd = {
+			.opcode			= CMDQ_OP_PRI_RESP,
+			.substream_valid	= ssv,
+			.pri			= {
+				.sid	= sid,
+				.ssid	= ssid,
+				.grpid	= grpid,
+				.resp	= PRI_RESP_DENY,
+			},
+		};
+
+		arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+	}
+}
+
+static irqreturn_t arm_smmu_priq_thread(int irq, void *dev)
+{
+	struct arm_smmu_device *smmu = dev;
+	struct arm_smmu_queue *q = &smmu->priq.q;
+	u64 evt[PRIQ_ENT_DWORDS];
+
+	do {
+		while (!queue_remove_raw(q, evt))
+			arm_smmu_handle_ppr(smmu, evt);
+
+		if (queue_sync_prod(q) == -EOVERFLOW)
+			dev_err(smmu->dev, "PRIQ overflow detected -- requests lost\n");
+	} while (!queue_empty(q));
+
+	/* Sync our overflow flag, as we believe we're up to speed */
+	q->cons = Q_OVF(q, q->prod) | Q_WRP(q, q->cons) | Q_IDX(q, q->cons);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t arm_smmu_cmdq_sync_handler(int irq, void *dev)
+{
+	/* We don't actually use CMD_SYNC interrupts for anything */
+	return IRQ_HANDLED;
+}
+
+static int arm_smmu_device_disable(struct arm_smmu_device *smmu);
+
+static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
+{
+	u32 gerror, gerrorn, active;
+	struct arm_smmu_device *smmu = dev;
+
+	gerror = readl_relaxed(smmu->base + ARM_SMMU_GERROR);
+	gerrorn = readl_relaxed(smmu->base + ARM_SMMU_GERRORN);
+
+	active = gerror ^ gerrorn;
+	if (!(active & GERROR_ERR_MASK))
+		return IRQ_NONE; /* No errors pending */
+
+	dev_warn(smmu->dev,
+		 "unexpected global error reported (0x%08x), this could be serious\n",
+		 active);
+
+	if (active & GERROR_SFM_ERR) {
+		dev_err(smmu->dev, "device has entered Service Failure Mode!\n");
+		arm_smmu_device_disable(smmu);
+	}
+
+	if (active & GERROR_MSI_GERROR_ABT_ERR)
+		dev_warn(smmu->dev, "GERROR MSI write aborted\n");
+
+	if (active & GERROR_MSI_PRIQ_ABT_ERR)
+		dev_warn(smmu->dev, "PRIQ MSI write aborted\n");
+
+	if (active & GERROR_MSI_EVTQ_ABT_ERR)
+		dev_warn(smmu->dev, "EVTQ MSI write aborted\n");
+
+	if (active & GERROR_MSI_CMDQ_ABT_ERR) {
+		dev_warn(smmu->dev, "CMDQ MSI write aborted\n");
+		arm_smmu_cmdq_sync_handler(irq, smmu->dev);
+	}
+
+	if (active & GERROR_PRIQ_ABT_ERR)
+		dev_err(smmu->dev, "PRIQ write aborted -- events may have been lost\n");
+
+	if (active & GERROR_EVTQ_ABT_ERR)
+		dev_err(smmu->dev, "EVTQ write aborted -- events may have been lost\n");
+
+	if (active & GERROR_CMDQ_ERR)
+		arm_smmu_cmdq_skip_err(smmu);
+
+	writel(gerror, smmu->base + ARM_SMMU_GERRORN);
+	return IRQ_HANDLED;
+}
+
+/* IO_PGTABLE API */
+static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu)
+{
+	struct arm_smmu_cmdq_ent cmd;
+
+	cmd.opcode = CMDQ_OP_CMD_SYNC;
+	arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+}
+
+static void arm_smmu_tlb_sync(void *cookie)
+{
+	struct arm_smmu_domain *smmu_domain = cookie;
+	__arm_smmu_tlb_sync(smmu_domain->smmu);
+}
+
+static void arm_smmu_tlb_inv_context(void *cookie)
+{
+	struct arm_smmu_domain *smmu_domain = cookie;
+	struct arm_smmu_device *smmu = smmu_domain->smmu;
+	struct arm_smmu_cmdq_ent cmd;
+
+	if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+		cmd.opcode	= CMDQ_OP_TLBI_NH_ASID;
+		cmd.tlbi.asid	= smmu_domain->s1_cfg.cd.asid;
+		cmd.tlbi.vmid	= 0;
+	} else {
+		cmd.opcode	= CMDQ_OP_TLBI_S12_VMALL;
+		cmd.tlbi.vmid	= smmu_domain->s2_cfg.vmid;
+	}
+
+	arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+	__arm_smmu_tlb_sync(smmu);
+}
+
+static void arm_smmu_tlb_inv_range_nosync(unsigned long iova, size_t size,
+					  size_t granule, bool leaf, void *cookie)
+{
+	struct arm_smmu_domain *smmu_domain = cookie;
+	struct arm_smmu_device *smmu = smmu_domain->smmu;
+	struct arm_smmu_cmdq_ent cmd = {
+		.tlbi = {
+			.leaf	= leaf,
+			.addr	= iova,
+		},
+	};
+
+	if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+		cmd.opcode	= CMDQ_OP_TLBI_NH_VA;
+		cmd.tlbi.asid	= smmu_domain->s1_cfg.cd.asid;
+	} else {
+		cmd.opcode	= CMDQ_OP_TLBI_S2_IPA;
+		cmd.tlbi.vmid	= smmu_domain->s2_cfg.vmid;
+	}
+
+	do {
+		arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+		cmd.tlbi.addr += granule;
+	} while (size -= granule);
+}
+
+static const struct iommu_gather_ops arm_smmu_gather_ops = {
+	.tlb_flush_all	= arm_smmu_tlb_inv_context,
+	.tlb_add_flush	= arm_smmu_tlb_inv_range_nosync,
+	.tlb_sync	= arm_smmu_tlb_sync,
+};
+
+/* IOMMU API */
+static bool arm_smmu_capable(enum iommu_cap cap)
+{
+	switch (cap) {
+	case IOMMU_CAP_CACHE_COHERENCY:
+		return true;
+	case IOMMU_CAP_NOEXEC:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
+{
+	struct arm_smmu_domain *smmu_domain;
+
+	if (type != IOMMU_DOMAIN_UNMANAGED &&
+	    type != IOMMU_DOMAIN_DMA &&
+	    type != IOMMU_DOMAIN_IDENTITY)
+		return NULL;
+
+	/*
+	 * 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 NULL;
+
+	if (type == IOMMU_DOMAIN_DMA &&
+	    iommu_get_dma_cookie(&smmu_domain->domain)) {
+		kfree(smmu_domain);
+		return NULL;
+	}
+
+	mutex_init(&smmu_domain->init_mutex);
+	spin_lock_init(&smmu_domain->pgtbl_lock);
+	return &smmu_domain->domain;
+}
+
+static int arm_smmu_bitmap_alloc(unsigned long *map, int span)
+{
+	int idx, size = 1 << span;
+
+	do {
+		idx = find_first_zero_bit(map, size);
+		if (idx == size)
+			return -ENOSPC;
+	} while (test_and_set_bit(idx, map));
+
+	return idx;
+}
+
+static void arm_smmu_bitmap_free(unsigned long *map, int idx)
+{
+	clear_bit(idx, map);
+}
+
+static void arm_smmu_domain_free(struct iommu_domain *domain)
+{
+	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+	struct arm_smmu_device *smmu = smmu_domain->smmu;
+
+	iommu_put_dma_cookie(domain);
+	free_io_pgtable_ops(smmu_domain->pgtbl_ops);
+
+	/* Free the CD and ASID, if we allocated them */
+	if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+		struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
+
+		if (cfg->cdptr) {
+			dmam_free_coherent(smmu_domain->smmu->dev,
+					   CTXDESC_CD_DWORDS << 3,
+					   cfg->cdptr,
+					   cfg->cdptr_dma);
+
+			arm_smmu_bitmap_free(smmu->asid_map, cfg->cd.asid);
+		}
+	} else {
+		struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
+		if (cfg->vmid)
+			arm_smmu_bitmap_free(smmu->vmid_map, cfg->vmid);
+	}
+
+	kfree(smmu_domain);
+}
+
+static int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain,
+				       struct io_pgtable_cfg *pgtbl_cfg)
+{
+	int ret;
+	int asid;
+	struct arm_smmu_device *smmu = smmu_domain->smmu;
+	struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
+
+	asid = arm_smmu_bitmap_alloc(smmu->asid_map, smmu->asid_bits);
+	if (asid < 0)
+		return asid;
+
+	cfg->cdptr = dmam_alloc_coherent(smmu->dev, CTXDESC_CD_DWORDS << 3,
+					 &cfg->cdptr_dma,
+					 GFP_KERNEL | __GFP_ZERO);
+	if (!cfg->cdptr) {
+		dev_warn(smmu->dev, "failed to allocate context descriptor\n");
+		ret = -ENOMEM;
+		goto out_free_asid;
+	}
+
+	cfg->cd.asid	= (u16)asid;
+	cfg->cd.ttbr	= pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
+	cfg->cd.tcr	= pgtbl_cfg->arm_lpae_s1_cfg.tcr;
+	cfg->cd.mair	= pgtbl_cfg->arm_lpae_s1_cfg.mair[0];
+	return 0;
+
+out_free_asid:
+	arm_smmu_bitmap_free(smmu->asid_map, asid);
+	return ret;
+}
+
+static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain,
+				       struct io_pgtable_cfg *pgtbl_cfg)
+{
+	int vmid;
+	struct arm_smmu_device *smmu = smmu_domain->smmu;
+	struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
+
+	vmid = arm_smmu_bitmap_alloc(smmu->vmid_map, smmu->vmid_bits);
+	if (vmid < 0)
+		return vmid;
+
+	cfg->vmid	= (u16)vmid;
+	cfg->vttbr	= pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
+	cfg->vtcr	= pgtbl_cfg->arm_lpae_s2_cfg.vtcr;
+	return 0;
+}
+
+static int arm_smmu_domain_finalise(struct iommu_domain *domain)
+{
+	int ret;
+	unsigned long ias, oas;
+	enum io_pgtable_fmt fmt;
+	struct io_pgtable_cfg pgtbl_cfg;
+	struct io_pgtable_ops *pgtbl_ops;
+	int (*finalise_stage_fn)(struct arm_smmu_domain *,
+				 struct io_pgtable_cfg *);
+	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+	struct arm_smmu_device *smmu = smmu_domain->smmu;
+
+	if (domain->type == IOMMU_DOMAIN_IDENTITY) {
+		smmu_domain->stage = ARM_SMMU_DOMAIN_BYPASS;
+		return 0;
+	}
+
+	/* Restrict the stage to what we can actually support */
+	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:
+		ias = VA_BITS;
+		oas = smmu->ias;
+		fmt = ARM_64_LPAE_S1;
+		finalise_stage_fn = arm_smmu_domain_finalise_s1;
+		break;
+	case ARM_SMMU_DOMAIN_NESTED:
+	case ARM_SMMU_DOMAIN_S2:
+		ias = smmu->ias;
+		oas = smmu->oas;
+		fmt = ARM_64_LPAE_S2;
+		finalise_stage_fn = arm_smmu_domain_finalise_s2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	pgtbl_cfg = (struct io_pgtable_cfg) {
+		.pgsize_bitmap	= smmu->pgsize_bitmap,
+		.ias		= ias,
+		.oas		= oas,
+		.tlb		= &arm_smmu_gather_ops,
+		.iommu_dev	= smmu->dev,
+	};
+
+	pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain);
+	if (!pgtbl_ops)
+		return -ENOMEM;
+
+	domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
+	domain->geometry.aperture_end = (1UL << ias) - 1;
+	domain->geometry.force_aperture = true;
+	smmu_domain->pgtbl_ops = pgtbl_ops;
+
+	ret = finalise_stage_fn(smmu_domain, &pgtbl_cfg);
+	if (ret < 0)
+		free_io_pgtable_ops(pgtbl_ops);
+
+	return ret;
+}
+
+static __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
+{
+	__le64 *step;
+	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+
+	if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
+		struct arm_smmu_strtab_l1_desc *l1_desc;
+		int idx;
+
+		/* Two-level walk */
+		idx = (sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS;
+		l1_desc = &cfg->l1_desc[idx];
+		idx = (sid & ((1 << STRTAB_SPLIT) - 1)) * STRTAB_STE_DWORDS;
+		step = &l1_desc->l2ptr[idx];
+	} else {
+		/* Simple linear lookup */
+		step = &cfg->strtab[sid * STRTAB_STE_DWORDS];
+	}
+
+	return step;
+}
+
+static void arm_smmu_install_ste_for_dev(struct iommu_fwspec *fwspec)
+{
+	int i;
+	struct arm_smmu_master_data *master = fwspec->iommu_priv;
+	struct arm_smmu_device *smmu = master->smmu;
+
+	for (i = 0; i < fwspec->num_ids; ++i) {
+		u32 sid = fwspec->ids[i];
+		__le64 *step = arm_smmu_get_step_for_sid(smmu, sid);
+
+		arm_smmu_write_strtab_ent(smmu, sid, step, &master->ste);
+	}
+}
+
+static void arm_smmu_detach_dev(struct device *dev)
+{
+	struct arm_smmu_master_data *master = dev->iommu_fwspec->iommu_priv;
+
+	master->ste.assigned = false;
+	arm_smmu_install_ste_for_dev(dev->iommu_fwspec);
+}
+
+static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
+{
+	int ret = 0;
+	struct arm_smmu_device *smmu;
+	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+	struct arm_smmu_master_data *master;
+	struct arm_smmu_strtab_ent *ste;
+
+	if (!dev->iommu_fwspec)
+		return -ENOENT;
+
+	master = dev->iommu_fwspec->iommu_priv;
+	smmu = master->smmu;
+	ste = &master->ste;
+
+	/* Already attached to a different domain? */
+	if (ste->assigned)
+		arm_smmu_detach_dev(dev);
+
+	mutex_lock(&smmu_domain->init_mutex);
+
+	if (!smmu_domain->smmu) {
+		smmu_domain->smmu = smmu;
+		ret = arm_smmu_domain_finalise(domain);
+		if (ret) {
+			smmu_domain->smmu = NULL;
+			goto out_unlock;
+		}
+	} else if (smmu_domain->smmu != smmu) {
+		dev_err(dev,
+			"cannot attach to SMMU %s (upstream of %s)\n",
+			dev_name(smmu_domain->smmu->dev),
+			dev_name(smmu->dev));
+		ret = -ENXIO;
+		goto out_unlock;
+	}
+
+	ste->assigned = true;
+
+	if (smmu_domain->stage == ARM_SMMU_DOMAIN_BYPASS) {
+		ste->s1_cfg = NULL;
+		ste->s2_cfg = NULL;
+	} else if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+		ste->s1_cfg = &smmu_domain->s1_cfg;
+		ste->s2_cfg = NULL;
+		arm_smmu_write_ctx_desc(smmu, ste->s1_cfg);
+	} else {
+		ste->s1_cfg = NULL;
+		ste->s2_cfg = &smmu_domain->s2_cfg;
+	}
+
+	arm_smmu_install_ste_for_dev(dev->iommu_fwspec);
+out_unlock:
+	mutex_unlock(&smmu_domain->init_mutex);
+	return ret;
+}
+
+static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
+			phys_addr_t paddr, size_t size, int prot)
+{
+	int ret;
+	unsigned long flags;
+	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+	struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
+
+	if (!ops)
+		return -ENODEV;
+
+	spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags);
+	ret = ops->map(ops, iova, paddr, size, prot);
+	spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags);
+	return ret;
+}
+
+static size_t
+arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size)
+{
+	size_t ret;
+	unsigned long flags;
+	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+	struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
+
+	if (!ops)
+		return 0;
+
+	spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags);
+	ret = ops->unmap(ops, iova, size);
+	spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags);
+	return ret;
+}
+
+static phys_addr_t
+arm_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
+{
+	phys_addr_t ret;
+	unsigned long flags;
+	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+	struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;
+
+	if (domain->type == IOMMU_DOMAIN_IDENTITY)
+		return iova;
+
+	if (!ops)
+		return 0;
+
+	spin_lock_irqsave(&smmu_domain->pgtbl_lock, flags);
+	ret = ops->iova_to_phys(ops, iova);
+	spin_unlock_irqrestore(&smmu_domain->pgtbl_lock, flags);
+
+	return ret;
+}
+
+static struct platform_driver arm_smmu_driver;
+
+static int arm_smmu_match_node(struct device *dev, void *data)
+{
+	return dev->fwnode == data;
+}
+
+static
+struct arm_smmu_device *arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode)
+{
+	struct device *dev = driver_find_device(&arm_smmu_driver.driver, NULL,
+						fwnode, arm_smmu_match_node);
+	put_device(dev);
+	return dev ? dev_get_drvdata(dev) : NULL;
+}
+
+static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid)
+{
+	unsigned long limit = smmu->strtab_cfg.num_l1_ents;
+
+	if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB)
+		limit *= 1UL << STRTAB_SPLIT;
+
+	return sid < limit;
+}
+
+static struct iommu_ops arm_smmu_ops;
+
+static int arm_smmu_add_device(struct device *dev)
+{
+	int i, ret;
+	struct arm_smmu_device *smmu;
+	struct arm_smmu_master_data *master;
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+	struct iommu_group *group;
+
+	if (!fwspec || fwspec->ops != &arm_smmu_ops)
+		return -ENODEV;
+	/*
+	 * We _can_ actually withstand dodgy bus code re-calling add_device()
+	 * without an intervening remove_device()/of_xlate() sequence, but
+	 * we're not going to do so quietly...
+	 */
+	if (WARN_ON_ONCE(fwspec->iommu_priv)) {
+		master = fwspec->iommu_priv;
+		smmu = master->smmu;
+	} else {
+		smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode);
+		if (!smmu)
+			return -ENODEV;
+		master = kzalloc(sizeof(*master), GFP_KERNEL);
+		if (!master)
+			return -ENOMEM;
+
+		master->smmu = smmu;
+		fwspec->iommu_priv = master;
+	}
+
+	/* Check the SIDs are in range of the SMMU and our stream table */
+	for (i = 0; i < fwspec->num_ids; i++) {
+		u32 sid = fwspec->ids[i];
+
+		if (!arm_smmu_sid_in_range(smmu, sid))
+			return -ERANGE;
+
+		/* Ensure l2 strtab is initialised */
+		if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
+			ret = arm_smmu_init_l2_strtab(smmu, sid);
+			if (ret)
+				return ret;
+		}
+	}
+
+	group = iommu_group_get_for_dev(dev);
+	if (!IS_ERR(group)) {
+		iommu_group_put(group);
+		iommu_device_link(&smmu->iommu, dev);
+	}
+
+	return PTR_ERR_OR_ZERO(group);
+}
+
+static void arm_smmu_remove_device(struct device *dev)
+{
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+	struct arm_smmu_master_data *master;
+	struct arm_smmu_device *smmu;
+
+	if (!fwspec || fwspec->ops != &arm_smmu_ops)
+		return;
+
+	master = fwspec->iommu_priv;
+	smmu = master->smmu;
+	if (master && master->ste.assigned)
+		arm_smmu_detach_dev(dev);
+	iommu_group_remove_device(dev);
+	iommu_device_unlink(&smmu->iommu, dev);
+	kfree(master);
+	iommu_fwspec_free(dev);
+}
+
+static struct iommu_group *arm_smmu_device_group(struct device *dev)
+{
+	struct iommu_group *group;
+
+	/*
+	 * We don't support devices sharing stream IDs other than PCI RID
+	 * aliases, since the necessary ID-to-device lookup becomes rather
+	 * impractical given a potential sparse 32-bit stream ID space.
+	 */
+	if (dev_is_pci(dev))
+		group = pci_device_group(dev);
+	else
+		group = generic_device_group(dev);
+
+	return group;
+}
+
+static int arm_smmu_domain_get_attr(struct iommu_domain *domain,
+				    enum iommu_attr attr, void *data)
+{
+	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+
+	if (domain->type != IOMMU_DOMAIN_UNMANAGED)
+		return -EINVAL;
+
+	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)
+{
+	int ret = 0;
+	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+
+	if (domain->type != IOMMU_DOMAIN_UNMANAGED)
+		return -EINVAL;
+
+	mutex_lock(&smmu_domain->init_mutex);
+
+	switch (attr) {
+	case DOMAIN_ATTR_NESTING:
+		if (smmu_domain->smmu) {
+			ret = -EPERM;
+			goto out_unlock;
+		}
+
+		if (*(int *)data)
+			smmu_domain->stage = ARM_SMMU_DOMAIN_NESTED;
+		else
+			smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
+
+		break;
+	default:
+		ret = -ENODEV;
+	}
+
+out_unlock:
+	mutex_unlock(&smmu_domain->init_mutex);
+	return ret;
+}
+
+static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
+{
+	return iommu_fwspec_add_ids(dev, args->args, 1);
+}
+
+static void arm_smmu_get_resv_regions(struct device *dev,
+				      struct list_head *head)
+{
+	struct iommu_resv_region *region;
+	int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
+
+	region = iommu_alloc_resv_region(MSI_IOVA_BASE, MSI_IOVA_LENGTH,
+					 prot, IOMMU_RESV_SW_MSI);
+	if (!region)
+		return;
+
+	list_add_tail(&region->list, head);
+
+	iommu_dma_get_resv_regions(dev, head);
+}
+
+static void arm_smmu_put_resv_regions(struct device *dev,
+				      struct list_head *head)
+{
+	struct iommu_resv_region *entry, *next;
+
+	list_for_each_entry_safe(entry, next, head, list)
+		kfree(entry);
+}
+
+static struct iommu_ops arm_smmu_ops = {
+	.capable		= arm_smmu_capable,
+	.domain_alloc		= arm_smmu_domain_alloc,
+	.domain_free		= arm_smmu_domain_free,
+	.attach_dev		= arm_smmu_attach_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,
+	.device_group		= arm_smmu_device_group,
+	.domain_get_attr	= arm_smmu_domain_get_attr,
+	.domain_set_attr	= arm_smmu_domain_set_attr,
+	.of_xlate		= arm_smmu_of_xlate,
+	.get_resv_regions	= arm_smmu_get_resv_regions,
+	.put_resv_regions	= arm_smmu_put_resv_regions,
+	.pgsize_bitmap		= -1UL, /* Restricted during device attach */
+};
+
+/* Probing and initialisation functions */
+static int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
+				   struct arm_smmu_queue *q,
+				   unsigned long prod_off,
+				   unsigned long cons_off,
+				   size_t dwords)
+{
+	size_t qsz = ((1 << q->max_n_shift) * dwords) << 3;
+
+	q->base = dmam_alloc_coherent(smmu->dev, qsz, &q->base_dma, GFP_KERNEL);
+	if (!q->base) {
+		dev_err(smmu->dev, "failed to allocate queue (0x%zx bytes)\n",
+			qsz);
+		return -ENOMEM;
+	}
+
+	q->prod_reg	= smmu->base + prod_off;
+	q->cons_reg	= smmu->base + cons_off;
+	q->ent_dwords	= dwords;
+
+	q->q_base  = Q_BASE_RWA;
+	q->q_base |= q->base_dma & Q_BASE_ADDR_MASK << Q_BASE_ADDR_SHIFT;
+	q->q_base |= (q->max_n_shift & Q_BASE_LOG2SIZE_MASK)
+		     << Q_BASE_LOG2SIZE_SHIFT;
+
+	q->prod = q->cons = 0;
+	return 0;
+}
+
+static int arm_smmu_init_queues(struct arm_smmu_device *smmu)
+{
+	int ret;
+
+	/* cmdq */
+	spin_lock_init(&smmu->cmdq.lock);
+	ret = arm_smmu_init_one_queue(smmu, &smmu->cmdq.q, ARM_SMMU_CMDQ_PROD,
+				      ARM_SMMU_CMDQ_CONS, CMDQ_ENT_DWORDS);
+	if (ret)
+		return ret;
+
+	/* evtq */
+	ret = arm_smmu_init_one_queue(smmu, &smmu->evtq.q, ARM_SMMU_EVTQ_PROD,
+				      ARM_SMMU_EVTQ_CONS, EVTQ_ENT_DWORDS);
+	if (ret)
+		return ret;
+
+	/* priq */
+	if (!(smmu->features & ARM_SMMU_FEAT_PRI))
+		return 0;
+
+	return arm_smmu_init_one_queue(smmu, &smmu->priq.q, ARM_SMMU_PRIQ_PROD,
+				       ARM_SMMU_PRIQ_CONS, PRIQ_ENT_DWORDS);
+}
+
+static int arm_smmu_init_l1_strtab(struct arm_smmu_device *smmu)
+{
+	unsigned int i;
+	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+	size_t size = sizeof(*cfg->l1_desc) * cfg->num_l1_ents;
+	void *strtab = smmu->strtab_cfg.strtab;
+
+	cfg->l1_desc = devm_kzalloc(smmu->dev, size, GFP_KERNEL);
+	if (!cfg->l1_desc) {
+		dev_err(smmu->dev, "failed to allocate l1 stream table desc\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < cfg->num_l1_ents; ++i) {
+		arm_smmu_write_strtab_l1_desc(strtab, &cfg->l1_desc[i]);
+		strtab += STRTAB_L1_DESC_DWORDS << 3;
+	}
+
+	return 0;
+}
+
+static int arm_smmu_init_strtab_2lvl(struct arm_smmu_device *smmu)
+{
+	void *strtab;
+	u64 reg;
+	u32 size, l1size;
+	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+
+	/* Calculate the L1 size, capped to the SIDSIZE. */
+	size = STRTAB_L1_SZ_SHIFT - (ilog2(STRTAB_L1_DESC_DWORDS) + 3);
+	size = min(size, smmu->sid_bits - STRTAB_SPLIT);
+	cfg->num_l1_ents = 1 << size;
+
+	size += STRTAB_SPLIT;
+	if (size < smmu->sid_bits)
+		dev_warn(smmu->dev,
+			 "2-level strtab only covers %u/%u bits of SID\n",
+			 size, smmu->sid_bits);
+
+	l1size = cfg->num_l1_ents * (STRTAB_L1_DESC_DWORDS << 3);
+	strtab = dmam_alloc_coherent(smmu->dev, l1size, &cfg->strtab_dma,
+				     GFP_KERNEL | __GFP_ZERO);
+	if (!strtab) {
+		dev_err(smmu->dev,
+			"failed to allocate l1 stream table (%u bytes)\n",
+			size);
+		return -ENOMEM;
+	}
+	cfg->strtab = strtab;
+
+	/* Configure strtab_base_cfg for 2 levels */
+	reg  = STRTAB_BASE_CFG_FMT_2LVL;
+	reg |= (size & STRTAB_BASE_CFG_LOG2SIZE_MASK)
+		<< STRTAB_BASE_CFG_LOG2SIZE_SHIFT;
+	reg |= (STRTAB_SPLIT & STRTAB_BASE_CFG_SPLIT_MASK)
+		<< STRTAB_BASE_CFG_SPLIT_SHIFT;
+	cfg->strtab_base_cfg = reg;
+
+	return arm_smmu_init_l1_strtab(smmu);
+}
+
+static int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu)
+{
+	void *strtab;
+	u64 reg;
+	u32 size;
+	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
+
+	size = (1 << smmu->sid_bits) * (STRTAB_STE_DWORDS << 3);
+	strtab = dmam_alloc_coherent(smmu->dev, size, &cfg->strtab_dma,
+				     GFP_KERNEL | __GFP_ZERO);
+	if (!strtab) {
+		dev_err(smmu->dev,
+			"failed to allocate linear stream table (%u bytes)\n",
+			size);
+		return -ENOMEM;
+	}
+	cfg->strtab = strtab;
+	cfg->num_l1_ents = 1 << smmu->sid_bits;
+
+	/* Configure strtab_base_cfg for a linear table covering all SIDs */
+	reg  = STRTAB_BASE_CFG_FMT_LINEAR;
+	reg |= (smmu->sid_bits & STRTAB_BASE_CFG_LOG2SIZE_MASK)
+		<< STRTAB_BASE_CFG_LOG2SIZE_SHIFT;
+	cfg->strtab_base_cfg = reg;
+
+	arm_smmu_init_bypass_stes(strtab, cfg->num_l1_ents);
+	return 0;
+}
+
+static int arm_smmu_init_strtab(struct arm_smmu_device *smmu)
+{
+	u64 reg;
+	int ret;
+
+	if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB)
+		ret = arm_smmu_init_strtab_2lvl(smmu);
+	else
+		ret = arm_smmu_init_strtab_linear(smmu);
+
+	if (ret)
+		return ret;
+
+	/* Set the strtab base address */
+	reg  = smmu->strtab_cfg.strtab_dma &
+	       STRTAB_BASE_ADDR_MASK << STRTAB_BASE_ADDR_SHIFT;
+	reg |= STRTAB_BASE_RA;
+	smmu->strtab_cfg.strtab_base = reg;
+
+	/* Allocate the first VMID for stage-2 bypass STEs */
+	set_bit(0, smmu->vmid_map);
+	return 0;
+}
+
+static int arm_smmu_init_structures(struct arm_smmu_device *smmu)
+{
+	int ret;
+
+	ret = arm_smmu_init_queues(smmu);
+	if (ret)
+		return ret;
+
+	return arm_smmu_init_strtab(smmu);
+}
+
+static int arm_smmu_write_reg_sync(struct arm_smmu_device *smmu, u32 val,
+				   unsigned int reg_off, unsigned int ack_off)
+{
+	u32 reg;
+
+	writel_relaxed(val, smmu->base + reg_off);
+	return readl_relaxed_poll_timeout(smmu->base + ack_off, reg, reg == val,
+					  1, ARM_SMMU_POLL_TIMEOUT_US);
+}
+
+/* GBPA is "special" */
+static int arm_smmu_update_gbpa(struct arm_smmu_device *smmu, u32 set, u32 clr)
+{
+	int ret;
+	u32 reg, __iomem *gbpa = smmu->base + ARM_SMMU_GBPA;
+
+	ret = readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE),
+					 1, ARM_SMMU_POLL_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	reg &= ~clr;
+	reg |= set;
+	writel_relaxed(reg | GBPA_UPDATE, gbpa);
+	return readl_relaxed_poll_timeout(gbpa, reg, !(reg & GBPA_UPDATE),
+					  1, ARM_SMMU_POLL_TIMEOUT_US);
+}
+
+static void arm_smmu_free_msis(void *data)
+{
+	struct device *dev = data;
+	platform_msi_domain_free_irqs(dev);
+}
+
+static void arm_smmu_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
+{
+	phys_addr_t doorbell;
+	struct device *dev = msi_desc_to_dev(desc);
+	struct arm_smmu_device *smmu = dev_get_drvdata(dev);
+	phys_addr_t *cfg = arm_smmu_msi_cfg[desc->platform.msi_index];
+
+	doorbell = (((u64)msg->address_hi) << 32) | msg->address_lo;
+	doorbell &= MSI_CFG0_ADDR_MASK << MSI_CFG0_ADDR_SHIFT;
+
+	writeq_relaxed(doorbell, smmu->base + cfg[0]);
+	writel_relaxed(msg->data, smmu->base + cfg[1]);
+	writel_relaxed(MSI_CFG2_MEMATTR_DEVICE_nGnRE, smmu->base + cfg[2]);
+}
+
+static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
+{
+	struct msi_desc *desc;
+	int ret, nvec = ARM_SMMU_MAX_MSIS;
+	struct device *dev = smmu->dev;
+
+	/* Clear the MSI address regs */
+	writeq_relaxed(0, smmu->base + ARM_SMMU_GERROR_IRQ_CFG0);
+	writeq_relaxed(0, smmu->base + ARM_SMMU_EVTQ_IRQ_CFG0);
+
+	if (smmu->features & ARM_SMMU_FEAT_PRI)
+		writeq_relaxed(0, smmu->base + ARM_SMMU_PRIQ_IRQ_CFG0);
+	else
+		nvec--;
+
+	if (!(smmu->features & ARM_SMMU_FEAT_MSI))
+		return;
+
+	/* Allocate MSIs for evtq, gerror and priq. Ignore cmdq */
+	ret = platform_msi_domain_alloc_irqs(dev, nvec, arm_smmu_write_msi_msg);
+	if (ret) {
+		dev_warn(dev, "failed to allocate MSIs\n");
+		return;
+	}
+
+	for_each_msi_entry(desc, dev) {
+		switch (desc->platform.msi_index) {
+		case EVTQ_MSI_INDEX:
+			smmu->evtq.q.irq = desc->irq;
+			break;
+		case GERROR_MSI_INDEX:
+			smmu->gerr_irq = desc->irq;
+			break;
+		case PRIQ_MSI_INDEX:
+			smmu->priq.q.irq = desc->irq;
+			break;
+		default:	/* Unknown */
+			continue;
+		}
+	}
+
+	/* Add callback to free MSIs on teardown */
+	devm_add_action(dev, arm_smmu_free_msis, dev);
+}
+
+static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
+{
+	int ret, irq;
+	u32 irqen_flags = IRQ_CTRL_EVTQ_IRQEN | IRQ_CTRL_GERROR_IRQEN;
+
+	/* Disable IRQs first */
+	ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_IRQ_CTRL,
+				      ARM_SMMU_IRQ_CTRLACK);
+	if (ret) {
+		dev_err(smmu->dev, "failed to disable irqs\n");
+		return ret;
+	}
+
+	arm_smmu_setup_msis(smmu);
+
+	/* Request interrupt lines */
+	irq = smmu->evtq.q.irq;
+	if (irq) {
+		ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
+						arm_smmu_evtq_thread,
+						IRQF_ONESHOT,
+						"arm-smmu-v3-evtq", smmu);
+		if (ret < 0)
+			dev_warn(smmu->dev, "failed to enable evtq irq\n");
+	}
+
+	irq = smmu->cmdq.q.irq;
+	if (irq) {
+		ret = devm_request_irq(smmu->dev, irq,
+				       arm_smmu_cmdq_sync_handler, 0,
+				       "arm-smmu-v3-cmdq-sync", smmu);
+		if (ret < 0)
+			dev_warn(smmu->dev, "failed to enable cmdq-sync irq\n");
+	}
+
+	irq = smmu->gerr_irq;
+	if (irq) {
+		ret = devm_request_irq(smmu->dev, irq, arm_smmu_gerror_handler,
+				       0, "arm-smmu-v3-gerror", smmu);
+		if (ret < 0)
+			dev_warn(smmu->dev, "failed to enable gerror irq\n");
+	}
+
+	if (smmu->features & ARM_SMMU_FEAT_PRI) {
+		irq = smmu->priq.q.irq;
+		if (irq) {
+			ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
+							arm_smmu_priq_thread,
+							IRQF_ONESHOT,
+							"arm-smmu-v3-priq",
+							smmu);
+			if (ret < 0)
+				dev_warn(smmu->dev,
+					 "failed to enable priq irq\n");
+			else
+				irqen_flags |= IRQ_CTRL_PRIQ_IRQEN;
+		}
+	}
+
+	/* Enable interrupt generation on the SMMU */
+	ret = arm_smmu_write_reg_sync(smmu, irqen_flags,
+				      ARM_SMMU_IRQ_CTRL, ARM_SMMU_IRQ_CTRLACK);
+	if (ret)
+		dev_warn(smmu->dev, "failed to enable irqs\n");
+
+	return 0;
+}
+
+static int arm_smmu_device_disable(struct arm_smmu_device *smmu)
+{
+	int ret;
+
+	ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_CR0, ARM_SMMU_CR0ACK);
+	if (ret)
+		dev_err(smmu->dev, "failed to clear cr0\n");
+
+	return ret;
+}
+
+static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass)
+{
+	int ret;
+	u32 reg, enables;
+	struct arm_smmu_cmdq_ent cmd;
+
+	/* Clear CR0 and sync (disables SMMU and queue processing) */
+	reg = readl_relaxed(smmu->base + ARM_SMMU_CR0);
+	if (reg & CR0_SMMUEN)
+		dev_warn(smmu->dev, "SMMU currently enabled! Resetting...\n");
+
+	ret = arm_smmu_device_disable(smmu);
+	if (ret)
+		return ret;
+
+	/* CR1 (table and queue memory attributes) */
+	reg = (CR1_SH_ISH << CR1_TABLE_SH_SHIFT) |
+	      (CR1_CACHE_WB << CR1_TABLE_OC_SHIFT) |
+	      (CR1_CACHE_WB << CR1_TABLE_IC_SHIFT) |
+	      (CR1_SH_ISH << CR1_QUEUE_SH_SHIFT) |
+	      (CR1_CACHE_WB << CR1_QUEUE_OC_SHIFT) |
+	      (CR1_CACHE_WB << CR1_QUEUE_IC_SHIFT);
+	writel_relaxed(reg, smmu->base + ARM_SMMU_CR1);
+
+	/* CR2 (random crap) */
+	reg = CR2_PTM | CR2_RECINVSID | CR2_E2H;
+	writel_relaxed(reg, smmu->base + ARM_SMMU_CR2);
+
+	/* Stream table */
+	writeq_relaxed(smmu->strtab_cfg.strtab_base,
+		       smmu->base + ARM_SMMU_STRTAB_BASE);
+	writel_relaxed(smmu->strtab_cfg.strtab_base_cfg,
+		       smmu->base + ARM_SMMU_STRTAB_BASE_CFG);
+
+	/* Command queue */
+	writeq_relaxed(smmu->cmdq.q.q_base, smmu->base + ARM_SMMU_CMDQ_BASE);
+	writel_relaxed(smmu->cmdq.q.prod, smmu->base + ARM_SMMU_CMDQ_PROD);
+	writel_relaxed(smmu->cmdq.q.cons, smmu->base + ARM_SMMU_CMDQ_CONS);
+
+	enables = CR0_CMDQEN;
+	ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
+				      ARM_SMMU_CR0ACK);
+	if (ret) {
+		dev_err(smmu->dev, "failed to enable command queue\n");
+		return ret;
+	}
+
+	/* Invalidate any cached configuration */
+	cmd.opcode = CMDQ_OP_CFGI_ALL;
+	arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+	cmd.opcode = CMDQ_OP_CMD_SYNC;
+	arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+
+	/* Invalidate any stale TLB entries */
+	if (smmu->features & ARM_SMMU_FEAT_HYP) {
+		cmd.opcode = CMDQ_OP_TLBI_EL2_ALL;
+		arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+	}
+
+	cmd.opcode = CMDQ_OP_TLBI_NSNH_ALL;
+	arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+	cmd.opcode = CMDQ_OP_CMD_SYNC;
+	arm_smmu_cmdq_issue_cmd(smmu, &cmd);
+
+	/* Event queue */
+	writeq_relaxed(smmu->evtq.q.q_base, smmu->base + ARM_SMMU_EVTQ_BASE);
+	writel_relaxed(smmu->evtq.q.prod, smmu->base + ARM_SMMU_EVTQ_PROD);
+	writel_relaxed(smmu->evtq.q.cons, smmu->base + ARM_SMMU_EVTQ_CONS);
+
+	enables |= CR0_EVTQEN;
+	ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
+				      ARM_SMMU_CR0ACK);
+	if (ret) {
+		dev_err(smmu->dev, "failed to enable event queue\n");
+		return ret;
+	}
+
+	/* PRI queue */
+	if (smmu->features & ARM_SMMU_FEAT_PRI) {
+		writeq_relaxed(smmu->priq.q.q_base,
+			       smmu->base + ARM_SMMU_PRIQ_BASE);
+		writel_relaxed(smmu->priq.q.prod,
+			       smmu->base + ARM_SMMU_PRIQ_PROD);
+		writel_relaxed(smmu->priq.q.cons,
+			       smmu->base + ARM_SMMU_PRIQ_CONS);
+
+		enables |= CR0_PRIQEN;
+		ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
+					      ARM_SMMU_CR0ACK);
+		if (ret) {
+			dev_err(smmu->dev, "failed to enable PRI queue\n");
+			return ret;
+		}
+	}
+
+	ret = arm_smmu_setup_irqs(smmu);
+	if (ret) {
+		dev_err(smmu->dev, "failed to setup irqs\n");
+		return ret;
+	}
+
+
+	/* Enable the SMMU interface, or ensure bypass */
+	if (!bypass || disable_bypass) {
+		enables |= CR0_SMMUEN;
+	} else {
+		ret = arm_smmu_update_gbpa(smmu, 0, GBPA_ABORT);
+		if (ret) {
+			dev_err(smmu->dev, "GBPA not responding to update\n");
+			return ret;
+		}
+	}
+	ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
+				      ARM_SMMU_CR0ACK);
+	if (ret) {
+		dev_err(smmu->dev, "failed to enable SMMU interface\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
+{
+	u32 reg;
+	bool coherent = smmu->features & ARM_SMMU_FEAT_COHERENCY;
+
+	/* IDR0 */
+	reg = readl_relaxed(smmu->base + ARM_SMMU_IDR0);
+
+	/* 2-level structures */
+	if ((reg & IDR0_ST_LVL_MASK << IDR0_ST_LVL_SHIFT) == IDR0_ST_LVL_2LVL)
+		smmu->features |= ARM_SMMU_FEAT_2_LVL_STRTAB;
+
+	if (reg & IDR0_CD2L)
+		smmu->features |= ARM_SMMU_FEAT_2_LVL_CDTAB;
+
+	/*
+	 * Translation table endianness.
+	 * We currently require the same endianness as the CPU, but this
+	 * could be changed later by adding a new IO_PGTABLE_QUIRK.
+	 */
+	switch (reg & IDR0_TTENDIAN_MASK << IDR0_TTENDIAN_SHIFT) {
+	case IDR0_TTENDIAN_MIXED:
+		smmu->features |= ARM_SMMU_FEAT_TT_LE | ARM_SMMU_FEAT_TT_BE;
+		break;
+#ifdef __BIG_ENDIAN
+	case IDR0_TTENDIAN_BE:
+		smmu->features |= ARM_SMMU_FEAT_TT_BE;
+		break;
+#else
+	case IDR0_TTENDIAN_LE:
+		smmu->features |= ARM_SMMU_FEAT_TT_LE;
+		break;
+#endif
+	default:
+		dev_err(smmu->dev, "unknown/unsupported TT endianness!\n");
+		return -ENXIO;
+	}
+
+	/* Boolean feature flags */
+	if (IS_ENABLED(CONFIG_PCI_PRI) && reg & IDR0_PRI)
+		smmu->features |= ARM_SMMU_FEAT_PRI;
+
+	if (IS_ENABLED(CONFIG_PCI_ATS) && reg & IDR0_ATS)
+		smmu->features |= ARM_SMMU_FEAT_ATS;
+
+	if (reg & IDR0_SEV)
+		smmu->features |= ARM_SMMU_FEAT_SEV;
+
+	if (reg & IDR0_MSI)
+		smmu->features |= ARM_SMMU_FEAT_MSI;
+
+	if (reg & IDR0_HYP)
+		smmu->features |= ARM_SMMU_FEAT_HYP;
+
+	/*
+	 * The coherency feature as set by FW is used in preference to the ID
+	 * register, but warn on mismatch.
+	 */
+	if (!!(reg & IDR0_COHACC) != coherent)
+		dev_warn(smmu->dev, "IDR0.COHACC overridden by dma-coherent property (%s)\n",
+			 coherent ? "true" : "false");
+
+	switch (reg & IDR0_STALL_MODEL_MASK << IDR0_STALL_MODEL_SHIFT) {
+	case IDR0_STALL_MODEL_STALL:
+		/* Fallthrough */
+	case IDR0_STALL_MODEL_FORCE:
+		smmu->features |= ARM_SMMU_FEAT_STALLS;
+	}
+
+	if (reg & IDR0_S1P)
+		smmu->features |= ARM_SMMU_FEAT_TRANS_S1;
+
+	if (reg & IDR0_S2P)
+		smmu->features |= ARM_SMMU_FEAT_TRANS_S2;
+
+	if (!(reg & (IDR0_S1P | IDR0_S2P))) {
+		dev_err(smmu->dev, "no translation support!\n");
+		return -ENXIO;
+	}
+
+	/* We only support the AArch64 table format at present */
+	switch (reg & IDR0_TTF_MASK << IDR0_TTF_SHIFT) {
+	case IDR0_TTF_AARCH32_64:
+		smmu->ias = 40;
+		/* Fallthrough */
+	case IDR0_TTF_AARCH64:
+		break;
+	default:
+		dev_err(smmu->dev, "AArch64 table format not supported!\n");
+		return -ENXIO;
+	}
+
+	/* ASID/VMID sizes */
+	smmu->asid_bits = reg & IDR0_ASID16 ? 16 : 8;
+	smmu->vmid_bits = reg & IDR0_VMID16 ? 16 : 8;
+
+	/* IDR1 */
+	reg = readl_relaxed(smmu->base + ARM_SMMU_IDR1);
+	if (reg & (IDR1_TABLES_PRESET | IDR1_QUEUES_PRESET | IDR1_REL)) {
+		dev_err(smmu->dev, "embedded implementation not supported\n");
+		return -ENXIO;
+	}
+
+	/* Queue sizes, capped at 4k */
+	smmu->cmdq.q.max_n_shift = min((u32)CMDQ_MAX_SZ_SHIFT,
+				       reg >> IDR1_CMDQ_SHIFT & IDR1_CMDQ_MASK);
+	if (!smmu->cmdq.q.max_n_shift) {
+		/* Odd alignment restrictions on the base, so ignore for now */
+		dev_err(smmu->dev, "unit-length command queue not supported\n");
+		return -ENXIO;
+	}
+
+	smmu->evtq.q.max_n_shift = min((u32)EVTQ_MAX_SZ_SHIFT,
+				       reg >> IDR1_EVTQ_SHIFT & IDR1_EVTQ_MASK);
+	smmu->priq.q.max_n_shift = min((u32)PRIQ_MAX_SZ_SHIFT,
+				       reg >> IDR1_PRIQ_SHIFT & IDR1_PRIQ_MASK);
+
+	/* SID/SSID sizes */
+	smmu->ssid_bits = reg >> IDR1_SSID_SHIFT & IDR1_SSID_MASK;
+	smmu->sid_bits = reg >> IDR1_SID_SHIFT & IDR1_SID_MASK;
+
+	/*
+	 * If the SMMU supports fewer bits than would fill a single L2 stream
+	 * table, use a linear table instead.
+	 */
+	if (smmu->sid_bits <= STRTAB_SPLIT)
+		smmu->features &= ~ARM_SMMU_FEAT_2_LVL_STRTAB;
+
+	/* IDR5 */
+	reg = readl_relaxed(smmu->base + ARM_SMMU_IDR5);
+
+	/* Maximum number of outstanding stalls */
+	smmu->evtq.max_stalls = reg >> IDR5_STALL_MAX_SHIFT
+				& IDR5_STALL_MAX_MASK;
+
+	/* Page sizes */
+	if (reg & IDR5_GRAN64K)
+		smmu->pgsize_bitmap |= SZ_64K | SZ_512M;
+	if (reg & IDR5_GRAN16K)
+		smmu->pgsize_bitmap |= SZ_16K | SZ_32M;
+	if (reg & IDR5_GRAN4K)
+		smmu->pgsize_bitmap |= SZ_4K | SZ_2M | SZ_1G;
+
+	if (arm_smmu_ops.pgsize_bitmap == -1UL)
+		arm_smmu_ops.pgsize_bitmap = smmu->pgsize_bitmap;
+	else
+		arm_smmu_ops.pgsize_bitmap |= smmu->pgsize_bitmap;
+
+	/* Output address size */
+	switch (reg & IDR5_OAS_MASK << IDR5_OAS_SHIFT) {
+	case IDR5_OAS_32_BIT:
+		smmu->oas = 32;
+		break;
+	case IDR5_OAS_36_BIT:
+		smmu->oas = 36;
+		break;
+	case IDR5_OAS_40_BIT:
+		smmu->oas = 40;
+		break;
+	case IDR5_OAS_42_BIT:
+		smmu->oas = 42;
+		break;
+	case IDR5_OAS_44_BIT:
+		smmu->oas = 44;
+		break;
+	default:
+		dev_info(smmu->dev,
+			"unknown output address size. Truncating to 48-bit\n");
+		/* Fallthrough */
+	case IDR5_OAS_48_BIT:
+		smmu->oas = 48;
+	}
+
+	/* Set the DMA mask for our table walker */
+	if (dma_set_mask_and_coherent(smmu->dev, DMA_BIT_MASK(smmu->oas)))
+		dev_warn(smmu->dev,
+			 "failed to set DMA mask for table walker\n");
+
+	smmu->ias = max(smmu->ias, smmu->oas);
+
+	dev_info(smmu->dev, "ias %lu-bit, oas %lu-bit (features 0x%08x)\n",
+		 smmu->ias, smmu->oas, smmu->features);
+	return 0;
+}
+
+#ifdef CONFIG_ACPI
+static int arm_smmu_device_acpi_probe(struct platform_device *pdev,
+				      struct arm_smmu_device *smmu)
+{
+	struct acpi_iort_smmu_v3 *iort_smmu;
+	struct device *dev = smmu->dev;
+	struct acpi_iort_node *node;
+
+	node = *(struct acpi_iort_node **)dev_get_platdata(dev);
+
+	/* Retrieve SMMUv3 specific data */
+	iort_smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
+
+	if (iort_smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE)
+		smmu->features |= ARM_SMMU_FEAT_COHERENCY;
+
+	return 0;
+}
+#else
+static inline int arm_smmu_device_acpi_probe(struct platform_device *pdev,
+					     struct arm_smmu_device *smmu)
+{
+	return -ENODEV;
+}
+#endif
+
+static int arm_smmu_device_dt_probe(struct platform_device *pdev,
+				    struct arm_smmu_device *smmu)
+{
+	struct device *dev = &pdev->dev;
+	u32 cells;
+	int ret = -EINVAL;
+
+	if (of_property_read_u32(dev->of_node, "#iommu-cells", &cells))
+		dev_err(dev, "missing #iommu-cells property\n");
+	else if (cells != 1)
+		dev_err(dev, "invalid #iommu-cells value (%d)\n", cells);
+	else
+		ret = 0;
+
+	parse_driver_options(smmu);
+
+	if (of_dma_is_coherent(dev->of_node))
+		smmu->features |= ARM_SMMU_FEAT_COHERENCY;
+
+	return ret;
+}
+
+static int arm_smmu_device_probe(struct platform_device *pdev)
+{
+	int irq, ret;
+	struct resource *res;
+	resource_size_t ioaddr;
+	struct arm_smmu_device *smmu;
+	struct device *dev = &pdev->dev;
+	bool bypass;
+
+	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;
+
+	/* Base address */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (resource_size(res) + 1 < SZ_128K) {
+		dev_err(dev, "MMIO region too small (%pr)\n", res);
+		return -EINVAL;
+	}
+	ioaddr = res->start;
+
+	smmu->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(smmu->base))
+		return PTR_ERR(smmu->base);
+
+	/* Interrupt lines */
+	irq = platform_get_irq_byname(pdev, "eventq");
+	if (irq > 0)
+		smmu->evtq.q.irq = irq;
+
+	irq = platform_get_irq_byname(pdev, "priq");
+	if (irq > 0)
+		smmu->priq.q.irq = irq;
+
+	irq = platform_get_irq_byname(pdev, "cmdq-sync");
+	if (irq > 0)
+		smmu->cmdq.q.irq = irq;
+
+	irq = platform_get_irq_byname(pdev, "gerror");
+	if (irq > 0)
+		smmu->gerr_irq = irq;
+
+	if (dev->of_node) {
+		ret = arm_smmu_device_dt_probe(pdev, smmu);
+	} else {
+		ret = arm_smmu_device_acpi_probe(pdev, smmu);
+		if (ret == -ENODEV)
+			return ret;
+	}
+
+	/* Set bypass mode according to firmware probing result */
+	bypass = !!ret;
+
+	/* Probe the h/w */
+	ret = arm_smmu_device_hw_probe(smmu);
+	if (ret)
+		return ret;
+
+	/* Initialise in-memory data structures */
+	ret = arm_smmu_init_structures(smmu);
+	if (ret)
+		return ret;
+
+	/* Record our private device structure */
+	platform_set_drvdata(pdev, smmu);
+
+	/* Reset the device */
+	ret = arm_smmu_device_reset(smmu, bypass);
+	if (ret)
+		return ret;
+
+	/* And we're up. Go go go! */
+	ret = iommu_device_sysfs_add(&smmu->iommu, dev, NULL,
+				     "smmu3.%pa", &ioaddr);
+	if (ret)
+		return ret;
+
+	iommu_device_set_ops(&smmu->iommu, &arm_smmu_ops);
+	iommu_device_set_fwnode(&smmu->iommu, dev->fwnode);
+
+	ret = iommu_device_register(&smmu->iommu);
+
+#ifdef CONFIG_PCI
+	if (pci_bus_type.iommu_ops != &arm_smmu_ops) {
+		pci_request_acs();
+		ret = bus_set_iommu(&pci_bus_type, &arm_smmu_ops);
+		if (ret)
+			return ret;
+	}
+#endif
+#ifdef CONFIG_ARM_AMBA
+	if (amba_bustype.iommu_ops != &arm_smmu_ops) {
+		ret = bus_set_iommu(&amba_bustype, &arm_smmu_ops);
+		if (ret)
+			return ret;
+	}
+#endif
+	if (platform_bus_type.iommu_ops != &arm_smmu_ops) {
+		ret = bus_set_iommu(&platform_bus_type, &arm_smmu_ops);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+static int arm_smmu_device_remove(struct platform_device *pdev)
+{
+	struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
+
+	arm_smmu_device_disable(smmu);
+	return 0;
+}
+
+static struct of_device_id arm_smmu_of_match[] = {
+	{ .compatible = "arm,smmu-v3", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
+
+static struct platform_driver arm_smmu_driver = {
+	.driver	= {
+		.name		= "arm-smmu-v3",
+		.of_match_table	= of_match_ptr(arm_smmu_of_match),
+	},
+	.probe	= arm_smmu_device_probe,
+	.remove	= arm_smmu_device_remove,
+};
+module_platform_driver(arm_smmu_driver);
+
+IOMMU_OF_DECLARE(arm_smmuv3, "arm,smmu-v3", NULL);
+
+MODULE_DESCRIPTION("IOMMU API for ARM architected SMMUv3 implementations");
+MODULE_AUTHOR("Will Deacon <[email protected]>");
+MODULE_LICENSE("GPL v2");
Signature · 7 lines
Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.


_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#24 lore raw

RFC v2 7/7 xen/iommu: smmu-v3: Add Xen specific code to enable the ported driver

In reply to Sameer Goel

This driver follows an approach similar to smmu driver. The intent here is to reuse as much Linux code as possible.
- Glue code has been introduced to bridge the API calls.
- Called Linux functions from the Xen IOMMU function calls.
- Xen modifications are preceded by /*Xen: comment */

---
 xen/drivers/passthrough/arm/Makefile  |   1 +
 xen/drivers/passthrough/arm/smmu-v3.c | 853 +++++++++++++++++++++++++++++-----
 2 files changed, 738 insertions(+), 116 deletions(-)
xen/drivers/passthrough/arm/Makefile +1 -0
index f4cd26e..57a6da6 100644
@@ -1,2 +1,3 @@
 obj-y += iommu.o
 obj-y += smmu.o
+obj-y += smmu-v3.o
xen/drivers/passthrough/arm/smmu-v3.c +737 -116

1,317 lines not on this page · raw message

Signature · 7 lines
Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.


_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#25 lore raw

Re: [RFC v2 7/7] xen/iommu: smmu-v3: Add Xen specific code to enable the ported driver

Quoted 1,139 lines · Sameer Goel

This driver follows an approach similar to smmu driver. The intent here is to reuse as much Linux code as possible.
- Glue code has been introduced to bridge the API calls.
- Called Linux functions from the Xen IOMMU function calls.
- Xen modifications are preceded by /*Xen: comment */

---
 xen/drivers/passthrough/arm/Makefile  |   1 +
 xen/drivers/passthrough/arm/smmu-v3.c | 853 +++++++++++++++++++++++++++++-----
 2 files changed, 738 insertions(+), 116 deletions(-)
xen/drivers/passthrough/arm/Makefile +1 -0
index f4cd26e..57a6da6 100644
@@ -1,2 +1,3 @@
 obj-y += iommu.o
 obj-y += smmu.o
+obj-y += smmu-v3.o
xen/drivers/passthrough/arm/smmu-v3.c +475 -116
index 380969a..8f3b43d 100644
@@ -18,28 +18,266 @@
  * Author: Will Deacon <[email protected]>
  *
  * This driver is powered by bad coffee and bombay mix.
+ *
+ *
+ * Based on Linux drivers/iommu/arm-smmu-v3.c
+ * => commit bdf95923086fb359ccb44c815724c3ace1611c90
+ *
+ * Xen modifications:
+ * Sameer Goel <[email protected]>
+ * Copyright (C) 2017, The Linux Foundation, All rights reserved.
+ *
  */
 
-#include <linux/acpi.h>
-#include <linux/acpi_iort.h>
-#include <linux/delay.h>
-#include <linux/dma-iommu.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/iommu.h>
-#include <linux/iopoll.h>
-#include <linux/module.h>
-#include <linux/msi.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_iommu.h>
-#include <linux/of_platform.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-
-#include <linux/amba/bus.h>
-
-#include "io-pgtable.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>
+#include <xen/acpi.h>
+
+typedef paddr_t phys_addr_t;
+typedef paddr_t dma_addr_t;
+
+/* 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
+#define mutex spinlock_t
+#define mutex_init spin_lock_init
+#define mutex_lock spin_lock
+#define mutex_unlock spin_unlock
+
+/* 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 device
+
+#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;
+	struct acpi_iort_node *iort_node;
+	struct acpi_iort_smmu_v3 *node_smmu_data;
+	int ret = 0;
+
+	res.type = type;
+
+	switch (type) {
+	case IORESOURCE_MEM:
+		if (pdev->type == DEV_ACPI) {
+			ret = 1;
+			iort_node = pdev->acpi_node;
+			node_smmu_data =
+				(struct acpi_iort_smmu_v3 *)iort_node->node_data;
+
+			if (node_smmu_data != NULL) {
+				res.addr = node_smmu_data->base_address;
+				res.size = SZ_128K;
+				ret = 0;
+			}
+		} else {
+			ret = dt_device_get_address(dev_to_dt(pdev), num,
+						    &res.addr, &res.size);
+		}
+
+		return ((ret) ? NULL : &res);
+
+	case IORESOURCE_IRQ:
+		ret = platform_get_irq(dev_to_dt(pdev), num);
+
+		if (ret < 0)
+			return NULL;
+
+		res.addr = ret;
+		res.size = 1;
+
+		return &res;
+
+	default:
+		return NULL;
+	}
+}
+
+static int platform_get_irq_byname(struct platform_device *pdev, const char *name)
+{
+	const struct dt_property *dtprop;
+	struct acpi_iort_node *iort_node;
+	struct acpi_iort_smmu_v3 *node_smmu_data;
+	int ret = 0;
+
+	if (pdev->type == DEV_ACPI) {
+		iort_node = pdev->acpi_node;
+		node_smmu_data = (struct acpi_iort_smmu_v3 *)iort_node->node_data;
+
+		if (node_smmu_data != NULL) {
+			if (!strcmp(name, "eventq"))
+				ret = node_smmu_data->event_gsiv;
+			else if (!strcmp(name, "priq"))
+				ret = node_smmu_data->pri_gsiv;
+			else if (!strcmp(name, "cmdq-sync"))
+				ret = node_smmu_data->sync_gsiv;
+			else if (!strcmp(name, "gerror"))
+				ret = node_smmu_data->gerr_gsiv;
+			else
+				ret = -EINVAL;
+		}
+	} else {
+		dtprop = dt_find_property(dev_to_dt(pdev), "interrupt-names", NULL);
+		if (!dtprop)
+			return -EINVAL;
+
+		if (!dtprop->value)
+			return -ENODATA;
+	}
+
+	return ret;
+}
+
+#define readx_poll_timeout(op, addr, val, cond, sleep_us, timeout_us) \
+({ \
+	s_time_t deadline = NOW() + MICROSECS(timeout_us); \
+	for (;;) { \
+		(val) = op(addr); \
+		if (cond) \
+			break; \
+		if (NOW() > deadline) { \
+			(val) = op(addr); \
+			break; \
+		} \
+		cpu_relax(); \
+		udelay(sleep_us); \
+	} \
+	(cond) ? 0 : -ETIMEDOUT; \
+})
+
+#define readl_relaxed_poll_timeout(addr, val, cond, delay_us, timeout_us) \
+	readx_poll_timeout(readl_relaxed, addr, val, cond, delay_us, timeout_us)
+
+/* Xen: Helpers for IRQ functions */
+#define request_irq(irq, func, flags, name, dev) request_irq(irq, flags, func, name, dev)
+#define free_irq release_irq
+
+enum irqreturn {
+	IRQ_NONE	= (0 << 0),
+	IRQ_HANDLED	= (1 << 0),
+};
+
+typedef enum irqreturn irqreturn_t;
+
+/* Device logger functions */
+#define dev_print(dev, lvl, fmt, ...)						\
+	 printk(lvl "smmu: " fmt, ## __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_info(dev, fmt, ...) dev_print(dev, XENLOG_INFO, fmt, ## __VA_ARGS__)
+
+#define dev_err_ratelimited(dev, fmt, ...)					\
+	 dev_print(dev, XENLOG_ERR, fmt, ## __VA_ARGS__)
+
+#define dev_name(dev) dt_node_full_name(dev_to_dt(dev))
+
+/* 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)
+
+/* Compatibility defines */
+#undef WARN_ON
+#define WARN_ON(cond) (!!cond)
+#define WARN_ON_ONCE(cond) WARN_ON(cond)
+
+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: Dummy iommu_domain */
+struct iommu_domain {
+	/* Runtime SMMU configuration for this iommu_domain */
+	struct arm_smmu_domain		*priv;
+	unsigned int			type;
+
+	atomic_t ref;
+	/* Used to link iommu_domain contexts for a same domain.
+	 * There is at least one per-SMMU to used by the domain.
+	 */
+	struct list_head		list;
+};
+/* Xen: Domain type definitions. Not really needed for Xen, defining to port
+ * Linux code as-is
+ */
+#define IOMMU_DOMAIN_UNMANAGED 0
+#define IOMMU_DOMAIN_DMA 1
+#define IOMMU_DOMAIN_IDENTITY 2
+
+/* Xen: Describes information required for a Xen domain */
+struct arm_smmu_xen_domain {
+	spinlock_t			lock;
+	/* List of iommu domains associated to this domain */
+	struct list_head		iommu_domains;
+};
+
+/*
+ * Xen: Information about each device stored in dev->archdata.iommu
+ *
+ * The dev->archdata.iommu stores the iommu_domain (runtime configuration of
+ * the SMMU).
+ */
+struct arm_smmu_xen_device {
+	struct iommu_domain *domain;
+};
 
 /* MMIO registers */
 #define ARM_SMMU_IDR0			0x0
@@ -412,10 +650,12 @@
 #define MSI_IOVA_BASE			0x8000000
 #define MSI_IOVA_LENGTH			0x100000
 
+#if 0 /* Not applicable for Xen */
 static bool disable_bypass;
 module_param_named(disable_bypass, disable_bypass, bool, S_IRUGO);
 MODULE_PARM_DESC(disable_bypass,
 	"Disable bypass streams such that incoming transactions from devices that are not attached to an iommu domain will report an abort back to the device and will not be allowed to pass through the SMMU.");
+#endif
 
 enum pri_resp {
 	PRI_RESP_DENY,
@@ -423,6 +663,7 @@ enum pri_resp {
 	PRI_RESP_SUCC,
 };
 
+#if 0 /* Xen: No MSI support in this iteration */
 enum arm_smmu_msi_index {
 	EVTQ_MSI_INDEX,
 	GERROR_MSI_INDEX,
@@ -447,6 +688,7 @@ static phys_addr_t arm_smmu_msi_cfg[ARM_SMMU_MAX_MSIS][3] = {
 		ARM_SMMU_PRIQ_IRQ_CFG2,
 	},
 };
+#endif
 
 struct arm_smmu_cmdq_ent {
 	/* Common fields */
@@ -551,6 +793,8 @@ struct arm_smmu_s2_cfg {
 	u16				vmid;
 	u64				vttbr;
 	u64				vtcr;
+	/* Xen: Domain associated to this configuration */
+	struct domain			*domain;
 };
 
 struct arm_smmu_strtab_ent {
@@ -623,9 +867,20 @@ struct arm_smmu_device {
 	struct arm_smmu_strtab_cfg	strtab_cfg;
 
 	/* IOMMU core code handle */
-	struct iommu_device		iommu;
+	//struct iommu_device		iommu;
+
+	/* Xen: Need to keep a list of SMMU devices */
+	struct list_head                devices;
 };
 
+/* Xen: Keep a list of devices associated with this driver */
+static DEFINE_SPINLOCK(arm_smmu_devices_lock);
+static LIST_HEAD(arm_smmu_devices);
+/* Xen: Helper for finding a device using fwnode */
+static
+struct arm_smmu_device *arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode);
+
+
 /* SMMU private data for each master */
 struct arm_smmu_master_data {
 	struct arm_smmu_device		*smmu;
@@ -642,7 +897,7 @@ enum arm_smmu_domain_stage {
 
 struct arm_smmu_domain {
 	struct arm_smmu_device		*smmu;
-	struct mutex			init_mutex; /* Protects smmu pointer */
+	mutex			init_mutex; /* Protects smmu pointer */
 
 	struct io_pgtable_ops		*pgtbl_ops;
 	spinlock_t			pgtbl_lock;
@@ -737,15 +992,16 @@ static void queue_inc_prod(struct arm_smmu_queue *q)
  */
 static int queue_poll_cons(struct arm_smmu_queue *q, bool drain, bool wfe)
 {
-	ktime_t timeout = ktime_add_us(ktime_get(), ARM_SMMU_POLL_TIMEOUT_US);
+	s_time_t deadline = NOW() + MICROSECS(ARM_SMMU_POLL_TIMEOUT_US);
 
 	while (queue_sync_cons(q), (drain ? !queue_empty(q) : queue_full(q))) {
-		if (ktime_compare(ktime_get(), timeout) > 0)
+
+		if (NOW() > deadline)
 			return -ETIMEDOUT;
 
-		if (wfe) {
+		if (wfe)
 			wfe();
-		} else {
+		else {
 			cpu_relax();
 			udelay(1);
 		}
@@ -931,7 +1187,7 @@ static void arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
 		dev_err_ratelimited(smmu->dev, "CMD_SYNC timeout\n");
 	spin_unlock_irqrestore(&smmu->cmdq.lock, flags);
 }
-
+#if 0
 /* Context descriptor manipulation functions */
 static u64 arm_smmu_cpu_tcr_to_cd(u64 tcr)
 {
@@ -974,7 +1230,7 @@ static void arm_smmu_write_ctx_desc(struct arm_smmu_device *smmu,
 
 	cfg->cdptr[3] = cpu_to_le64(cfg->cd.mair << CTXDESC_CD_3_MAIR_SHIFT);
 }
-
+#endif
 /* Stream table manipulation functions */
 static void
 arm_smmu_write_strtab_l1_desc(__le64 *dst, struct arm_smmu_strtab_l1_desc *desc)
@@ -1044,7 +1300,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
 			ste_live = true;
 			break;
 		case STRTAB_STE_0_CFG_ABORT:
-			if (disable_bypass)
+			//No bypass override for Xen
 				break;
 		default:
 			BUG(); /* STE corruption */
@@ -1056,7 +1312,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
 
 	/* Bypass/fault */
 	if (!ste->assigned || !(ste->s1_cfg || ste->s2_cfg)) {
-		if (!ste->assigned && disable_bypass)
+		if (!ste->assigned)
 			val |= STRTAB_STE_0_CFG_ABORT;
 		else
 			val |= STRTAB_STE_0_CFG_BYPASS;
@@ -1135,16 +1391,20 @@ static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid)
 	void *strtab;
 	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
 	struct arm_smmu_strtab_l1_desc *desc = &cfg->l1_desc[sid >> STRTAB_SPLIT];
+	u32 alignment = 0;
 
 	if (desc->l2ptr)
 		return 0;
 
-	size = 1 << (STRTAB_SPLIT + ilog2(STRTAB_STE_DWORDS) + 3);
+	size = 1 << (STRTAB_SPLIT + LOG_2(STRTAB_STE_DWORDS) + 3);
 	strtab = &cfg->strtab[(sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS];
 
 	desc->span = STRTAB_SPLIT + 1;
-	desc->l2ptr = dmam_alloc_coherent(smmu->dev, size, &desc->l2ptr_dma,
-					  GFP_KERNEL | __GFP_ZERO);
+
+	alignment = 1 << ((5 + (desc->span - 1)));
+	desc->l2ptr = _xzalloc(size, alignment);
+	desc->l2ptr_dma = virt_to_maddr(desc->l2ptr);
+
 	if (!desc->l2ptr) {
 		dev_err(smmu->dev,
 			"failed to allocate l2 stream table for SID %u\n",
@@ -1158,7 +1418,7 @@ static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid)
 }
 
 /* IRQ and event handlers */
-static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
+static void arm_smmu_evtq_thread(int irq, void *dev, struct cpu_user_regs *regs)
 {
 	int i;
 	struct arm_smmu_device *smmu = dev;
@@ -1186,7 +1446,6 @@ static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
 
 	/* Sync our overflow flag, as we believe we're up to speed */
 	q->cons = Q_OVF(q, q->prod) | Q_WRP(q, q->cons) | Q_IDX(q, q->cons);
-	return IRQ_HANDLED;
 }
 
 static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)
@@ -1203,7 +1462,7 @@ static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)
 
 	dev_info(smmu->dev, "unexpected PRI request received:\n");
 	dev_info(smmu->dev,
-		 "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016llx\n",
+		 "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016lx\n",
 		 sid, ssid, grpid, last ? "L" : "",
 		 evt[0] & PRIQ_0_PERM_PRIV ? "" : "un",
 		 evt[0] & PRIQ_0_PERM_READ ? "R" : "",
@@ -1227,7 +1486,7 @@ static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)
 	}
 }
 
-static irqreturn_t arm_smmu_priq_thread(int irq, void *dev)
+static void arm_smmu_priq_thread(int irq, void *dev, struct cpu_user_regs *regs)
 {
 	struct arm_smmu_device *smmu = dev;
 	struct arm_smmu_queue *q = &smmu->priq.q;
@@ -1243,18 +1502,16 @@ static irqreturn_t arm_smmu_priq_thread(int irq, void *dev)
 
 	/* Sync our overflow flag, as we believe we're up to speed */
 	q->cons = Q_OVF(q, q->prod) | Q_WRP(q, q->cons) | Q_IDX(q, q->cons);
-	return IRQ_HANDLED;
 }
 
-static irqreturn_t arm_smmu_cmdq_sync_handler(int irq, void *dev)
+static void arm_smmu_cmdq_sync_handler(int irq, void *dev, struct cpu_user_regs *regs)
 {
 	/* We don't actually use CMD_SYNC interrupts for anything */
-	return IRQ_HANDLED;
 }
 
 static int arm_smmu_device_disable(struct arm_smmu_device *smmu);
 
-static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
+static void arm_smmu_gerror_handler(int irq, void *dev, struct cpu_user_regs *regs)
 {
 	u32 gerror, gerrorn, active;
 	struct arm_smmu_device *smmu = dev;
@@ -1264,7 +1521,7 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
 
 	active = gerror ^ gerrorn;
 	if (!(active & GERROR_ERR_MASK))
-		return IRQ_NONE; /* No errors pending */
+		return; /* No errors pending */
 
 	dev_warn(smmu->dev,
 		 "unexpected global error reported (0x%08x), this could be serious\n",
@@ -1286,7 +1543,7 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
 
 	if (active & GERROR_MSI_CMDQ_ABT_ERR) {
 		dev_warn(smmu->dev, "CMDQ MSI write aborted\n");
-		arm_smmu_cmdq_sync_handler(irq, smmu->dev);
+		arm_smmu_cmdq_sync_handler(irq, smmu->dev, NULL);
 	}
 
 	if (active & GERROR_PRIQ_ABT_ERR)
@@ -1299,7 +1556,6 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
 		arm_smmu_cmdq_skip_err(smmu);
 
 	writel(gerror, smmu->base + ARM_SMMU_GERRORN);
-	return IRQ_HANDLED;
 }
 
 /* IO_PGTABLE API */
@@ -1311,11 +1567,13 @@ static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu)
 	arm_smmu_cmdq_issue_cmd(smmu, &cmd);
 }
 
+#if 0 /*Xen: Unused function */
 static void arm_smmu_tlb_sync(void *cookie)
 {
 	struct arm_smmu_domain *smmu_domain = cookie;
 	__arm_smmu_tlb_sync(smmu_domain->smmu);
 }
+#endif
 
 static void arm_smmu_tlb_inv_context(void *cookie)
 {
@@ -1336,6 +1594,7 @@ static void arm_smmu_tlb_inv_context(void *cookie)
 	__arm_smmu_tlb_sync(smmu);
 }
 
+#if 0 /*Xen: Unused functionality */
 static void arm_smmu_tlb_inv_range_nosync(unsigned long iova, size_t size,
 					  size_t granule, bool leaf, void *cookie)
 {
@@ -1362,7 +1621,7 @@ static void arm_smmu_tlb_inv_range_nosync(unsigned long iova, size_t size,
 	} while (size -= granule);
 }
 
-static const struct iommu_gather_ops arm_smmu_gather_ops = {
+static struct iommu_gather_ops arm_smmu_gather_ops = {
 	.tlb_flush_all	= arm_smmu_tlb_inv_context,
 	.tlb_add_flush	= arm_smmu_tlb_inv_range_nosync,
 	.tlb_sync	= arm_smmu_tlb_sync,
@@ -1380,6 +1639,11 @@ static bool arm_smmu_capable(enum iommu_cap cap)
 		return false;
 	}
 }
+#endif
+/* Xen: Stub out DMA domain related functions */
+#define iommu_get_dma_cookie(dom) 0
+#define iommu_put_dma_cookie(dom) 0
+
 
 static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
 {
@@ -1410,6 +1674,7 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
 	return &smmu_domain->domain;
 }
 
+#if 0
 static int arm_smmu_bitmap_alloc(unsigned long *map, int span)
 {
 	int idx, size = 1 << span;
@@ -1427,36 +1692,20 @@ static void arm_smmu_bitmap_free(unsigned long *map, int idx)
 {
 	clear_bit(idx, map);
 }
+#endif
 
 static void arm_smmu_domain_free(struct iommu_domain *domain)
 {
 	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
-	struct arm_smmu_device *smmu = smmu_domain->smmu;
-
-	iommu_put_dma_cookie(domain);
-	free_io_pgtable_ops(smmu_domain->pgtbl_ops);
-
-	/* Free the CD and ASID, if we allocated them */
-	if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
-		struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
-
-		if (cfg->cdptr) {
-			dmam_free_coherent(smmu_domain->smmu->dev,
-					   CTXDESC_CD_DWORDS << 3,
-					   cfg->cdptr,
-					   cfg->cdptr_dma);
-
-			arm_smmu_bitmap_free(smmu->asid_map, cfg->cd.asid);
-		}
-	} else {
-		struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
-		if (cfg->vmid)
-			arm_smmu_bitmap_free(smmu->vmid_map, cfg->vmid);
-	}
+	/*
+	 * Xen: Remove the free functions that are not used and code related
+	 * to S1 translation. We just need to free the domain here.
+	 */
 
 	kfree(smmu_domain);
 }
 
+#if 0
 static int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain,
 				       struct io_pgtable_cfg *pgtbl_cfg)
 {
@@ -1488,33 +1737,30 @@ out_free_asid:
 	arm_smmu_bitmap_free(smmu->asid_map, asid);
 	return ret;
 }
+#endif
 
-static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain,
-				       struct io_pgtable_cfg *pgtbl_cfg)
+static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain)
 {
-	int vmid;
-	struct arm_smmu_device *smmu = smmu_domain->smmu;
 	struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
 
-	vmid = arm_smmu_bitmap_alloc(smmu->vmid_map, smmu->vmid_bits);
-	if (vmid < 0)
-		return vmid;
+	/* Xen: Set the values as needed */
 
-	cfg->vmid	= (u16)vmid;
-	cfg->vttbr	= pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
-	cfg->vtcr	= pgtbl_cfg->arm_lpae_s2_cfg.vtcr;
+	cfg->vmid	= cfg->domain->arch.p2m.vmid;
+	cfg->vttbr	= page_to_maddr(cfg->domain->arch.p2m.root);
+	cfg->vtcr	= READ_SYSREG32(VTCR_EL2);
 	return 0;
 }
 
 static int arm_smmu_domain_finalise(struct iommu_domain *domain)
 {
 	int ret;
+#if 0	/* Xen: pgtbl_cfg not needed. So modify the function as needed */
 	unsigned long ias, oas;
 	enum io_pgtable_fmt fmt;
 	struct io_pgtable_cfg pgtbl_cfg;
 	struct io_pgtable_ops *pgtbl_ops;
-	int (*finalise_stage_fn)(struct arm_smmu_domain *,
-				 struct io_pgtable_cfg *);
+	int (*finalise_stage_fn)(struct arm_smmu_domain *);
+#endif
 	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
 	struct arm_smmu_device *smmu = smmu_domain->smmu;
 
@@ -1529,6 +1775,7 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain)
 	if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2))
 		smmu_domain->stage = ARM_SMMU_DOMAIN_S1;
 
+#if 0
 	switch (smmu_domain->stage) {
 	case ARM_SMMU_DOMAIN_S1:
 		ias = VA_BITS;
@@ -1563,10 +1810,11 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain)
 	domain->geometry.aperture_end = (1UL << ias) - 1;
 	domain->geometry.force_aperture = true;
 	smmu_domain->pgtbl_ops = pgtbl_ops;
-
 	ret = finalise_stage_fn(smmu_domain, &pgtbl_cfg);
 	if (ret < 0)
 		free_io_pgtable_ops(pgtbl_ops);
+#endif
+	ret = arm_smmu_domain_finalise_s2(smmu_domain);
 
 	return ret;
 }
@@ -1660,7 +1908,8 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
 	} else if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
 		ste->s1_cfg = &smmu_domain->s1_cfg;
 		ste->s2_cfg = NULL;
-		arm_smmu_write_ctx_desc(smmu, ste->s1_cfg);
+		/*Xen: S1 configuratio not needed */
+		//arm_smmu_write_ctx_desc(smmu, ste->s1_cfg);
 	} else {
 		ste->s1_cfg = NULL;
 		ste->s2_cfg = &smmu_domain->s2_cfg;
@@ -1672,6 +1921,7 @@ out_unlock:
 	return ret;
 }
 
+#if 0
 static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
 			phys_addr_t paddr, size_t size, int prot)
 {
@@ -1737,11 +1987,13 @@ static int arm_smmu_match_node(struct device *dev, void *data)
 static
 struct arm_smmu_device *arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode)
 {
+	/* Xen; Function modified as needed for Xen.*/
 	struct device *dev = driver_find_device(&arm_smmu_driver.driver, NULL,
 						fwnode, arm_smmu_match_node);
 	put_device(dev);
 	return dev ? dev_get_drvdata(dev) : NULL;
 }
+#endif
 
 static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid)
 {
@@ -1752,8 +2004,9 @@ static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid)
 
 	return sid < limit;
 }
-
+#if 0
 static struct iommu_ops arm_smmu_ops;
+#endif
 
 static int arm_smmu_add_device(struct device *dev)
 {
@@ -1761,9 +2014,9 @@ static int arm_smmu_add_device(struct device *dev)
 	struct arm_smmu_device *smmu;
 	struct arm_smmu_master_data *master;
 	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
-	struct iommu_group *group;
 
-	if (!fwspec || fwspec->ops != &arm_smmu_ops)
+	/* Xen: fwspec->ops are not needed */
+	if (!fwspec)
 		return -ENODEV;
 	/*
 	 * We _can_ actually withstand dodgy bus code re-calling add_device()
@@ -1800,6 +2053,12 @@ static int arm_smmu_add_device(struct device *dev)
 		}
 	}
 
+#if 0
+/*
+ * Xen: Do not need an iommu group as the stream data is carried by the SMMU
+ * master device object
+ */
+
 	group = iommu_group_get_for_dev(dev);
 	if (!IS_ERR(group)) {
 		iommu_group_put(group);
@@ -1807,8 +2066,16 @@ static int arm_smmu_add_device(struct device *dev)
 	}
 
 	return PTR_ERR_OR_ZERO(group);
+#endif
+	return 0;
 }
 
+/*
+ * Xen: We can potentially support this function and destroy a device. This
+ * will be relevant for PCI hotplug. So, will be implemented as needed after
+ * passthrough support is available.
+ */
+#if 0
 static void arm_smmu_remove_device(struct device *dev)
 {
 	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
@@ -1944,7 +2211,7 @@ static struct iommu_ops arm_smmu_ops = {
 	.put_resv_regions	= arm_smmu_put_resv_regions,
 	.pgsize_bitmap		= -1UL, /* Restricted during device attach */
 };
-
+#endif
 /* Probing and initialisation functions */
 static int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
 				   struct arm_smmu_queue *q,
@@ -1954,7 +2221,12 @@ static int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,
 {
 	size_t qsz = ((1 << q->max_n_shift) * dwords) << 3;
 
-	q->base = dmam_alloc_coherent(smmu->dev, qsz, &q->base_dma, GFP_KERNEL);
+	/* The SMMU cache coherency property is always set. Since we are sharing the CPU translation tables
+	 * just make a regular allocation.
+	 */
+	q->base = _xzalloc(qsz, sizeof(void *));
+	q->base_dma = virt_to_maddr(q->base);
+
 	if (!q->base) {
 		dev_err(smmu->dev, "failed to allocate queue (0x%zx bytes)\n",
 			qsz);
@@ -2025,10 +2297,11 @@ static int arm_smmu_init_strtab_2lvl(struct arm_smmu_device *smmu)
 	void *strtab;
 	u64 reg;
 	u32 size, l1size;
+	u32 alignment;
 	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
 
 	/* Calculate the L1 size, capped to the SIDSIZE. */
-	size = STRTAB_L1_SZ_SHIFT - (ilog2(STRTAB_L1_DESC_DWORDS) + 3);
+	size = STRTAB_L1_SZ_SHIFT - (LOG_2(STRTAB_L1_DESC_DWORDS) + 3);
 	size = min(size, smmu->sid_bits - STRTAB_SPLIT);
 	cfg->num_l1_ents = 1 << size;
 
@@ -2039,8 +2312,13 @@ static int arm_smmu_init_strtab_2lvl(struct arm_smmu_device *smmu)
 			 size, smmu->sid_bits);
 
 	l1size = cfg->num_l1_ents * (STRTAB_L1_DESC_DWORDS << 3);
-	strtab = dmam_alloc_coherent(smmu->dev, l1size, &cfg->strtab_dma,
-				     GFP_KERNEL | __GFP_ZERO);
+
+	alignment = max_t(u32, cfg->num_l1_ents, 64);
+	//strtab = _xzalloc(l1size, sizeof(void *));
+	strtab = _xzalloc(l1size, l1size);
+	cfg->strtab_dma = virt_to_maddr(strtab);
+
+	//dsb(sy);
 	if (!strtab) {
 		dev_err(smmu->dev,
 			"failed to allocate l1 stream table (%u bytes)\n",
@@ -2068,8 +2346,9 @@ static int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu)
 	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
 
 	size = (1 << smmu->sid_bits) * (STRTAB_STE_DWORDS << 3);
-	strtab = dmam_alloc_coherent(smmu->dev, size, &cfg->strtab_dma,
-				     GFP_KERNEL | __GFP_ZERO);
+	strtab = _xzalloc(size, size);
+	cfg->strtab_dma = virt_to_maddr(strtab);
+
 	if (!strtab) {
 		dev_err(smmu->dev,
 			"failed to allocate linear stream table (%u bytes)\n",
@@ -2152,6 +2431,8 @@ static int arm_smmu_update_gbpa(struct arm_smmu_device *smmu, u32 set, u32 clr)
 					  1, ARM_SMMU_POLL_TIMEOUT_US);
 }
 
+/* Xen: There is no MSI support as yet */
+#if 0
 static void arm_smmu_free_msis(void *data)
 {
 	struct device *dev = data;
@@ -2217,7 +2498,7 @@ static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
 	/* Add callback to free MSIs on teardown */
 	devm_add_action(dev, arm_smmu_free_msis, dev);
 }
-
+#endif
 static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
 {
 	int ret, irq;
@@ -2231,31 +2512,31 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
 		return ret;
 	}
 
-	arm_smmu_setup_msis(smmu);
+	//arm_smmu_setup_msis(smmu);
 
 	/* Request interrupt lines */
 	irq = smmu->evtq.q.irq;
 	if (irq) {
-		ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
-						arm_smmu_evtq_thread,
-						IRQF_ONESHOT,
-						"arm-smmu-v3-evtq", smmu);
+		irq_set_type(irq, IRQ_TYPE_EDGE_BOTH);
+		ret = request_irq(irq, arm_smmu_evtq_thread,
+						0, "arm-smmu-v3-evtq", smmu);
 		if (ret < 0)
 			dev_warn(smmu->dev, "failed to enable evtq irq\n");
 	}
 
 	irq = smmu->cmdq.q.irq;
 	if (irq) {
-		ret = devm_request_irq(smmu->dev, irq,
-				       arm_smmu_cmdq_sync_handler, 0,
-				       "arm-smmu-v3-cmdq-sync", smmu);
+		irq_set_type(irq, IRQ_TYPE_EDGE_BOTH);
+		ret = request_irq(irq, arm_smmu_cmdq_sync_handler,
+				0, "arm-smmu-v3-cmdq-sync", smmu);
 		if (ret < 0)
 			dev_warn(smmu->dev, "failed to enable cmdq-sync irq\n");
 	}
 
 	irq = smmu->gerr_irq;
 	if (irq) {
-		ret = devm_request_irq(smmu->dev, irq, arm_smmu_gerror_handler,
+		irq_set_type(irq, IRQ_TYPE_EDGE_BOTH);
+		ret = request_irq(irq, arm_smmu_gerror_handler,
 				       0, "arm-smmu-v3-gerror", smmu);
 		if (ret < 0)
 			dev_warn(smmu->dev, "failed to enable gerror irq\n");
@@ -2263,12 +2544,13 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
 
 	if (smmu->features & ARM_SMMU_FEAT_PRI) {
 		irq = smmu->priq.q.irq;
+		irq_set_type(irq, IRQ_TYPE_EDGE_BOTH);
 		if (irq) {
-			ret = devm_request_threaded_irq(smmu->dev, irq, NULL,
-							arm_smmu_priq_thread,
-							IRQF_ONESHOT,
-							"arm-smmu-v3-priq",
-							smmu);
+			ret = request_irq(irq,
+					  arm_smmu_priq_thread,
+					  0,
+					  "arm-smmu-v3-priq",
+					  smmu);
 			if (ret < 0)
 				dev_warn(smmu->dev,
 					 "failed to enable priq irq\n");
@@ -2400,7 +2682,7 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass)
 
 
 	/* Enable the SMMU interface, or ensure bypass */
-	if (!bypass || disable_bypass) {
+	if (!bypass) {
 		enables |= CR0_SMMUEN;
 	} else {
 		ret = arm_smmu_update_gbpa(smmu, 0, GBPA_ABORT);
@@ -2488,8 +2770,11 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
 		smmu->features |= ARM_SMMU_FEAT_STALLS;
 	}
 
+#if 0/* Xen: Do not enable Stage 1 translations */
+
 	if (reg & IDR0_S1P)
 		smmu->features |= ARM_SMMU_FEAT_TRANS_S1;
+#endif
 
 	if (reg & IDR0_S2P)
 		smmu->features |= ARM_SMMU_FEAT_TRANS_S2;
@@ -2562,10 +2847,12 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
 	if (reg & IDR5_GRAN4K)
 		smmu->pgsize_bitmap |= SZ_4K | SZ_2M | SZ_1G;
 
+#if 0 /* Xen: SMMU ops do not have a pgsize_bitmap member for Xen */
 	if (arm_smmu_ops.pgsize_bitmap == -1UL)
 		arm_smmu_ops.pgsize_bitmap = smmu->pgsize_bitmap;
 	else
 		arm_smmu_ops.pgsize_bitmap |= smmu->pgsize_bitmap;
+#endif
 
 	/* Output address size */
 	switch (reg & IDR5_OAS_MASK << IDR5_OAS_SHIFT) {
@@ -2592,10 +2879,12 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
 		smmu->oas = 48;
 	}
 
+#if 0 /* Xen: There is no support for DMA mask */
 	/* Set the DMA mask for our table walker */
 	if (dma_set_mask_and_coherent(smmu->dev, DMA_BIT_MASK(smmu->oas)))
 		dev_warn(smmu->dev,
 			 "failed to set DMA mask for table walker\n");
+#endif
 
 	smmu->ias = max(smmu->ias, smmu->oas);
 
@@ -2612,7 +2901,8 @@ static int arm_smmu_device_acpi_probe(struct platform_device *pdev,
 	struct device *dev = smmu->dev;
 	struct acpi_iort_node *node;
 
-	node = *(struct acpi_iort_node **)dev_get_platdata(dev);
+	/* Xen: Modification to get iort_node */
+	node = (struct acpi_iort_node *)dev->acpi_node;
 
 	/* Retrieve SMMUv3 specific data */
 	iort_smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
@@ -2633,7 +2923,7 @@ static inline int arm_smmu_device_acpi_probe(struct platform_device *pdev,
 static int arm_smmu_device_dt_probe(struct platform_device *pdev,
 				    struct arm_smmu_device *smmu)
 {
-	struct device *dev = &pdev->dev;
+	struct device *dev = pdev;
 	u32 cells;
 	int ret = -EINVAL;
 
@@ -2646,8 +2936,8 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev,
 
 	parse_driver_options(smmu);
 
-	if (of_dma_is_coherent(dev->of_node))
-		smmu->features |= ARM_SMMU_FEAT_COHERENCY;
+	/* Xen: Set the COHERNECY feature */
+	smmu->features |= ARM_SMMU_FEAT_COHERENCY;
 
 	return ret;
 }
@@ -2656,9 +2946,10 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
 {
 	int irq, ret;
 	struct resource *res;
-	resource_size_t ioaddr;
+	/*Xen: Do not need to setup sysfs */
+	/* resource_size_t ioaddr;*/
 	struct arm_smmu_device *smmu;
-	struct device *dev = &pdev->dev;
+	struct device *dev = pdev;/* Xen: dev is ignored */
 	bool bypass;
 
 	smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
@@ -2674,7 +2965,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
 		dev_err(dev, "MMIO region too small (%pr)\n", res);
 		return -EINVAL;
 	}
-	ioaddr = res->start;
+	/*ioaddr = res->start;*/
 
 	smmu->base = devm_ioremap_resource(dev, res);
 	if (IS_ERR(smmu->base))
@@ -2719,13 +3010,15 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
 		return ret;
 
 	/* Record our private device structure */
-	platform_set_drvdata(pdev, smmu);
+	/* platform_set_drvdata(pdev, smmu); */
 
 	/* Reset the device */
 	ret = arm_smmu_device_reset(smmu, bypass);
 	if (ret)
 		return ret;
 
+/* Xen: Not creating an IOMMU device list for Xen */
+#if 0
 	/* And we're up. Go go go! */
 	ret = iommu_device_sysfs_add(&smmu->iommu, dev, NULL,
 				     "smmu3.%pa", &ioaddr);
@@ -2757,9 +3050,18 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
 		if (ret)
 			return ret;
 	}
+#endif
+	/*
+	 * Xen: Keep a list of all probed devices. This will be used to query
+	 * the smmu devices based on the fwnode.
+	 */
+	INIT_LIST_HEAD(&smmu->devices);
+	spin_lock(&arm_smmu_devices_lock);
+	list_add(&smmu->devices, &arm_smmu_devices);
+	spin_unlock(&arm_smmu_devices_lock);
 	return 0;
 }
-
+#if 0
 static int arm_smmu_device_remove(struct platform_device *pdev)
 {
 	struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
@@ -2767,6 +3069,9 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
 	arm_smmu_device_disable(smmu);
 	return 0;
 }
+#endif
+#define MODULE_DEVICE_TABLE(type, name)
+#define of_device_id dt_device_match
 
 static struct of_device_id arm_smmu_of_match[] = {
 	{ .compatible = "arm,smmu-v3", },
@@ -2774,6 +3079,7 @@ static struct of_device_id arm_smmu_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
 
+#if 0
 static struct platform_driver arm_smmu_driver = {
 	.driver	= {
 		.name		= "arm-smmu-v3",
@@ -2789,3 +3095,318 @@ IOMMU_OF_DECLARE(arm_smmuv3, "arm,smmu-v3", NULL);
 MODULE_DESCRIPTION("IOMMU API for ARM architected SMMUv3 implementations");
 MODULE_AUTHOR("Will Deacon <[email protected]>");
 MODULE_LICENSE("GPL v2");
+
+#endif
+/***** Start of Xen specific code *****/
+
+static int __must_check arm_smmu_iotlb_flush_all(struct domain *d)
+{
+	struct arm_smmu_xen_domain *smmu_domain = dom_iommu(d)->arch.priv;
+	struct iommu_domain *cfg;
+
+	spin_lock(&smmu_domain->lock);
+	list_for_each_entry(cfg, &smmu_domain->iommu_domains, 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);
+	return 0;
+}
+
+static int __must_check arm_smmu_iotlb_flush(struct domain *d,
+					     unsigned long gfn,
+					     unsigned int page_count)
+{
+	return arm_smmu_iotlb_flush_all(d);
+}
+
+static struct iommu_domain *arm_smmu_get_domain(struct domain *d,
+						struct device *dev)
+{
+	struct iommu_domain *domain;
+	struct arm_smmu_xen_domain *xen_domain;
+	struct arm_smmu_device *smmu;
+	struct arm_smmu_domain *smmu_domain;
+
+	xen_domain = dom_iommu(d)->arch.priv;
+
+	smmu = arm_smmu_get_by_fwnode(dev->iommu_fwspec->iommu_fwnode);
+	if (!smmu)
+		return NULL;
+
+	/*
+	 * Loop through the &xen_domain->contexts to locate a context
+	 * assigned to this SMMU
+	 */
+	list_for_each_entry(domain, &xen_domain->iommu_domains, list) {
+		smmu_domain = to_smmu_domain(domain);
+		if (smmu_domain->smmu == smmu)
+			return domain;

xen/drivers/passthrough/arm/smmu-v3.c IOMMU_OF_DECLARE(arm_smmuv3, "arm,smmu-v3", NULL); · line 3160

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +10 -0
+	}
+
+	return NULL;
+}
+
+static void arm_smmu_destroy_iommu_domain(struct iommu_domain *domain)
+{
+	list_del(&domain->list);
+	xfree(domain);
+}

The above function needs to call arm_smmu_domain_free(domain) instead of xfree(domain).

Quoted 253 lines
xen/drivers/passthrough/arm/smmu-v3.c +252 -0
+
+static int arm_smmu_assign_dev(struct domain *d, u8 devfn,
+			       struct device *dev, u32 flag)
+{
+	int ret = 0;
+	struct iommu_domain *domain;
+	struct arm_smmu_xen_domain *xen_domain;
+	struct arm_smmu_domain *arm_smmu;
+
+	xen_domain = dom_iommu(d)->arch.priv;
+
+	if (!dev->archdata.iommu) {
+		dev->archdata.iommu = xzalloc(struct arm_smmu_xen_device);
+		if (!dev->archdata.iommu)
+			return -ENOMEM;
+	}
+
+	ret = arm_smmu_add_device(dev);
+	if (ret)
+		return ret;
+
+	spin_lock(&xen_domain->lock);
+
+	/*
+	 * Check to see if an iommu_domain already exists for this xen domain
+	 * under the same SMMU
+	 */
+	domain = arm_smmu_get_domain(d, dev);
+	if (!domain) {
+
+		domain = arm_smmu_domain_alloc(IOMMU_DOMAIN_DMA);
+		if (!domain) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		arm_smmu = to_smmu_domain(domain);
+		arm_smmu->s2_cfg.domain = d;
+
+		/* Chain the new context to the domain */
+		list_add(&domain->list, &xen_domain->iommu_domains);
+
+	}
+
+	ret = arm_smmu_attach_dev(domain, dev);
+	if (ret) {
+		if (domain->ref.counter == 0)
+			arm_smmu_destroy_iommu_domain(domain);
+	} else {
+		atomic_inc(&domain->ref);
+	}
+
+out:
+	spin_unlock(&xen_domain->lock);
+	return ret;
+}
+
+static int arm_smmu_deassign_dev(struct domain *d, struct device *dev)
+{
+	struct iommu_domain *domain = arm_smmu_get_domain(d, dev);
+	struct arm_smmu_xen_domain *xen_domain;
+	struct arm_smmu_domain *arm_smmu = to_smmu_domain(domain);
+
+	xen_domain = dom_iommu(d)->arch.priv;
+
+	if (!arm_smmu || arm_smmu->s2_cfg.domain != d) {
+		dev_err(dev, " not attached to domain %d\n", d->domain_id);
+		return -ESRCH;
+	}
+
+	spin_lock(&xen_domain->lock);
+
+	arm_smmu_detach_dev(dev);
+	atomic_dec(&domain->ref);
+
+	if (domain->ref.counter == 0)
+		arm_smmu_destroy_iommu_domain(domain);
+
+	spin_unlock(&xen_domain->lock);
+
+
+
+	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 && t != hardware_domain)
+		return -EPERM;
+
+	if (t == s)
+		return 0;
+
+	ret = arm_smmu_deassign_dev(s, dev);
+	if (ret)
+		return ret;
+
+	if (t) {
+		/* No flags are defined for ARM. */
+		ret = arm_smmu_assign_dev(t, devfn, dev, 0);
+		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->iommu_domains);
+
+	dom_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 = dom_iommu(d)->arch.priv;
+
+	ASSERT(list_empty(&xen_domain->iommu_domains));
+	xfree(xen_domain);
+}
+
+static int __must_check 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(gfn), _mfn(mfn), 0, t);
+}
+
+static int __must_check 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;
+
+	return guest_physmap_remove_page(d, _gfn(gfn), _mfn(gfn), 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
+struct arm_smmu_device *arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode)
+{
+	struct arm_smmu_device *smmu = NULL;
+
+	spin_lock(&arm_smmu_devices_lock);
+	list_for_each_entry(smmu, &arm_smmu_devices, devices) {
+		if (smmu->dev->fwnode == fwnode)
+			break;
+	}
+	spin_unlock(&arm_smmu_devices_lock);
+
+	return smmu;
+}
+
+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_probe(dt_to_dev(dev));
+	if (rc)
+		return rc;
+
+	iommu_set_ops(&arm_smmu_iommu_ops);
+
+	return 0;
+}
+
+DT_DEVICE_START(smmuv3, "ARM SMMU V3", DEVICE_IOMMU)
+	.dt_match = arm_smmu_of_match,
+	.init = arm_smmu_dt_init,
+DT_DEVICE_END
+
+#ifdef CONFIG_ACPI
+/* Set up the IOMMU */
+static int __init arm_smmu_acpi_init(const void *data)
+{
+	int rc;
+	rc = arm_smmu_device_probe((struct device *)data);
+
+	if (rc)
+		return rc;
+
+	iommu_set_ops(&arm_smmu_iommu_ops);
+	return 0;
+}
+
+ACPI_DEVICE_START(asmmuv3, "ARM SMMU V3", DEVICE_IOMMU)
+	.class_type = ACPI_IORT_NODE_SMMU_V3,
+	.init = arm_smmu_acpi_init,
+ACPI_DEVICE_END
+
+#endif
Signature · 6 lines
 Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#26 lore raw

Re: [RFC v2 7/7] xen/iommu: smmu-v3: Add Xen specific code to enable the ported driver

In reply to Sameer Goel

Hi Sameer,

Given this is all Arm specific. I am not sure why people like Andrew, Jan have been added.

Please use scripts/get_maintainers to find the list of maintainers per patches and avoid to CC all of them on each patches.

Quoted 10 lines · Sameer Goel

This driver follows an approach similar to smmu driver. The intent here is to reuse as much Linux code as possible.
- Glue code has been introduced to bridge the API calls.
- Called Linux functions from the Xen IOMMU function calls.
- Xen modifications are preceded by /*Xen: comment */

---
  xen/drivers/passthrough/arm/Makefile  |   1 +
  xen/drivers/passthrough/arm/smmu-v3.c | 853 +++++++++++++++++++++++++++++-----

This is based on an old SMMUv3 version and I have been told there are some changes may benefits Xen (such as increasing the timeout for sync) and some optimisations also exist on the ML and will be queued soon.

So maybe you want to re-sync at least to master.

xen/drivers/passthrough/arm/Makefile line 3

Quoted 10 lines
  2 files changed, 738 insertions(+), 116 deletions(-)
xen/drivers/passthrough/arm/Makefile +1 -0
index f4cd26e..57a6da6 100644
@@ -1,2 +1,3 @@
  obj-y += iommu.o
  obj-y += smmu.o
+obj-y += smmu-v3.o

Do we want SMMUv3 to be built on Arm32? Maybe we should introduce a new Kconfig to let the user select.

Quoted 30 lines
xen/drivers/passthrough/arm/smmu-v3.c +9 -11
index 380969a..8f3b43d 100644
@@ -18,28 +18,266 @@
   * Author: Will Deacon <[email protected]>
   *
   * This driver is powered by bad coffee and bombay mix.
+ *
+ *
+ * Based on Linux drivers/iommu/arm-smmu-v3.c
+ * => commit bdf95923086fb359ccb44c815724c3ace1611c90
+ *
+ * Xen modifications:
+ * Sameer Goel <[email protected]>
+ * Copyright (C) 2017, The Linux Foundation, All rights reserved.
+ *
   */
 
-#include <linux/acpi.h>
-#include <linux/acpi_iort.h>
-#include <linux/delay.h>
-#include <linux/dma-iommu.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/iommu.h>
-#include <linux/iopoll.h>
-#include <linux/module.h>
-#include <linux/msi.h>
-#include <linux/of.h>

xen/drivers/passthrough/arm/smmu-v3.c line 32

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -9
-#include <linux/of_address.h>
-#include <linux/of_iommu.h>
-#include <linux/of_platform.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-
-#include <linux/amba/bus.h>
-
-#include "io-pgtable.h"
+#include <xen/config.h>

This is not necessary.

Quoted 6 lines
xen/drivers/passthrough/arm/smmu-v3.c +6 -0
+#include <xen/delay.h>
+#include <xen/errno.h>
+#include <xen/err.h>
+#include <xen/irq.h>
+#include <xen/lib.h>
+#include <xen/list.h>

xen/drivers/passthrough/arm/smmu-v3.c line 48

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +10 -0
+#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>
+#include <xen/acpi.h>

Please order the includes alphabetically with xen/* first then asm/*

Quoted 6 lines
xen/drivers/passthrough/arm/smmu-v3.c +6 -0
+
+typedef paddr_t phys_addr_t;
+typedef paddr_t dma_addr_t;
+
+/* Alias to Xen device tree helpers */
+#define device_node dt_device_node

xen/drivers/passthrough/arm/smmu-v3.c line 64

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +10 -0
+#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
+#define mutex spinlock_t
+#define mutex_init spin_lock_init
+#define mutex_lock spin_lock
+#define mutex_unlock spin_unlock

mutex and spinlock are not the same. The former is sleeping whilst the later is not.

Can you please explain why this is fine and possibly add that in a comment?

xen/drivers/passthrough/arm/smmu-v3.c line 71

Quoted 7 lines
xen/drivers/passthrough/arm/smmu-v3.c +7 -0
+
+/* Xen: Helpers to get device MMIO and IRQs */
+struct resource {
+	u64 addr;
+	u64 size;
+	unsigned int type;
+};

Likely we want a compat header for defining Linux helpers. This would avoid replicating it everywhere.

Quoted 36 lines
xen/drivers/passthrough/arm/smmu-v3.c +36 -0
+
+#define resource_size(res) ((res)->size)
+
+#define platform_device device
+
+#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;
+	struct acpi_iort_node *iort_node;
+	struct acpi_iort_smmu_v3 *node_smmu_data;
+	int ret = 0;
+
+	res.type = type;
+
+	switch (type) {
+	case IORESOURCE_MEM:
+		if (pdev->type == DEV_ACPI) {
+			ret = 1;
+			iort_node = pdev->acpi_node;
+			node_smmu_data =
+				(struct acpi_iort_smmu_v3 *)iort_node->node_data;
+
+			if (node_smmu_data != NULL) {
+				res.addr = node_smmu_data->base_address;
+				res.size = SZ_128K;
+				ret = 0;

xen/drivers/passthrough/arm/smmu-v3.c line 117

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +10 -0
+			}
+		} else {
+			ret = dt_device_get_address(dev_to_dt(pdev), num,
+						    &res.addr, &res.size);
+		}
+
+		return ((ret) ? NULL : &res);
+
+	case IORESOURCE_IRQ:
+		ret = platform_get_irq(dev_to_dt(pdev), num);

No IRQ for ACPI?

Quoted 51 lines
xen/drivers/passthrough/arm/smmu-v3.c +51 -0
+
+		if (ret < 0)
+			return NULL;
+
+		res.addr = ret;
+		res.size = 1;
+
+		return &res;
+
+	default:
+		return NULL;
+	}
+}
+
+static int platform_get_irq_byname(struct platform_device *pdev, const char *name)
+{
+	const struct dt_property *dtprop;
+	struct acpi_iort_node *iort_node;
+	struct acpi_iort_smmu_v3 *node_smmu_data;
+	int ret = 0;
+
+	if (pdev->type == DEV_ACPI) {
+		iort_node = pdev->acpi_node;
+		node_smmu_data = (struct acpi_iort_smmu_v3 *)iort_node->node_data;
+
+		if (node_smmu_data != NULL) {
+			if (!strcmp(name, "eventq"))
+				ret = node_smmu_data->event_gsiv;
+			else if (!strcmp(name, "priq"))
+				ret = node_smmu_data->pri_gsiv;
+			else if (!strcmp(name, "cmdq-sync"))
+				ret = node_smmu_data->sync_gsiv;
+			else if (!strcmp(name, "gerror"))
+				ret = node_smmu_data->gerr_gsiv;
+			else
+				ret = -EINVAL;
+		}
+	} else {
+		dtprop = dt_find_property(dev_to_dt(pdev), "interrupt-names", NULL);
+		if (!dtprop)
+			return -EINVAL;
+
+		if (!dtprop->value)
+			return -ENODATA;
+	}
+
+	return ret;
+}
+
+#define readx_poll_timeout(op, addr, val, cond, sleep_us, timeout_us) \
+({ \

xen/drivers/passthrough/arm/smmu-v3.c line 178

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +10 -0
+	s_time_t deadline = NOW() + MICROSECS(timeout_us); \
+	for (;;) { \
+		(val) = op(addr); \
+		if (cond) \
+			break; \
+		if (NOW() > deadline) { \
+			(val) = op(addr); \
+			break; \
+		} \
+		cpu_relax(); \

I don't think calling cpu_relax() is correct here.

Quoted 34 lines
xen/drivers/passthrough/arm/smmu-v3.c +34 -0
+		udelay(sleep_us); \
+	} \
+	(cond) ? 0 : -ETIMEDOUT; \
+})
+
+#define readl_relaxed_poll_timeout(addr, val, cond, delay_us, timeout_us) \
+	readx_poll_timeout(readl_relaxed, addr, val, cond, delay_us, timeout_us)
+
+/* Xen: Helpers for IRQ functions */
+#define request_irq(irq, func, flags, name, dev) request_irq(irq, flags, func, name, dev)
+#define free_irq release_irq
+
+enum irqreturn {
+	IRQ_NONE	= (0 << 0),
+	IRQ_HANDLED	= (1 << 0),
+};
+
+typedef enum irqreturn irqreturn_t;
+
+/* Device logger functions */
+#define dev_print(dev, lvl, fmt, ...)						\
+	 printk(lvl "smmu: " fmt, ## __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_info(dev, fmt, ...) dev_print(dev, XENLOG_INFO, fmt, ## __VA_ARGS__)
+
+#define dev_err_ratelimited(dev, fmt, ...)					\
+	 dev_print(dev, XENLOG_ERR, fmt, ## __VA_ARGS__)
+
+#define dev_name(dev) dt_node_full_name(dev_to_dt(dev))
+

xen/drivers/passthrough/arm/smmu-v3.c line 222

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +10 -0
+/* 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)
+
+/* Compatibility defines */
+#undef WARN_ON
+#define WARN_ON(cond) (!!cond)

Why do you redefine WARN_ON?

xen/drivers/passthrough/arm/smmu-v3.c line 223

Quoted 1 line
xen/drivers/passthrough/arm/smmu-v3.c +1 -0
+#define WARN_ON_ONCE(cond) WARN_ON(cond)

Hmmm, can't we implement a common WARN_ON_ONCE?

Quoted 24 lines
xen/drivers/passthrough/arm/smmu-v3.c +24 -0
+
+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: Dummy iommu_domain */
+struct iommu_domain {

xen/drivers/passthrough/arm/smmu-v3.c line 257

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +10 -0
+	/* Runtime SMMU configuration for this iommu_domain */
+	struct arm_smmu_domain		*priv;
+	unsigned int			type;
+
+	atomic_t ref;
+	/* Used to link iommu_domain contexts for a same domain.
+	 * There is at least one per-SMMU to used by the domain.
+	 */
+	struct list_head		list;
+};

This is very similar to the SMMU version. Could we share some bits?

xen/drivers/passthrough/arm/smmu-v3.c line 270

Quoted 13 lines
xen/drivers/passthrough/arm/smmu-v3.c +13 -0
+/* Xen: Domain type definitions. Not really needed for Xen, defining to port
+ * Linux code as-is
+ */
+#define IOMMU_DOMAIN_UNMANAGED 0
+#define IOMMU_DOMAIN_DMA 1
+#define IOMMU_DOMAIN_IDENTITY 2
+
+/* Xen: Describes information required for a Xen domain */
+struct arm_smmu_xen_domain {
+	spinlock_t			lock;
+	/* List of iommu domains associated to this domain */
+	struct list_head		iommu_domains;
+};

Ditoo.

xen/drivers/passthrough/arm/smmu-v3.c line 280

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +10 -0
+
+/*
+ * Xen: Information about each device stored in dev->archdata.iommu
+ *
+ * The dev->archdata.iommu stores the iommu_domain (runtime configuration of
+ * the SMMU).
+ */
+struct arm_smmu_xen_device {
+	struct iommu_domain *domain;
+};

Ditto.

xen/drivers/passthrough/arm/smmu-v3.c line 653

Quoted 8 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -0
  /* MMIO registers */
  #define ARM_SMMU_IDR0			0x0
@@ -412,10 +650,12 @@
  #define MSI_IOVA_BASE			0x8000000
  #define MSI_IOVA_LENGTH			0x100000
 
+#if 0 /* Not applicable for Xen */

While the module_param_name() is not applicable in Xen, I don't see any reason to remove the variable.

Quoted 33 lines
xen/drivers/passthrough/arm/smmu-v3.c +5 -0
  static bool disable_bypass; >   module_param_named(disable_bypass, disable_bypass, bool, S_IRUGO);
  MODULE_PARM_DESC(disable_bypass,
  	"Disable bypass streams such that incoming transactions from devices that are not attached to an iommu domain will report an abort back to the device and will not be allowed to pass through the SMMU.");
+#endif
 
  enum pri_resp {
  	PRI_RESP_DENY,
@@ -423,6 +663,7 @@ enum pri_resp {
  	PRI_RESP_SUCC,
  };
 
+#if 0 /* Xen: No MSI support in this iteration */
  enum arm_smmu_msi_index {
  	EVTQ_MSI_INDEX,
  	GERROR_MSI_INDEX,
@@ -447,6 +688,7 @@ static phys_addr_t arm_smmu_msi_cfg[ARM_SMMU_MAX_MSIS][3] = {
  		ARM_SMMU_PRIQ_IRQ_CFG2,
  	},
  };
+#endif
 
  struct arm_smmu_cmdq_ent {
  	/* Common fields */
@@ -551,6 +793,8 @@ struct arm_smmu_s2_cfg {
  	u16				vmid;
  	u64				vttbr;
  	u64				vtcr;
+	/* Xen: Domain associated to this configuration */
+	struct domain			*domain;
  };
 
  struct arm_smmu_strtab_ent {

xen/drivers/passthrough/arm/smmu-v3.c struct arm_smmu_device { · line 870

Quoted 5 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -1
@@ -623,9 +867,20 @@ struct arm_smmu_device {
  	struct arm_smmu_strtab_cfg	strtab_cfg;
 
  	/* IOMMU core code handle */
-	struct iommu_device		iommu;
+	//struct iommu_device		iommu;

#if 0 but no // please.

Quoted 26 lines
xen/drivers/passthrough/arm/smmu-v3.c +12 -1
+
+	/* Xen: Need to keep a list of SMMU devices */
+	struct list_head                devices;
  };
 
+/* Xen: Keep a list of devices associated with this driver */
+static DEFINE_SPINLOCK(arm_smmu_devices_lock);
+static LIST_HEAD(arm_smmu_devices);
+/* Xen: Helper for finding a device using fwnode */
+static
+struct arm_smmu_device *arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode);
+
+
  /* SMMU private data for each master */
  struct arm_smmu_master_data {
  	struct arm_smmu_device		*smmu;
@@ -642,7 +897,7 @@ enum arm_smmu_domain_stage {
 
  struct arm_smmu_domain {
  	struct arm_smmu_device		*smmu;
-	struct mutex			init_mutex; /* Protects smmu pointer */
+	mutex			init_mutex; /* Protects smmu pointer */
 
  	struct io_pgtable_ops		*pgtbl_ops;
  	spinlock_t			pgtbl_lock;

xen/drivers/passthrough/arm/smmu-v3.c static void queue_inc_prod(struct arm_smmu_queue *q) · line 995

Quoted 5 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -1
@@ -737,15 +992,16 @@ static void queue_inc_prod(struct arm_smmu_queue *q)
   */
  static int queue_poll_cons(struct arm_smmu_queue *q, bool drain, bool wfe)
  {
-	ktime_t timeout = ktime_add_us(ktime_get(), ARM_SMMU_POLL_TIMEOUT_US);
+	s_time_t deadline = NOW() + MICROSECS(ARM_SMMU_POLL_TIMEOUT_US);

Please introduce proper wrappers to avoid the modification of the code.

xen/drivers/passthrough/arm/smmu-v3.c static void queue_inc_prod(struct arm_smmu_queue *q) · line 999

Quoted 5 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -1
  	while (queue_sync_cons(q), (drain ? !queue_empty(q) : queue_full(q))) {
-		if (ktime_compare(ktime_get(), timeout) > 0)
+
+		if (NOW() > deadline)

Ditto.

xen/drivers/passthrough/arm/smmu-v3.c static void queue_inc_prod(struct arm_smmu_queue *q) · line 1002

Quoted 4 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -1
  			return -ETIMEDOUT;
 
-		if (wfe) {
+		if (wfe)

Please avoid to drop {

xen/drivers/passthrough/arm/smmu-v3.c static void queue_inc_prod(struct arm_smmu_queue *q) · line 748

Quoted 2 lines
xen/drivers/passthrough/arm/smmu-v3.c +0 -1
  			wfe();
-		} else {

Ditto.

xen/drivers/passthrough/arm/smmu-v3.c static void queue_inc_prod(struct arm_smmu_queue *q) · line 1005

Quoted 2 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -0
+		else {
  			cpu_relax();

Hmmm I now see why you added cpu_relax() at the top. Well, on Xen cpu_relax is just a barrier. On Linux it is used to yield.

And that bit is worrying me. The Linux code will allow context switching to another tasks if the code is taking too much time.

Xen is not preemptible, so is it fine?

xen/drivers/passthrough/arm/smmu-v3.c static void arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu, · line 1190

Quoted 8 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -1
  			udelay(1);
  		}
@@ -931,7 +1187,7 @@ static void arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
  		dev_err_ratelimited(smmu->dev, "CMD_SYNC timeout\n");
  	spin_unlock_irqrestore(&smmu->cmdq.lock, flags);
  }
-
+#if 0

Please avoid dropping newline and explain why the #if 0.

xen/drivers/passthrough/arm/smmu-v3.c static void arm_smmu_write_ctx_desc(struct arm_smmu_device *smmu, · line 1233

Quoted 9 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -1
  /* Context descriptor manipulation functions */
  static u64 arm_smmu_cpu_tcr_to_cd(u64 tcr)
  {
@@ -974,7 +1230,7 @@ static void arm_smmu_write_ctx_desc(struct arm_smmu_device *smmu,
 
  	cfg->cdptr[3] = cpu_to_le64(cfg->cd.mair << CTXDESC_CD_3_MAIR_SHIFT);
  }
-
+#endif

Ditto for the newline.

xen/drivers/passthrough/arm/smmu-v3.c static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid, · line 1303

Quoted 9 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -1
  /* Stream table manipulation functions */
  static void
  arm_smmu_write_strtab_l1_desc(__le64 *dst, struct arm_smmu_strtab_l1_desc *desc)
@@ -1044,7 +1300,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
  			ste_live = true;
  			break;
  		case STRTAB_STE_0_CFG_ABORT:
-			if (disable_bypass)
+			//No bypass override for Xen

Why no leaving the variable on top with a comment. This would avoid such change.

xen/drivers/passthrough/arm/smmu-v3.c static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid, · line 1315

Quoted 9 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -1
  				break;
  		default:
  			BUG(); /* STE corruption */
@@ -1056,7 +1312,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
 
  	/* Bypass/fault */
  	if (!ste->assigned || !(ste->s1_cfg || ste->s2_cfg)) {
-		if (!ste->assigned && disable_bypass)
+		if (!ste->assigned)

Ditto.

Quoted 4 lines
xen/drivers/passthrough/arm/smmu-v3.c +0 -0
  			val |= STRTAB_STE_0_CFG_ABORT;
  		else
  			val |= STRTAB_STE_0_CFG_BYPASS;

xen/drivers/passthrough/arm/smmu-v3.c static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid) · line 1399

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -1
@@ -1135,16 +1391,20 @@ static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid)
  	void *strtab;
  	struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
  	struct arm_smmu_strtab_l1_desc *desc = &cfg->l1_desc[sid >> STRTAB_SPLIT];
+	u32 alignment = 0;
 
  	if (desc->l2ptr)
  		return 0;
 
-	size = 1 << (STRTAB_SPLIT + ilog2(STRTAB_STE_DWORDS) + 3);
+	size = 1 << (STRTAB_SPLIT + LOG_2(STRTAB_STE_DWORDS) + 3);

I would prefer if you introduce ilog2.

xen/drivers/passthrough/arm/smmu-v3.c static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid) · line 1404

Quoted 7 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -2
  	strtab = &cfg->strtab[(sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS];
 
  	desc->span = STRTAB_SPLIT + 1;
-	desc->l2ptr = dmam_alloc_coherent(smmu->dev, size, &desc->l2ptr_dma,
-					  GFP_KERNEL | __GFP_ZERO);
+
+	alignment = 1 << ((5 + (desc->span - 1)));

Do you mind explaining the 5? Also, does the shift will always be < 32?

xen/drivers/passthrough/arm/smmu-v3.c static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid) · line 1406

Quoted 2 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -0
+	desc->l2ptr = _xzalloc(size, alignment);
+	desc->l2ptr_dma = virt_to_maddr(desc->l2ptr);

_xzalloc can fail and virt_to_maddr will result to a panic.

xen/drivers/passthrough/arm/smmu-v3.c static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid) · line 1421

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -1
+
  	if (!desc->l2ptr) {
  		dev_err(smmu->dev,
  			"failed to allocate l2 stream table for SID %u\n",
@@ -1158,7 +1418,7 @@ static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid)
  }
 
  /* IRQ and event handlers */
-static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
+static void arm_smmu_evtq_thread(int irq, void *dev, struct cpu_user_regs *regs)

Could you please introduce a wrapper instead as it was done in smmu.c?

Quoted 12 lines
xen/drivers/passthrough/arm/smmu-v3.c +0 -1
  {
  	int i;
  	struct arm_smmu_device *smmu = dev;
@@ -1186,7 +1446,6 @@ static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
 
  	/* Sync our overflow flag, as we believe we're up to speed */
  	q->cons = Q_OVF(q, q->prod) | Q_WRP(q, q->cons) | Q_IDX(q, q->cons);
-	return IRQ_HANDLED;
  }
 
  static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)

xen/drivers/passthrough/arm/smmu-v3.c static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt) · line 1465

Quoted 5 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -1
@@ -1203,7 +1462,7 @@ static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)
 
  	dev_info(smmu->dev, "unexpected PRI request received:\n");
  	dev_info(smmu->dev,
-		 "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016llx\n",
+		 "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016lx\n",

Hmmm why?

xen/drivers/passthrough/arm/smmu-v3.c static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt) · line 1489

Quoted 9 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -1
  		 sid, ssid, grpid, last ? "L" : "",
  		 evt[0] & PRIQ_0_PERM_PRIV ? "" : "un",
  		 evt[0] & PRIQ_0_PERM_READ ? "R" : "",
@@ -1227,7 +1486,7 @@ static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)
  	}
  }
 
-static irqreturn_t arm_smmu_priq_thread(int irq, void *dev)
+static void arm_smmu_priq_thread(int irq, void *dev, struct cpu_user_regs *regs)

Ditto about the prototype.

xen/drivers/passthrough/arm/smmu-v3.c static irqreturn_t arm_smmu_priq_thread(int irq, void *dev) · line 1507

Quoted 12 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -2
  {
  	struct arm_smmu_device *smmu = dev;
  	struct arm_smmu_queue *q = &smmu->priq.q;
@@ -1243,18 +1502,16 @@ static irqreturn_t arm_smmu_priq_thread(int irq, void *dev)
 
  	/* Sync our overflow flag, as we believe we're up to speed */
  	q->cons = Q_OVF(q, q->prod) | Q_WRP(q, q->cons) | Q_IDX(q, q->cons);
-	return IRQ_HANDLED;
  }
 
-static irqreturn_t arm_smmu_cmdq_sync_handler(int irq, void *dev)
+static void arm_smmu_cmdq_sync_handler(int irq, void *dev, struct cpu_user_regs *regs)

Ditto.

xen/drivers/passthrough/arm/smmu-v3.c static irqreturn_t arm_smmu_priq_thread(int irq, void *dev) · line 1514

Quoted 9 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -2
  {
  	/* We don't actually use CMD_SYNC interrupts for anything */
-	return IRQ_HANDLED;
  }
 
  static int arm_smmu_device_disable(struct arm_smmu_device *smmu);
 
-static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
+static void arm_smmu_gerror_handler(int irq, void *dev, struct cpu_user_regs *regs)

Ditto.

Quoted 61 lines
xen/drivers/passthrough/arm/smmu-v3.c +6 -4
  {
  	u32 gerror, gerrorn, active;
  	struct arm_smmu_device *smmu = dev;
@@ -1264,7 +1521,7 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
 
  	active = gerror ^ gerrorn;
  	if (!(active & GERROR_ERR_MASK))
-		return IRQ_NONE; /* No errors pending */
+		return; /* No errors pending */
 
  	dev_warn(smmu->dev,
  		 "unexpected global error reported (0x%08x), this could be serious\n",
@@ -1286,7 +1543,7 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
 
  	if (active & GERROR_MSI_CMDQ_ABT_ERR) {
  		dev_warn(smmu->dev, "CMDQ MSI write aborted\n");
-		arm_smmu_cmdq_sync_handler(irq, smmu->dev);
+		arm_smmu_cmdq_sync_handler(irq, smmu->dev, NULL);
  	}
 
  	if (active & GERROR_PRIQ_ABT_ERR)
@@ -1299,7 +1556,6 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
  		arm_smmu_cmdq_skip_err(smmu);
 
  	writel(gerror, smmu->base + ARM_SMMU_GERRORN);
-	return IRQ_HANDLED;
  }
 
  /* IO_PGTABLE API */
@@ -1311,11 +1567,13 @@ static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu)
  	arm_smmu_cmdq_issue_cmd(smmu, &cmd);
  }
 
+#if 0 /*Xen: Unused function */
  static void arm_smmu_tlb_sync(void *cookie)
  {
  	struct arm_smmu_domain *smmu_domain = cookie;
  	__arm_smmu_tlb_sync(smmu_domain->smmu);
  }
+#endif
 
  static void arm_smmu_tlb_inv_context(void *cookie)
  {
@@ -1336,6 +1594,7 @@ static void arm_smmu_tlb_inv_context(void *cookie)
  	__arm_smmu_tlb_sync(smmu);
  }
 
+#if 0 /*Xen: Unused functionality */
  static void arm_smmu_tlb_inv_range_nosync(unsigned long iova, size_t size,
  					  size_t granule, bool leaf, void *cookie)
  {
@@ -1362,7 +1621,7 @@ static void arm_smmu_tlb_inv_range_nosync(unsigned long iova, size_t size,
  	} while (size -= granule);
  }
 
-static const struct iommu_gather_ops arm_smmu_gather_ops = {
+static struct iommu_gather_ops arm_smmu_gather_ops = {
  	.tlb_flush_all	= arm_smmu_tlb_inv_context,
  	.tlb_add_flush	= arm_smmu_tlb_inv_range_nosync,
  	.tlb_sync	= arm_smmu_tlb_sync,

xen/drivers/passthrough/arm/smmu-v3.c static bool arm_smmu_capable(enum iommu_cap cap) · line 1645

Quoted 7 lines
xen/drivers/passthrough/arm/smmu-v3.c +4 -0
@@ -1380,6 +1639,11 @@ static bool arm_smmu_capable(enum iommu_cap cap)
  		return false;
  	}
  }
+#endif
+/* Xen: Stub out DMA domain related functions */
+#define iommu_get_dma_cookie(dom) 0
+#define iommu_put_dma_cookie(dom) 0

Please stub them at the top of the file.

xen/drivers/passthrough/arm/smmu-v3.c static struct iommu_domain *arm_smmu_domain_alloc(unsigned type) · line 1677

Quoted 9 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -0
+
 
  static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
  {
@@ -1410,6 +1674,7 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
  	return &smmu_domain->domain;
  }
 
+#if 0

Please explain the #if 0

Quoted 28 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -16
  static int arm_smmu_bitmap_alloc(unsigned long *map, int span)
  {
  	int idx, size = 1 << span;
@@ -1427,36 +1692,20 @@ static void arm_smmu_bitmap_free(unsigned long *map, int idx)
  {
  	clear_bit(idx, map);
  }
+#endif
 
  static void arm_smmu_domain_free(struct iommu_domain *domain)
  {
  	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
-	struct arm_smmu_device *smmu = smmu_domain->smmu;
-
-	iommu_put_dma_cookie(domain);
-	free_io_pgtable_ops(smmu_domain->pgtbl_ops);
-
-	/* Free the CD and ASID, if we allocated them */
-	if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
-		struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
-
-		if (cfg->cdptr) {
-			dmam_free_coherent(smmu_domain->smmu->dev,
-					   CTXDESC_CD_DWORDS << 3,
-					   cfg->cdptr,
-					   cfg->cdptr_dma);
-
-			arm_smmu_bitmap_free(smmu->asid_map, cfg->cd.asid);

xen/drivers/passthrough/arm/smmu-v3.c static void arm_smmu_bitmap_free(unsigned long *map, int idx) · line 1703

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +4 -6
-		}
-	} else {
-		struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
-		if (cfg->vmid)
-			arm_smmu_bitmap_free(smmu->vmid_map, cfg->vmid);
-	}
+	/*
+	 * Xen: Remove the free functions that are not used and code related
+	 * to S1 translation. We just need to free the domain here.
+	 */

Please use #if 0 rather than removing the code + comment on top. But I am not sure why you drop the S2 free code. Shouldn't we allocate VMID from the SMMU?

Quoted 21 lines
xen/drivers/passthrough/arm/smmu-v3.c +3 -4
  	kfree(smmu_domain);
  }
 
+#if 0
  static int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain,
  				       struct io_pgtable_cfg *pgtbl_cfg)
  {
@@ -1488,33 +1737,30 @@ out_free_asid:
  	arm_smmu_bitmap_free(smmu->asid_map, asid);
  	return ret;
  }
+#endif
 
-static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain,
-				       struct io_pgtable_cfg *pgtbl_cfg)
+static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain)
  {
-	int vmid;
-	struct arm_smmu_device *smmu = smmu_domain->smmu;
  	struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;

xen/drivers/passthrough/arm/smmu-v3.c out_free_asid: · line 1748

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -6
 
-	vmid = arm_smmu_bitmap_alloc(smmu->vmid_map, smmu->vmid_bits);
-	if (vmid < 0)
-		return vmid;
+	/* Xen: Set the values as needed */
 
-	cfg->vmid	= (u16)vmid;
-	cfg->vttbr	= pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
-	cfg->vtcr	= pgtbl_cfg->arm_lpae_s2_cfg.vtcr;
+	cfg->vmid	= cfg->domain->arch.p2m.vmid;

See my comment above.

xen/drivers/passthrough/arm/smmu-v3.c out_free_asid: · line 1750

Quoted 2 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -0
+	cfg->vttbr	= page_to_maddr(cfg->domain->arch.p2m.root);
+	cfg->vtcr	= READ_SYSREG32(VTCR_EL2);

This looks a bit suspicious. Looking at the specs, the bits does not seem to correspond here.

I will look at the rest either tomorrow or Monday.

Cheers,

Signature · 6 lines
Julien Grall

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#27 lore raw

Re: [RFC v2 7/7] xen/iommu: smmu-v3: Add Xen specific code to enable the ported driver

Quoted 5 lines · Julien Grall

Hi Sameer,

Given this is all Arm specific. I am not sure why people like Andrew, Jan have been added.

Please use scripts/get_maintainers to find the list of maintainers per patches and avoid to CC all of them on each patches.

Ok.

Quoted 19 lines · Sameer Goel
Quoted 10 lines

This driver follows an approach similar to smmu driver. The intent here is to reuse as much Linux code as possible.
- Glue code has been introduced to bridge the API calls.
- Called Linux functions from the Xen IOMMU function calls.
- Xen modifications are preceded by /*Xen: comment */

---
  xen/drivers/passthrough/arm/Makefile  |   1 + xen/drivers/passthrough/arm/smmu-v3.c | 853 +++++++++++++++++++++++++++++-----

This is based on an old SMMUv3 version and I have been told there are some changes may benefits Xen (such as increasing the timeout for sync) and some optimisations also exist on the ML and will be queued soon.

So maybe you want to re-sync at least to master.

Quoted 2 lines
  2 files changed, 738 insertions(+), 116 deletions(-)
Quoted 10 lines
Quoted 8 lines
xen/drivers/passthrough/arm/Makefile +0 -0
index f4cd26e..57a6da6 100644
@@ -1,2 +1,3 @@

  obj-y += iommu.o
  obj-y += smmu.o
+obj-y += smmu-v3.o

Do we want SMMUv3 to be built on Arm32? Maybe we should introduce a new Kconfig to let the user select.

Agreed. I will define a CONFIG_ARM_SMMU_V3.

Quoted 53 lines
Quoted 30 lines
xen/drivers/passthrough/arm/smmu-v3.c +9 -0
index 380969a..8f3b43d 100644
@@ -18,28 +18,266 @@
    * Author: Will Deacon <[email protected]>
    *
    * This driver is powered by bad coffee and bombay mix.
+ *
+ *
+ * Based on Linux drivers/iommu/arm-smmu-v3.c
+ * => commit bdf95923086fb359ccb44c815724c3ace1611c90
+ *
+ * Xen modifications:
+ * Sameer Goel <[email protected]>
+ * Copyright (C) 2017, The Linux Foundation, All rights reserved.
+ *
    */

  -#include <linux/acpi.h>

xen/drivers/passthrough/arm/smmu-v3.c +0 -10
-#include <linux/acpi_iort.h>
-#include <linux/delay.h>
-#include <linux/dma-iommu.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/iommu.h>
-#include <linux/iopoll.h>
-#include <linux/module.h>
-#include <linux/msi.h>
-#include <linux/of.h>

xen/drivers/passthrough/arm/smmu-v3.c line 32

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -9
-#include <linux/of_address.h>
-#include <linux/of_iommu.h>
-#include <linux/of_platform.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-
-#include <linux/amba/bus.h>
-
-#include "io-pgtable.h"
+#include <xen/config.h>

This is not necessary.

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +10 -0
+#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>
Quoted 8 lines

xen/drivers/passthrough/arm/smmu-v3.c line 48

Quoted 6 lines
xen/drivers/passthrough/arm/smmu-v3.c +6 -0
+#include <xen/sizes.h>
+#include <asm/atomic.h>
+#include <asm/device.h>
+#include <asm/io.h>
+#include <asm/platform.h>
+#include <xen/acpi.h>

Please order the includes alphabetically with xen/* first then asm/*

Done.

Quoted 20 lines
Quoted 7 lines
xen/drivers/passthrough/arm/smmu-v3.c +6 -0
+
+typedef paddr_t phys_addr_t;
+typedef paddr_t dma_addr_t;
+
+/* Alias to Xen device tree helpers */
+#define device_node dt_device_node

xen/drivers/passthrough/arm/smmu-v3.c line 64

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +10 -0
+#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
+#define mutex spinlock_t
+#define mutex_init spin_lock_init
+#define mutex_lock spin_lock
+#define mutex_unlock spin_unlock

mutex and spinlock are not the same. The former is sleeping whilst the later is not.

Quoted 2 lines

Can you please explain why this is fine and possibly add that in a comment?

Mutex is used to protect the access to smmu device internal data structure when setting up the s2 config and installing stes for a given device in Linux. The ste programming operation can be competitively long but in the current testing, I did not see this blocking for too long. I will put in a comment.

Quoted 9 lines

xen/drivers/passthrough/arm/smmu-v3.c line 71

Quoted 7 lines
xen/drivers/passthrough/arm/smmu-v3.c +7 -0
+
+/* Xen: Helpers to get device MMIO and IRQs */
+struct resource {
+    u64 addr;
+    u64 size;
+    unsigned int type;
+};

Likely we want a compat header for defining Linux helpers. This would avoid replicating it everywhere.

Agreed.

Quoted 41 lines
Quoted 41 lines
xen/drivers/passthrough/arm/smmu-v3.c +40 -0
+
+#define resource_size(res) ((res)->size)
+
+#define platform_device device
+
+#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;
+    struct acpi_iort_node *iort_node;
+    struct acpi_iort_smmu_v3 *node_smmu_data;
+    int ret = 0;
+
+    res.type = type;
+
+    switch (type) {
+    case IORESOURCE_MEM:
+        if (pdev->type == DEV_ACPI) {
+            ret = 1;
+            iort_node = pdev->acpi_node;
+            node_smmu_data =
+                (struct acpi_iort_smmu_v3 *)iort_node->node_data;
+
+            if (node_smmu_data != NULL) {
+                res.addr = node_smmu_data->base_address;
+                res.size = SZ_128K;
+                ret = 0;
+            }
+        } else {
+            ret = dt_device_get_address(dev_to_dt(pdev), num,
+                            &res.addr, &res.size);
Quoted 8 lines

xen/drivers/passthrough/arm/smmu-v3.c line 117

Quoted 6 lines

+        }

xen/drivers/passthrough/arm/smmu-v3.c +5 -0
+
+        return ((ret) ? NULL : &res);
+
+    case IORESOURCE_IRQ:
+        ret = platform_get_irq(dev_to_dt(pdev), num);

No IRQ for ACPI?

For IRQs the code calls platform_get_irq_byname. So, the IORESOURCE_IRQ implementation is not needed at all. (DT or ACPI)

Quoted 56 lines
Quoted 56 lines
xen/drivers/passthrough/arm/smmu-v3.c +55 -0
+
+        if (ret < 0)
+            return NULL;
+
+        res.addr = ret;
+        res.size = 1;
+
+        return &res;
+
+    default:
+        return NULL;
+    }
+}
+
+static int platform_get_irq_byname(struct platform_device *pdev, const char *name)
+{
+    const struct dt_property *dtprop;
+    struct acpi_iort_node *iort_node;
+    struct acpi_iort_smmu_v3 *node_smmu_data;
+    int ret = 0;
+
+    if (pdev->type == DEV_ACPI) {
+        iort_node = pdev->acpi_node;
+        node_smmu_data = (struct acpi_iort_smmu_v3 *)iort_node->node_data;
+
+        if (node_smmu_data != NULL) {
+            if (!strcmp(name, "eventq"))
+                ret = node_smmu_data->event_gsiv;
+            else if (!strcmp(name, "priq"))
+                ret = node_smmu_data->pri_gsiv;
+            else if (!strcmp(name, "cmdq-sync"))
+                ret = node_smmu_data->sync_gsiv;
+            else if (!strcmp(name, "gerror"))
+                ret = node_smmu_data->gerr_gsiv;
+            else
+                ret = -EINVAL;
+        }
+    } else {
+        dtprop = dt_find_property(dev_to_dt(pdev), "interrupt-names", NULL);
+        if (!dtprop)
+            return -EINVAL;
+
+        if (!dtprop->value)
+            return -ENODATA;
+    }
+
+    return ret;
+}
+
+#define readx_poll_timeout(op, addr, val, cond, sleep_us, timeout_us) \
+({ \
+    s_time_t deadline = NOW() + MICROSECS(timeout_us); \
+    for (;;) { \
+        (val) = op(addr); \
+        if (cond) \
Quoted 8 lines
Quoted 6 lines

+            break; \
+        if (NOW() > deadline) { \
+            (val) = op(addr); \
+            break; \
+        } \
+        cpu_relax(); \

I don't think calling cpu_relax() is correct here.

I will remove cpu_relax() from here. It should not be needed.

Quoted 39 lines
Quoted 39 lines

+        udelay(sleep_us); \
+    } \
+    (cond) ? 0 : -ETIMEDOUT; \

xen/drivers/passthrough/arm/smmu-v3.c +35 -0
+})
+
+#define readl_relaxed_poll_timeout(addr, val, cond, delay_us, timeout_us) \
+    readx_poll_timeout(readl_relaxed, addr, val, cond, delay_us, timeout_us)
+
+/* Xen: Helpers for IRQ functions */
+#define request_irq(irq, func, flags, name, dev) request_irq(irq, flags, func, name, dev)
+#define free_irq release_irq
+
+enum irqreturn {
+    IRQ_NONE    = (0 << 0),
+    IRQ_HANDLED    = (1 << 0),
+};
+
+typedef enum irqreturn irqreturn_t;
+
+/* Device logger functions */
+#define dev_print(dev, lvl, fmt, ...)                        \
+     printk(lvl "smmu: " fmt, ## __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_info(dev, fmt, ...) dev_print(dev, XENLOG_INFO, fmt, ## __VA_ARGS__)
+
+#define dev_err_ratelimited(dev, fmt, ...)                    \
+     dev_print(dev, XENLOG_ERR, fmt, ## __VA_ARGS__)
+
+#define dev_name(dev) dt_node_full_name(dev_to_dt(dev))
+
+/* Alias to Xen allocation helpers */
+#define kfree xfree
+#define kmalloc(size, flags)        _xmalloc(size, sizeof(void *))
+#define kzalloc(size, flags)        _xzalloc(size, sizeof(void *))
Quoted 11 lines

xen/drivers/passthrough/arm/smmu-v3.c line 222

Quoted 6 lines
xen/drivers/passthrough/arm/smmu-v3.c +6 -0
+#define devm_kzalloc(dev, size, flags)    _xzalloc(size, sizeof(void *))
+#define kmalloc_array(size, n, flags)    _xmalloc_array(size, sizeof(void *), n)
+
+/* Compatibility defines */
+#undef WARN_ON
+#define WARN_ON(cond) (!!cond)

Why do you redefine WARN_ON?Need it to be a scalar value. The Xen implementation is a do..while. Did not want a large impact hence the local define. I can try proposing a new common define.

Quoted 2 lines

+#define WARN_ON_ONCE(cond) WARN_ON(cond)
Hmmm, can't we implement a common WARN_ON_ONCE?

Will not really be executed. Defining it to maintain compatibility. I think this file is the right place for this define. We can send it to a generic file if so needed.

Quoted 29 lines
Quoted 29 lines
xen/drivers/passthrough/arm/smmu-v3.c +28 -0
+
+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: Dummy iommu_domain */
+struct iommu_domain {
+    /* Runtime SMMU configuration for this iommu_domain */
+    struct arm_smmu_domain        *priv;
+    unsigned int            type;
+
Quoted 8 lines
Quoted 6 lines

+    atomic_t ref;
+    /* Used to link iommu_domain contexts for a same domain. +     * There is at least one per-SMMU to used by the domain. +     */
+    struct list_head        list;
+};

This is very similar to the SMMU version. Could we share some bits?

I will propose a common header for arm-smmu files.

Quoted 30 lines
Quoted 4 lines
xen/drivers/passthrough/arm/smmu-v3.c +3 -0
+/* Xen: Domain type definitions. Not really needed for Xen, defining to port
+ * Linux code as-is
+ */

xen/drivers/passthrough/arm/smmu-v3.c line 270

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +10 -0
+#define IOMMU_DOMAIN_UNMANAGED 0
+#define IOMMU_DOMAIN_DMA 1
+#define IOMMU_DOMAIN_IDENTITY 2
+
+/* Xen: Describes information required for a Xen domain */
+struct arm_smmu_xen_domain {
+    spinlock_t            lock;
+    /* List of iommu domains associated to this domain */
+    struct list_head        iommu_domains;
+};

Ditoo.

xen/drivers/passthrough/arm/smmu-v3.c line 280

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +10 -0
+
+/*
+ * Xen: Information about each device stored in dev->archdata.iommu
+ *
+ * The dev->archdata.iommu stores the iommu_domain (runtime configuration of
+ * the SMMU).
+ */
+struct arm_smmu_xen_device {
+    struct iommu_domain *domain;
+};

Ditto.

Quoted 8 lines
Quoted 6 lines

    /* MMIO registers */
  #define ARM_SMMU_IDR0            0x0

xen/drivers/passthrough/arm/smmu-v3.c +0 -0
@@ -412,10 +650,12 @@
   #define MSI_IOVA_BASE            0x8000000
   #define MSI_IOVA_LENGTH            0x100000

  +#if 0 /* Not applicable for Xen */

While the module_param_name() is not applicable in Xen, I don't see any reason to remove the variable.

Agreed.

Quoted 28 lines
Quoted 28 lines

  static bool disable_bypass; >   module_param_named(disable_bypass, disable_bypass, bool, S_IRUGO);
  MODULE_PARM_DESC(disable_bypass,
      "Disable bypass streams such that incoming transactions from devices that are not attached to an iommu domain will report an abort back to the device and will not be allowed to pass through the SMMU."); +#endif
    enum pri_resp {
      PRI_RESP_DENY,

xen/drivers/passthrough/arm/smmu-v3.c +0 -0
@@ -423,6 +663,7 @@ enum pri_resp {
       PRI_RESP_SUCC,
   };

  +#if 0 /* Xen: No MSI support in this iteration */ enum arm_smmu_msi_index {
      EVTQ_MSI_INDEX,
      GERROR_MSI_INDEX,

xen/drivers/passthrough/arm/smmu-v3.c +3 -0
@@ -447,6 +688,7 @@ static phys_addr_t arm_smmu_msi_cfg[ARM_SMMU_MAX_MSIS][3] = {
           ARM_SMMU_PRIQ_IRQ_CFG2,
       },
   };
+#endif
     struct arm_smmu_cmdq_ent {
       /* Common fields */
@@ -551,6 +793,8 @@ struct arm_smmu_s2_cfg {
       u16                vmid;
       u64                vttbr;
       u64                vtcr;
+    /* Xen: Domain associated to this configuration */
+    struct domain            *domain;
   };
Quoted 8 lines

xen/drivers/passthrough/arm/smmu-v3.c struct arm_smmu_device { · line 869

Quoted 6 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -1
     struct arm_smmu_strtab_ent {
@@ -623,9 +867,20 @@ struct arm_smmu_device {
       struct arm_smmu_strtab_cfg    strtab_cfg;
         /* IOMMU core code handle */
-    struct iommu_device        iommu;
+    //struct iommu_device        iommu;

#if 0 but no // please.

Done.

Quoted 23 lines
Quoted 23 lines
xen/drivers/passthrough/arm/smmu-v3.c +3 -0
+
+    /* Xen: Need to keep a list of SMMU devices */
+    struct list_head                devices;
   };

  +/* Xen: Keep a list of devices associated with this driver */

xen/drivers/passthrough/arm/smmu-v3.c +7 -0
+static DEFINE_SPINLOCK(arm_smmu_devices_lock);
+static LIST_HEAD(arm_smmu_devices);
+/* Xen: Helper for finding a device using fwnode */
+static
+struct arm_smmu_device *arm_smmu_get_by_fwnode(struct fwnode_handle *fwnode);
+
+

  /* SMMU private data for each master */
  struct arm_smmu_master_data {
      struct arm_smmu_device        *smmu;

xen/drivers/passthrough/arm/smmu-v3.c +1 -1
@@ -642,7 +897,7 @@ enum arm_smmu_domain_stage {
     struct arm_smmu_domain {
       struct arm_smmu_device        *smmu;
-    struct mutex            init_mutex; /* Protects smmu pointer */
+    mutex            init_mutex; /* Protects smmu pointer */
         struct io_pgtable_ops        *pgtbl_ops;
       spinlock_t            pgtbl_lock;
Quoted 8 lines

xen/drivers/passthrough/arm/smmu-v3.c static void queue_inc_prod(struct arm_smmu_queue *q) · line 995

Quoted 6 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -1
@@ -737,15 +992,16 @@ static void queue_inc_prod(struct arm_smmu_queue *q)
    */
   static int queue_poll_cons(struct arm_smmu_queue *q, bool drain, bool wfe)
   {
-    ktime_t timeout = ktime_add_us(ktime_get(), ARM_SMMU_POLL_TIMEOUT_US);
+    s_time_t deadline = NOW() + MICROSECS(ARM_SMMU_POLL_TIMEOUT_US);

Please introduce proper wrappers to avoid the modification of the code.

Done.

Quoted 7 lines

xen/drivers/passthrough/arm/smmu-v3.c static void queue_inc_prod(struct arm_smmu_queue *q) · line 999

Quoted 5 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -1
         while (queue_sync_cons(q), (drain ? !queue_empty(q) : queue_full(q))) {
-        if (ktime_compare(ktime_get(), timeout) > 0)
+
+        if (NOW() > deadline)

Ditto.

Done.

Quoted 11 lines

xen/drivers/passthrough/arm/smmu-v3.c static void queue_inc_prod(struct arm_smmu_queue *q) · line 1002

Quoted 4 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -0
               return -ETIMEDOUT;
   -        if (wfe) {
+        if (wfe)

Please avoid to drop {

xen/drivers/passthrough/arm/smmu-v3.c static void queue_inc_prod(struct arm_smmu_queue *q) · line 748

Quoted 2 lines
xen/drivers/passthrough/arm/smmu-v3.c +0 -1
               wfe();
-        } else {

Ditto.

Done.

Quoted 9 lines

xen/drivers/passthrough/arm/smmu-v3.c static void queue_inc_prod(struct arm_smmu_queue *q) · line 1005

Quoted 3 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -0
+        else {
               cpu_relax();

Hmmm I now see why you added cpu_relax() at the top. Well, on Xen cpu_relax is just a barrier. On Linux it is used to yield.

And that bit is worrying me. The Linux code will allow context switching to another tasks if the code is taking too much time.

Xen is not preemptible, so is it fine?

This is used when consuming the command queue and could be a potential performance issue if the queue is large. (This is never the case). I am wondering if we should define a yeild in long run?

Quoted 11 lines

xen/drivers/passthrough/arm/smmu-v3.c static void arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu, · line 1190

Quoted 9 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -1
               udelay(1);
           }
@@ -931,7 +1187,7 @@ static void arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
           dev_err_ratelimited(smmu->dev, "CMD_SYNC timeout\n");
       spin_unlock_irqrestore(&smmu->cmdq.lock, flags);
   }
-
+#if 0

Please avoid dropping newline and explain why the #if 0.

Done.

Quoted 11 lines

xen/drivers/passthrough/arm/smmu-v3.c static void arm_smmu_write_ctx_desc(struct arm_smmu_device *smmu, · line 1232

Quoted 9 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -1
   /* Context descriptor manipulation functions */
   static u64 arm_smmu_cpu_tcr_to_cd(u64 tcr)
   {
@@ -974,7 +1230,7 @@ static void arm_smmu_write_ctx_desc(struct arm_smmu_device *smmu,
         cfg->cdptr[3] = cpu_to_le64(cfg->cd.mair << CTXDESC_CD_3_MAIR_SHIFT);
   }
-
+#endif

Ditto for the newline.

Done.

Quoted 12 lines
Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +0 -0
   /* Stream table manipulation functions */

  static void
  arm_smmu_write_strtab_l1_desc(__le64 *dst, struct arm_smmu_strtab_l1_desc *desc)

xen/drivers/passthrough/arm/smmu-v3.c +0 -0
@@ -1044,7 +1300,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
               ste_live = true;
               break;

          case STRTAB_STE_0_CFG_ABORT:
-            if (disable_bypass)
+            //No bypass override for Xen

Why no leaving the variable on top with a comment. This would avoid such change.

Done.

Quoted 11 lines

xen/drivers/passthrough/arm/smmu-v3.c static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid, · line 1314

Quoted 9 lines

                  break;
          default:
              BUG(); /* STE corruption */

xen/drivers/passthrough/arm/smmu-v3.c +1 -1
@@ -1056,7 +1312,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_device *smmu, u32 sid,
         /* Bypass/fault */
       if (!ste->assigned || !(ste->s1_cfg || ste->s2_cfg)) {
-        if (!ste->assigned && disable_bypass)
+        if (!ste->assigned)

Ditto.

Done.

Quoted 15 lines

xen/drivers/passthrough/arm/smmu-v3.c static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid) · line 1398

Quoted 13 lines
xen/drivers/passthrough/arm/smmu-v3.c +0 -0
               val |= STRTAB_STE_0_CFG_ABORT;

          else
              val |= STRTAB_STE_0_CFG_BYPASS;

xen/drivers/passthrough/arm/smmu-v3.c +2 -0
@@ -1135,16 +1391,20 @@ static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid)
       void *strtab;
       struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;
       struct arm_smmu_strtab_l1_desc *desc = &cfg->l1_desc[sid >> STRTAB_SPLIT];
+    u32 alignment = 0;
         if (desc->l2ptr)
           return 0;
   -    size = 1 << (STRTAB_SPLIT + ilog2(STRTAB_STE_DWORDS) + 3);
+    size = 1 << (STRTAB_SPLIT + LOG_2(STRTAB_STE_DWORDS) + 3);

I would prefer if you introduce ilog2.

Done.

Quoted 9 lines

xen/drivers/passthrough/arm/smmu-v3.c static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid) · line 1404

Quoted 7 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -2
       strtab = &cfg->strtab[(sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS];
         desc->span = STRTAB_SPLIT + 1;
-    desc->l2ptr = dmam_alloc_coherent(smmu->dev, size, &desc->l2ptr_dma,
-                      GFP_KERNEL | __GFP_ZERO);
+
+    alignment = 1 << ((5 + (desc->span - 1)));

Do you mind explaining the 5? Also, does the shift will always be < 32?

Its from the "Level 1 Stream Table Descriptor" from SMMU spec. I can add the ref to the spec. Yes the span is hard coded in the driver and with any spec valid span values this cannot exceed 32. I have added a comment tot he code

Quoted 6 lines

xen/drivers/passthrough/arm/smmu-v3.c static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid) · line 1406

Quoted 3 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -0
+    desc->l2ptr = _xzalloc(size, alignment);
+    desc->l2ptr_dma = virt_to_maddr(desc->l2ptr);

_xzalloc can fail and virt_to_maddr will result to a panic.

I will move the check.

Quoted 12 lines

xen/drivers/passthrough/arm/smmu-v3.c static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid) · line 1420

Quoted 9 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -1
+
       if (!desc->l2ptr) {
           dev_err(smmu->dev,
               "failed to allocate l2 stream table for SID %u\n",
@@ -1158,7 +1418,7 @@ static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid)
   }
     /* IRQ and event handlers */
-static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
+static void arm_smmu_evtq_thread(int irq, void *dev, struct cpu_user_regs *regs)

Could you please introduce a wrapper instead as it was done in smmu.c?

Done.

Quoted 8 lines
Quoted 8 lines
xen/drivers/passthrough/arm/smmu-v3.c +0 -1
   {
       int i;
       struct arm_smmu_device *smmu = dev;
@@ -1186,7 +1446,6 @@ static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
         /* Sync our overflow flag, as we believe we're up to speed */
       q->cons = Q_OVF(q, q->prod) | Q_WRP(q, q->cons) | Q_IDX(q, q->cons);
-    return IRQ_HANDLED;
   }
Quoted 8 lines

xen/drivers/passthrough/arm/smmu-v3.c static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt) · line 1464

Quoted 6 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -1
     static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)
@@ -1203,7 +1462,7 @@ static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)
         dev_info(smmu->dev, "unexpected PRI request received:\n");
       dev_info(smmu->dev,
-         "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016llx\n",
+         "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016lx\n",

Hmmm why?

The variable is defined as ungined long long in Linux. ( uses generic int-ll64.h) In Xen the definition changes to unsigned long for ARM_64 which actually makes sense.

Quoted 11 lines

xen/drivers/passthrough/arm/smmu-v3.c static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt) · line 1489

Quoted 9 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -0
            sid, ssid, grpid, last ? "L" : "",
            evt[0] & PRIQ_0_PERM_PRIV ? "" : "un",
            evt[0] & PRIQ_0_PERM_READ ? "R" : "",
@@ -1227,7 +1486,7 @@ static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)
       }
   }
   -static irqreturn_t arm_smmu_priq_thread(int irq, void *dev)
+static void arm_smmu_priq_thread(int irq, void *dev, struct cpu_user_regs *regs)

Ditto about the prototype.

Done.

Quoted 13 lines

xen/drivers/passthrough/arm/smmu-v3.c static irqreturn_t arm_smmu_priq_thread(int irq, void *dev) · line 1506

Quoted 11 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -1
   {
       struct arm_smmu_device *smmu = dev;
       struct arm_smmu_queue *q = &smmu->priq.q;
@@ -1243,18 +1502,16 @@ static irqreturn_t arm_smmu_priq_thread(int irq, void *dev)
         /* Sync our overflow flag, as we believe we're up to speed */
       q->cons = Q_OVF(q, q->prod) | Q_WRP(q, q->cons) | Q_IDX(q, q->cons);
-    return IRQ_HANDLED;
   }
   -static irqreturn_t arm_smmu_cmdq_sync_handler(int irq, void *dev)
+static void arm_smmu_cmdq_sync_handler(int irq, void *dev, struct cpu_user_regs *regs)

Ditto.

Done.

Quoted 10 lines

xen/drivers/passthrough/arm/smmu-v3.c static irqreturn_t arm_smmu_priq_thread(int irq, void *dev) · line 1514

Quoted 8 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -1
   {
       /* We don't actually use CMD_SYNC interrupts for anything */
-    return IRQ_HANDLED;
   }
     static int arm_smmu_device_disable(struct arm_smmu_device *smmu);
   -static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
+static void arm_smmu_gerror_handler(int irq, void *dev, struct cpu_user_regs *regs)

Ditto

Done.

Quoted 53 lines
Quoted 53 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -3
   {
       u32 gerror, gerrorn, active;
       struct arm_smmu_device *smmu = dev;
@@ -1264,7 +1521,7 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
         active = gerror ^ gerrorn;
       if (!(active & GERROR_ERR_MASK))
-        return IRQ_NONE; /* No errors pending */
+        return; /* No errors pending */
         dev_warn(smmu->dev,
            "unexpected global error reported (0x%08x), this could be serious\n",
@@ -1286,7 +1543,7 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
         if (active & GERROR_MSI_CMDQ_ABT_ERR) {
           dev_warn(smmu->dev, "CMDQ MSI write aborted\n");
-        arm_smmu_cmdq_sync_handler(irq, smmu->dev);
+        arm_smmu_cmdq_sync_handler(irq, smmu->dev, NULL);
       }
         if (active & GERROR_PRIQ_ABT_ERR)
@@ -1299,7 +1556,6 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
           arm_smmu_cmdq_skip_err(smmu);
         writel(gerror, smmu->base + ARM_SMMU_GERRORN);
-    return IRQ_HANDLED;
   }
     /* IO_PGTABLE API */
@@ -1311,11 +1567,13 @@ static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu)
       arm_smmu_cmdq_issue_cmd(smmu, &cmd);
   }

  +#if 0 /*Xen: Unused function */
  static void arm_smmu_tlb_sync(void *cookie) {
      struct arm_smmu_domain *smmu_domain = cookie; __arm_smmu_tlb_sync(smmu_domain->smmu); }
+#endif
    static void arm_smmu_tlb_inv_context(void *cookie) {

xen/drivers/passthrough/arm/smmu-v3.c +0 -0
@@ -1336,6 +1594,7 @@ static void arm_smmu_tlb_inv_context(void *cookie)
       __arm_smmu_tlb_sync(smmu);
   }

  +#if 0 /*Xen: Unused functionality */
  static void arm_smmu_tlb_inv_range_nosync(unsigned long iova, size_t size, size_t granule, bool leaf, void *cookie) {

xen/drivers/passthrough/arm/smmu-v3.c +1 -0
@@ -1362,7 +1621,7 @@ static void arm_smmu_tlb_inv_range_nosync(unsigned long iova, size_t size,
       } while (size -= granule);
   }
   -static const struct iommu_gather_ops arm_smmu_gather_ops = {
+static struct iommu_gather_ops arm_smmu_gather_ops = {
       .tlb_flush_all    = arm_smmu_tlb_inv_context,
       .tlb_add_flush    = arm_smmu_tlb_inv_range_nosync,
       .tlb_sync    = arm_smmu_tlb_sync,
@@ -1380,6 +1639,11 @@ static bool arm_smmu_capable(enum iommu_cap cap)
           return false;
Quoted 8 lines

xen/drivers/passthrough/arm/smmu-v3.c static bool arm_smmu_capable(enum iommu_cap cap) · line 1645

Quoted 6 lines
xen/drivers/passthrough/arm/smmu-v3.c +4 -0
       }
   }
+#endif
+/* Xen: Stub out DMA domain related functions */
+#define iommu_get_dma_cookie(dom) 0
+#define iommu_put_dma_cookie(dom) 0

Please stub them at the top of the file.

Done.

Quoted 11 lines
Quoted 8 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -0
+
     static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
   {
@@ -1410,6 +1674,7 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
       return &smmu_domain->domain;
   }

  +#if 0

Please explain the #if 0

Done.

Quoted 31 lines
Quoted 31 lines

  static int arm_smmu_bitmap_alloc(unsigned long *map, int span) {
      int idx, size = 1 << span;

xen/drivers/passthrough/arm/smmu-v3.c +1 -20
@@ -1427,36 +1692,20 @@ static void arm_smmu_bitmap_free(unsigned long *map, int idx)
   {
       clear_bit(idx, map);
   }
+#endif
     static void arm_smmu_domain_free(struct iommu_domain *domain)
   {
       struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
-    struct arm_smmu_device *smmu = smmu_domain->smmu;
-
-    iommu_put_dma_cookie(domain);
-    free_io_pgtable_ops(smmu_domain->pgtbl_ops);
-
-    /* Free the CD and ASID, if we allocated them */
-    if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
-        struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
-
-        if (cfg->cdptr) {
-            dmam_free_coherent(smmu_domain->smmu->dev,
-                       CTXDESC_CD_DWORDS << 3,
-                       cfg->cdptr,
-                       cfg->cdptr_dma);
-
-            arm_smmu_bitmap_free(smmu->asid_map, cfg->cd.asid);
-        }
-    } else {
-        struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
-        if (cfg->vmid)
Quoted 8 lines

xen/drivers/passthrough/arm/smmu-v3.c static void arm_smmu_bitmap_free(unsigned long *map, int idx) · line 1703

Quoted 6 lines
xen/drivers/passthrough/arm/smmu-v3.c +4 -2
-            arm_smmu_bitmap_free(smmu->vmid_map, cfg->vmid);
-    }
+    /*
+     * Xen: Remove the free functions that are not used and code related
+     * to S1 translation. We just need to free the domain here.
+     */

Please use #if 0 rather than removing the code + comment on top. But I am not sure why you drop the S2 free code. Shouldn't we allocate VMID from the SMMU?

I am picking up the vmid from the domain I am sharing the page tables with. domain->arch.p2m.vmid. This seemed valid. Please let me know if we want to generate a different vmid?

Quoted 21 lines
Quoted 21 lines
xen/drivers/passthrough/arm/smmu-v3.c +0 -0
         kfree(smmu_domain);
   }

  +#if 0
  static int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain, struct io_pgtable_cfg *pgtbl_cfg)
  {

xen/drivers/passthrough/arm/smmu-v3.c +2 -4
@@ -1488,33 +1737,30 @@ out_free_asid:
       arm_smmu_bitmap_free(smmu->asid_map, asid);
       return ret;
   }
+#endif
   -static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain,
-                       struct io_pgtable_cfg *pgtbl_cfg)
+static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain)
   {
-    int vmid;
-    struct arm_smmu_device *smmu = smmu_domain->smmu;
       struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
   -    vmid = arm_smmu_bitmap_alloc(smmu->vmid_map, smmu->vmid_bits);
-    if (vmid < 0)
Quoted 8 lines

xen/drivers/passthrough/arm/smmu-v3.c out_free_asid: · line 1748

Quoted 6 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -3
-        return vmid;
+    /* Xen: Set the values as needed */
   -    cfg->vmid    = (u16)vmid;
-    cfg->vttbr    = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
-    cfg->vtcr    = pgtbl_cfg->arm_lpae_s2_cfg.vtcr;
+    cfg->vmid    = cfg->domain->arch.p2m.vmid;

See my comment above.

I am wondering why we are considering this value invalid? Since the page tables are shared we can use the vmid from the domain. Is the concern regarding the size?

Thanks,
Sameer

Quoted 10 lines

xen/drivers/passthrough/arm/smmu-v3.c out_free_asid: · line 1750

Quoted 3 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -0
+    cfg->vttbr    = page_to_maddr(cfg->domain->arch.p2m.root);
+    cfg->vtcr    = READ_SYSREG32(VTCR_EL2);

This looks a bit suspicious. Looking at the specs, the bits does not seem to correspond here.

I will look at the rest either tomorrow or Monday.

Cheers,

Signature · 6 lines
 Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#28 lore raw

Re: [RFC v2 7/7] xen/iommu: smmu-v3: Add Xen specific code to enable the ported driver

Hi Sameer,

Quoted 20 lines · Sameer
Quoted 19 lines · Julien Grall
Quoted 6 lines
xen/drivers/passthrough/arm/smmu-v3.c +6 -0
+
+typedef paddr_t phys_addr_t;
+typedef paddr_t dma_addr_t;
+
+/* Alias to Xen device tree helpers */
+#define device_node dt_device_node

xen/drivers/passthrough/arm/smmu-v3.c line 64

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +10 -0
+#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
+#define mutex spinlock_t
+#define mutex_init spin_lock_init
+#define mutex_lock spin_lock
+#define mutex_unlock spin_unlock

mutex and spinlock are not the same. The former is sleeping whilst the later is not.

Quoted 3 lines
Quoted 2 lines

Can you please explain why this is fine and possibly add that in a comment?

Mutex is used to protect the access to smmu device internal data structure when setting up the s2 config and installing stes for a given device in Linux. The ste programming operation can be competitively long but in the current testing, I did not see this blocking for too long. I will put in a comment.

Well, I don't think that this is a justification. You tested on one platform and does not explain how you perform them.

If I understand correctly, that mutex is only used when assigning device. So it might be ok to switch to spinlock. But that's not because the operation is not too long, it just because it would be only perform by the toolstack (domctl) and will not be issued by guest.

Quoted 10 lines
Quoted 10 lines

xen/drivers/passthrough/arm/smmu-v3.c line 71

Quoted 8 lines
xen/drivers/passthrough/arm/smmu-v3.c +7 -0
+
+/* Xen: Helpers to get device MMIO and IRQs */
+struct resource {
+    u64 addr;
+    u64 size;
+    unsigned int type;
+};

Likely we want a compat header for defining Linux helpers. This would avoid replicating it everywhere.

Quoted 2 lines

Agreed.

That should be

Quoted 49 lines
Quoted 41 lines
Quoted 41 lines
xen/drivers/passthrough/arm/smmu-v3.c +40 -0
+
+#define resource_size(res) ((res)->size)
+
+#define platform_device device
+
+#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;
+    struct acpi_iort_node *iort_node;
+    struct acpi_iort_smmu_v3 *node_smmu_data;
+    int ret = 0;
+
+    res.type = type;
+
+    switch (type) {
+    case IORESOURCE_MEM:
+        if (pdev->type == DEV_ACPI) {
+            ret = 1;
+            iort_node = pdev->acpi_node;
+            node_smmu_data =
+                (struct acpi_iort_smmu_v3 *)iort_node->node_data;
+
+            if (node_smmu_data != NULL) {
+                res.addr = node_smmu_data->base_address;
+                res.size = SZ_128K;
+                ret = 0;
+            }
+        } else {
+            ret = dt_device_get_address(dev_to_dt(pdev), num,
+                            &res.addr, &res.size);
Quoted 8 lines

xen/drivers/passthrough/arm/smmu-v3.c line 117

Quoted 6 lines

+        }

xen/drivers/passthrough/arm/smmu-v3.c +5 -0
+
+        return ((ret) ? NULL : &res);
+
+    case IORESOURCE_IRQ:
+        ret = platform_get_irq(dev_to_dt(pdev), num);

No IRQ for ACPI?

Quoted 1 line

For IRQs the code calls platform_get_irq_byname. So, the IORESOURCE_IRQ implementation is not needed at all. (DT or ACPI)

Please document it then.

[...]

Quoted 39 lines
Quoted 39 lines
Quoted 39 lines

+        udelay(sleep_us); \
+    } \
+    (cond) ? 0 : -ETIMEDOUT; \

xen/drivers/passthrough/arm/smmu-v3.c +35 -0
+})
+
+#define readl_relaxed_poll_timeout(addr, val, cond, delay_us, timeout_us) \
+    readx_poll_timeout(readl_relaxed, addr, val, cond, delay_us, timeout_us)
+
+/* Xen: Helpers for IRQ functions */
+#define request_irq(irq, func, flags, name, dev) request_irq(irq, flags, func, name, dev)
+#define free_irq release_irq
+
+enum irqreturn {
+    IRQ_NONE    = (0 << 0),
+    IRQ_HANDLED    = (1 << 0),
+};
+
+typedef enum irqreturn irqreturn_t;
+
+/* Device logger functions */
+#define dev_print(dev, lvl, fmt, ...)                        \
+     printk(lvl "smmu: " fmt, ## __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_info(dev, fmt, ...) dev_print(dev, XENLOG_INFO, fmt, ## __VA_ARGS__)
+
+#define dev_err_ratelimited(dev, fmt, ...)                    \
+     dev_print(dev, XENLOG_ERR, fmt, ## __VA_ARGS__)
+
+#define dev_name(dev) dt_node_full_name(dev_to_dt(dev))
+
+/* Alias to Xen allocation helpers */
+#define kfree xfree
+#define kmalloc(size, flags)        _xmalloc(size, sizeof(void *))
+#define kzalloc(size, flags)        _xzalloc(size, sizeof(void *))
Quoted 8 lines
Quoted 8 lines

xen/drivers/passthrough/arm/smmu-v3.c line 222

Quoted 6 lines
xen/drivers/passthrough/arm/smmu-v3.c +6 -0
+#define devm_kzalloc(dev, size, flags)    _xzalloc(size, sizeof(void *))
+#define kmalloc_array(size, n, flags)    _xmalloc_array(size, sizeof(void *), n)
+
+/* Compatibility defines */
+#undef WARN_ON
+#define WARN_ON(cond) (!!cond)

Why do you redefine WARN_ON?Need it to be a scalar value. The Xen implementation is a do..while. Did not want a large impact hence the local define. I can try proposing a new common define.

Well, I don't think it is acceptable to redefine WARN_ON. At least without trying to modify the common version.

Quoted 4 lines
Quoted 3 lines
Quoted 3 lines

+#define WARN_ON_ONCE(cond) WARN_ON(cond)
Hmmm, can't we implement a common WARN_ON_ONCE?

Will not really be executed. Defining it to maintain compatibility. I think this file is the right place for this define. We can send it to a generic file if so needed.

I can't see why we wouldn't want to have a WARN_ON_ONCE in the common code. If you disagree, please explain.

[...]

Quoted 11 lines
Quoted 9 lines
Quoted 3 lines

+        else {
              cpu_relax();

Hmmm I now see why you added cpu_relax() at the top. Well, on Xen cpu_relax is just a barrier. On Linux it is used to yield.

And that bit is worrying me. The Linux code will allow context switching to another tasks if the code is taking too much time.

Xen is not preemptible, so is it fine?

This is used when consuming the command queue and could be a potential performance issue if the queue is large. (This is never the case). I am wondering if we should define a yeild in long run?

As I said before, Xen is not preemptible. In this particular case, there are spinlock taken by the callers (e.g any function assigning device). So yield would just make it worst.

[...]

Quoted 48 lines
Quoted 9 lines
Quoted 7 lines

      strtab = &cfg->strtab[(sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS]; desc->span = STRTAB_SPLIT + 1;
-    desc->l2ptr = dmam_alloc_coherent(smmu->dev, size, &desc->l2ptr_dma, -                      GFP_KERNEL | __GFP_ZERO);
+
+    alignment = 1 << ((5 + (desc->span - 1)));

Do you mind explaining the 5? Also, does the shift will always be < 32?

Its from the "Level 1 Stream Table Descriptor" from SMMU spec. I can add the ref to the spec. Yes the span is hard coded in the driver and with any spec valid span values this cannot exceed 32. I have added a comment tot he code

Quoted 6 lines
Quoted 3 lines

+    desc->l2ptr = _xzalloc(size, alignment); +    desc->l2ptr_dma = virt_to_maddr(desc->l2ptr);

_xzalloc can fail and virt_to_maddr will result to a panic.

I will move the check.

Quoted 12 lines

xen/drivers/passthrough/arm/smmu-v3.c static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid) · line 1420

Quoted 9 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -1
+
       if (!desc->l2ptr) {
           dev_err(smmu->dev,
               "failed to allocate l2 stream table for SID %u\n",
@@ -1158,7 +1418,7 @@ static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid)
   }
     /* IRQ and event handlers */
-static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
+static void arm_smmu_evtq_thread(int irq, void *dev, struct cpu_user_regs *regs)

Could you please introduce a wrapper instead as it was done in smmu.c?

Done.

Quoted 8 lines
Quoted 8 lines
xen/drivers/passthrough/arm/smmu-v3.c +0 -1
   {
       int i;
       struct arm_smmu_device *smmu = dev;
@@ -1186,7 +1446,6 @@ static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
         /* Sync our overflow flag, as we believe we're up to speed */
       q->cons = Q_OVF(q, q->prod) | Q_WRP(q, q->cons) | Q_IDX(q, q->cons);
-    return IRQ_HANDLED;
   }
Quoted 8 lines

xen/drivers/passthrough/arm/smmu-v3.c static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt) · line 1464

Quoted 6 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -1
     static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)
@@ -1203,7 +1462,7 @@ static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)
         dev_info(smmu->dev, "unexpected PRI request received:\n");
       dev_info(smmu->dev,
-         "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016llx\n",
+         "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016lx\n",

Hmmm why?

Quoted 1 line

The variable is defined as ungined long long in Linux. ( uses generic int-ll64.h) In Xen the definition changes to unsigned long for ARM_64 which actually makes sense.

u64 is a 64-bit variable. And therefore the format should be PRIx64 to accomodate 64-bit and 32-bit.

[...]

Quoted 52 lines
Quoted 11 lines

xen/drivers/passthrough/arm/smmu-v3.c static struct iommu_domain *arm_smmu_domain_alloc(unsigned type) · line 1676

Quoted 8 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -0
+
     static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
   {
@@ -1410,6 +1674,7 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
       return &smmu_domain->domain;
   }
   +#if 0

Please explain the #if 0

Done.

Quoted 31 lines
Quoted 31 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -20
   static int arm_smmu_bitmap_alloc(unsigned long *map, int span)
   {
       int idx, size = 1 << span;
@@ -1427,36 +1692,20 @@ static void arm_smmu_bitmap_free(unsigned long *map, int idx)
   {
       clear_bit(idx, map);
   }
+#endif
     static void arm_smmu_domain_free(struct iommu_domain *domain)
   {
       struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
-    struct arm_smmu_device *smmu = smmu_domain->smmu;
-
-    iommu_put_dma_cookie(domain);
-    free_io_pgtable_ops(smmu_domain->pgtbl_ops);
-
-    /* Free the CD and ASID, if we allocated them */
-    if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
-        struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
-
-        if (cfg->cdptr) {
-            dmam_free_coherent(smmu_domain->smmu->dev,
-                       CTXDESC_CD_DWORDS << 3,
-                       cfg->cdptr,
-                       cfg->cdptr_dma);
-
-            arm_smmu_bitmap_free(smmu->asid_map, cfg->cd.asid);
-        }
-    } else {
-        struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
-        if (cfg->vmid)
Quoted 8 lines

xen/drivers/passthrough/arm/smmu-v3.c static void arm_smmu_bitmap_free(unsigned long *map, int idx) · line 1703

Quoted 6 lines
xen/drivers/passthrough/arm/smmu-v3.c +4 -2
-            arm_smmu_bitmap_free(smmu->vmid_map, cfg->vmid);
-    }
+    /*
+     * Xen: Remove the free functions that are not used and code related
+     * to S1 translation. We just need to free the domain here.
+     */

Please use #if 0 rather than removing the code + comment on top. But I am not sure why you drop the S2 free code. Shouldn't we allocate VMID from the SMMU?

Quoted 1 line

I am picking up the vmid from the domain I am sharing the page tables with. domain->arch.p2m.vmid. This seemed valid. Please let me know if we want to generate a different vmid?

The processor and the SMMU may support either 8 or 16bits VMID but I don't think any thing in the spec says that both processor and SMMU must support the same value.

So it would be possible to have a processor supporting 16-bits VMID and the SMMU only 8-bits VMID.

Adding a check at the SMMU initialization might be a solution. But as the code for allocating the VMID is already present, then I would prefer to we stick with different the VMID.

Quoted 31 lines
Quoted 23 lines
Quoted 23 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -4
         kfree(smmu_domain);
   }
   +#if 0
   static int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain,
                          struct io_pgtable_cfg *pgtbl_cfg)
   {
@@ -1488,33 +1737,30 @@ out_free_asid:
       arm_smmu_bitmap_free(smmu->asid_map, asid);
       return ret;
   }
+#endif
   -static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain,
-                       struct io_pgtable_cfg *pgtbl_cfg)
+static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain)
   {
-    int vmid;
-    struct arm_smmu_device *smmu = smmu_domain->smmu;
       struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
   -    vmid = arm_smmu_bitmap_alloc(smmu->vmid_map, smmu->vmid_bits);
-    if (vmid < 0)
Quoted 8 lines

xen/drivers/passthrough/arm/smmu-v3.c out_free_asid: · line 1748

Quoted 6 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -3
-        return vmid;
+    /* Xen: Set the values as needed */
   -    cfg->vmid    = (u16)vmid;
-    cfg->vttbr    = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
-    cfg->vtcr    = pgtbl_cfg->arm_lpae_s2_cfg.vtcr;
+    cfg->vmid    = cfg->domain->arch.p2m.vmid;

See my comment above.

Quoted 1 line

I am wondering why we are considering this value invalid? Since the page tables are shared we can use the vmid from the domain. Is the concern regarding the size?

See above.

Cheers,

Signature · 6 lines
Julien Grall

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#29 lore raw

Re: [RFC v2 7/7] xen/iommu: smmu-v3: Add Xen specific code to enable the ported driver

On 20/11/17 14:25, Julien Grall wrote:
[...]

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 3 lines

+        else {
               cpu_relax();

Hmmm I now see why you added cpu_relax() at the top. Well, on Xen cpu_relax is just a barrier. On Linux it is used to yield.

Quoted 7 lines
Quoted 5 lines
Quoted 3 lines

And that bit is worrying me. The Linux code will allow context switching to another tasks if the code is taking too much time.

Xen is not preemptible, so is it fine?

This is used when consuming the command queue and could be a potential performance issue if the queue is large. (This is never the case). I am wondering if we should define a yeild in long run?

As I said before, Xen is not preemptible. In this particular case, there are spinlock taken by the callers (e.g any function assigning device). So yield would just make it worst.

The arguments here don't make much sense - the "yield" instruction has nothing to do with software-level concepts of preemption. It is a hint to SMT *hardware* that this logical processor is doing nothing useful in the short term, so it might be a good idea to let other logical processor(s) have priority over shared execution resources if applicable.

Until SMT CPUs become commonly available, though, it's somewhat of a moot point and mostly just a future-proofing consideration.

Robin.

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel

#30 lore raw

Re: [RFC v2 7/7] xen/iommu: smmu-v3: Add Xen specific code to enable the ported driver

Hi,

Quoted 10 lines · Robin Murphy

On 20/11/17 14:25, Julien Grall wrote:
[...]

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 3 lines

+        else {
               cpu_relax();

Hmmm I now see why you added cpu_relax() at the top. Well, on Xen cpu_relax is just a barrier. On Linux it is used to yield.

And that bit is worrying me. The Linux code will allow context switching to another tasks if the code is taking too much time.

Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
Quoted 1 line

Xen is not preemptible, so is it fine?

This is used when consuming the command queue and could be a potential performance issue if the queue is large. (This is never the case). I am wondering if we should define a yeild in long run?

As I said before, Xen is not preemptible. In this particular case, there are spinlock taken by the callers (e.g any function assigning device). So yield would just make it worst.

The arguments here don't make much sense - the "yield" instruction has nothing to do with software-level concepts of preemption. It is a hint to SMT *hardware* that this logical processor is doing nothing useful in the short term, so it might be a good idea to let other logical processor(s) have priority over shared execution resources if applicable.

Oh, sorry I thought this could also be used by the software to switch between thread. Please disregard my comment then.

Quoted 4 lines

Until SMT CPUs become commonly available, though, it's somewhat of a moot point and mostly just a future-proofing consideration.

Robin.

Cheers,

Signature · 6 lines
Julien Grall

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#31 lore raw

Re: [RFC v2 7/7] xen/iommu: smmu-v3: Add Xen specific code to enable the ported driver

In reply to Julien Grall
Quoted 23 lines · Julien Grall

Hi Sameer,

Quoted 20 lines · Sameer
Quoted 19 lines · Julien Grall
Quoted 6 lines
xen/drivers/passthrough/arm/smmu-v3.c +6 -0
+
+typedef paddr_t phys_addr_t;
+typedef paddr_t dma_addr_t;
+
+/* Alias to Xen device tree helpers */
+#define device_node dt_device_node

xen/drivers/passthrough/arm/smmu-v3.c line 64

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +10 -0
+#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
+#define mutex spinlock_t
+#define mutex_init spin_lock_init
+#define mutex_lock spin_lock
+#define mutex_unlock spin_unlock

mutex and spinlock are not the same. The former is sleeping whilst the later is not.

Quoted 7 lines
Quoted 3 lines
Quoted 2 lines

Can you please explain why this is fine and possibly add that in a comment?

Mutex is used to protect the access to smmu device internal data structure when setting up the s2 config and installing stes for a given device in Linux. The ste programming  operation can be competitively long but in the current testing, I did not see this blocking for too long. I will put in a comment.

Well, I don't think that this is a justification. You tested on one platform and does not explain how you perform them.

If I understand correctly, that mutex is only used when assigning device. So it might be ok to switch to spinlock. But that's not because the operation is not too long, it just because it would be only perform by the toolstack (domctl) and will not be issued by guest.

Ok. I agree ans will update the comment.

Quoted 63 lines
Quoted 11 lines
Quoted 11 lines

xen/drivers/passthrough/arm/smmu-v3.c line 71

Quoted 9 lines
xen/drivers/passthrough/arm/smmu-v3.c +7 -0
+
+/* Xen: Helpers to get device MMIO and IRQs */
+struct resource {
+    u64 addr;
+    u64 size;
+    unsigned int type;
+};

Likely we want a compat header for defining Linux helpers. This would avoid replicating it everywhere.

Quoted 2 lines

Agreed.

That should be

Quoted 49 lines
Quoted 49 lines
Quoted 37 lines
xen/drivers/passthrough/arm/smmu-v3.c +36 -0
+
+#define resource_size(res) ((res)->size)
+
+#define platform_device device
+
+#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;
+    struct acpi_iort_node *iort_node;
+    struct acpi_iort_smmu_v3 *node_smmu_data;
+    int ret = 0;
+
+    res.type = type;
+
+    switch (type) {
+    case IORESOURCE_MEM:
+        if (pdev->type == DEV_ACPI) {
+            ret = 1;
+            iort_node = pdev->acpi_node;
+            node_smmu_data =
+                (struct acpi_iort_smmu_v3 *)iort_node->node_data;
+
+            if (node_smmu_data != NULL) {
+                res.addr = node_smmu_data->base_address;
+                res.size = SZ_128K;
+                ret = 0;

xen/drivers/passthrough/arm/smmu-v3.c line 117

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +10 -0
+            }
+        } else {
+            ret = dt_device_get_address(dev_to_dt(pdev), num,
+                            &res.addr, &res.size);
+        }
+
+        return ((ret) ? NULL : &res);
+
+    case IORESOURCE_IRQ:
+        ret = platform_get_irq(dev_to_dt(pdev), num);

No IRQ for ACPI?

Quoted 3 lines
Quoted 1 line

For IRQs the code calls platform_get_irq_byname. So, the IORESOURCE_IRQ implementation is not needed at all. (DT or ACPI)

Please document it then.

Ok.

Quoted 51 lines

[...]

Quoted 39 lines
Quoted 39 lines
Quoted 39 lines

+        udelay(sleep_us); \
+    } \
+    (cond) ? 0 : -ETIMEDOUT; \

xen/drivers/passthrough/arm/smmu-v3.c +35 -0
+})
+
+#define readl_relaxed_poll_timeout(addr, val, cond, delay_us, timeout_us) \
+    readx_poll_timeout(readl_relaxed, addr, val, cond, delay_us, timeout_us)
+
+/* Xen: Helpers for IRQ functions */
+#define request_irq(irq, func, flags, name, dev) request_irq(irq, flags, func, name, dev)
+#define free_irq release_irq
+
+enum irqreturn {
+    IRQ_NONE    = (0 << 0),
+    IRQ_HANDLED    = (1 << 0),
+};
+
+typedef enum irqreturn irqreturn_t;
+
+/* Device logger functions */
+#define dev_print(dev, lvl, fmt, ...)                        \
+     printk(lvl "smmu: " fmt, ## __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_info(dev, fmt, ...) dev_print(dev, XENLOG_INFO, fmt, ## __VA_ARGS__)
+
+#define dev_err_ratelimited(dev, fmt, ...)                    \
+     dev_print(dev, XENLOG_ERR, fmt, ## __VA_ARGS__)
+
+#define dev_name(dev) dt_node_full_name(dev_to_dt(dev))
+
+/* Alias to Xen allocation helpers */
+#define kfree xfree
+#define kmalloc(size, flags)        _xmalloc(size, sizeof(void *))
+#define kzalloc(size, flags)        _xzalloc(size, sizeof(void *))
Quoted 8 lines
Quoted 8 lines

xen/drivers/passthrough/arm/smmu-v3.c line 222

Quoted 6 lines
xen/drivers/passthrough/arm/smmu-v3.c +6 -0
+#define devm_kzalloc(dev, size, flags)    _xzalloc(size, sizeof(void *))
+#define kmalloc_array(size, n, flags)    _xmalloc_array(size, sizeof(void *), n)
+
+/* Compatibility defines */
+#undef WARN_ON
+#define WARN_ON(cond) (!!cond)

Why do you redefine WARN_ON?Need it to be a scalar value. The Xen implementation is a do..while. Did not want a large impact hence the local define. I can try proposing a new common define.

Quoted 1 line

Well, I don't think it is acceptable to redefine WARN_ON. At least without trying to modify the common version.

I will put this in the common folder.

Quoted 18 lines
Quoted 5 lines
Quoted 4 lines
Quoted 4 lines

+#define WARN_ON_ONCE(cond) WARN_ON(cond)
Hmmm, can't we implement a common WARN_ON_ONCE?

Will not really be executed. Defining it to maintain compatibility. I think this file is the right place for this define. We can send it to a generic file if so needed.

I can't see why we wouldn't want to have a WARN_ON_ONCE in the common code. If you disagree, please explain.

[...]

Quoted 8 lines
Quoted 8 lines
Quoted 3 lines

+        else {
               cpu_relax();

Hmmm I now see why you added cpu_relax() at the top. Well, on Xen cpu_relax is just a barrier. On Linux it is used to yield.

And that bit is worrying me. The Linux code will allow context switching to another tasks if the code is taking too much time.

Quoted 7 lines
Quoted 3 lines
Quoted 1 line

Xen is not preemptible, so is it fine?

This is used when consuming the command queue and could be a potential performance issue if the queue is large. (This is never the case). I am wondering if we should define a yeild in long run?

As I said before, Xen is not preemptible. In this particular case, there are spinlock taken by the callers (e.g any function assigning device). So yield would just make it worst.

[...]

I can keep the memory barrier as is. This should not impact much. This should not be a concern.

Quoted 49 lines
Quoted 49 lines
Quoted 10 lines
Quoted 8 lines

       strtab = &cfg->strtab[(sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS]; desc->span = STRTAB_SPLIT + 1;
-    desc->l2ptr = dmam_alloc_coherent(smmu->dev, size, &desc->l2ptr_dma, -                      GFP_KERNEL | __GFP_ZERO);
+
+    alignment = 1 << ((5 + (desc->span - 1)));

Do you mind explaining the 5? Also, does the shift will always be < 32?

Its from the "Level 1 Stream Table Descriptor" from SMMU spec. I can add the ref to the spec. Yes the span is hard coded in the driver and with any spec valid span values this cannot exceed 32. I have added a comment tot he code

Quoted 6 lines
Quoted 3 lines

+    desc->l2ptr = _xzalloc(size, alignment); +    desc->l2ptr_dma = virt_to_maddr(desc->l2ptr);

_xzalloc can fail and virt_to_maddr will result to a panic.

I will move the check.

Quoted 12 lines

xen/drivers/passthrough/arm/smmu-v3.c static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid) · line 1420

Quoted 9 lines

+
       if (!desc->l2ptr) {
           dev_err(smmu->dev,
               "failed to allocate l2 stream table for SID %u\n",

xen/drivers/passthrough/arm/smmu-v3.c +1 -1
@@ -1158,7 +1418,7 @@ static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid)
    }
      /* IRQ and event handlers */
-static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
+static void arm_smmu_evtq_thread(int irq, void *dev, struct cpu_user_regs *regs)

Could you please introduce a wrapper instead as it was done in smmu.c?

Done.

Quoted 16 lines
Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +0 -1
    {
        int i;
        struct arm_smmu_device *smmu = dev;
@@ -1186,7 +1446,6 @@ static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev)
          /* Sync our overflow flag, as we believe we're up to speed */
        q->cons = Q_OVF(q, q->prod) | Q_WRP(q, q->cons) | Q_IDX(q, q->cons);
-    return IRQ_HANDLED;
    }
      static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)

xen/drivers/passthrough/arm/smmu-v3.c static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt) · line 1464

Quoted 4 lines
xen/drivers/passthrough/arm/smmu-v3.c +1 -1
@@ -1203,7 +1462,7 @@ static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt)
          dev_info(smmu->dev, "unexpected PRI request received:\n");
        dev_info(smmu->dev,
-         "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016llx\n",
+         "\tsid 0x%08x.0x%05x: [%u%s] %sprivileged %s%s%s access at iova 0x%016lx\n",

Hmmm why?

Quoted 3 lines
Quoted 1 line

The variable is defined as ungined long long in Linux. ( uses generic int-ll64.h) In Xen the definition changes to unsigned long for ARM_64 which actually makes sense.

u64 is a 64-bit variable. And therefore the format should be PRIx64 to accomodate 64-bit and 32-bit.

Sure. I can change the format specifier.

Quoted 55 lines

[...]

Quoted 52 lines
Quoted 11 lines
Quoted 8 lines

+
     static struct iommu_domain *arm_smmu_domain_alloc(unsigned type) {

xen/drivers/passthrough/arm/smmu-v3.c +0 -0
@@ -1410,6 +1674,7 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
        return &smmu_domain->domain;
    }

   +#if 0

Please explain the #if 0

Done.

Quoted 39 lines
Quoted 27 lines

   static int arm_smmu_bitmap_alloc(unsigned long *map, int span) {
       int idx, size = 1 << span;

xen/drivers/passthrough/arm/smmu-v3.c +1 -16
@@ -1427,36 +1692,20 @@ static void arm_smmu_bitmap_free(unsigned long *map, int idx)
    {
        clear_bit(idx, map);
    }
+#endif
      static void arm_smmu_domain_free(struct iommu_domain *domain)
    {
        struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
-    struct arm_smmu_device *smmu = smmu_domain->smmu;
-
-    iommu_put_dma_cookie(domain);
-    free_io_pgtable_ops(smmu_domain->pgtbl_ops);
-
-    /* Free the CD and ASID, if we allocated them */
-    if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
-        struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;
-
-        if (cfg->cdptr) {
-            dmam_free_coherent(smmu_domain->smmu->dev,
-                       CTXDESC_CD_DWORDS << 3,
-                       cfg->cdptr,
-                       cfg->cdptr_dma);
-
-            arm_smmu_bitmap_free(smmu->asid_map, cfg->cd.asid);

xen/drivers/passthrough/arm/smmu-v3.c static void arm_smmu_bitmap_free(unsigned long *map, int idx) · line 1702

Quoted 10 lines
xen/drivers/passthrough/arm/smmu-v3.c +4 -6
-        }
-    } else {
-        struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
-        if (cfg->vmid)
-            arm_smmu_bitmap_free(smmu->vmid_map, cfg->vmid);
-    }
+    /*
+     * Xen: Remove the free functions that are not used and code related
+     * to S1 translation. We just need to free the domain here.
+     */

Please use #if 0 rather than removing the code + comment on top. But I am not sure why you drop the S2 free code. Shouldn't we allocate VMID from the SMMU?

Quoted 7 lines
Quoted 1 line

I am picking up the vmid from the domain I am sharing the page tables with. domain->arch.p2m.vmid. This seemed valid. Please let me know if we want to generate a different vmid?

The processor and the SMMU may support either 8 or 16bits VMID but I don't think any thing in the spec says that both processor and SMMU must support the same value.

So it would be possible to have a processor supporting 16-bits VMID and the SMMU only 8-bits VMID.

Adding a check at the SMMU initialization might be a solution. But as the code for allocating the VMID is already present, then I would prefer to we stick with different the VMID.

Sure. For this iteration I will generate an SMMU specific VMID. Do you think it would be an optimization to reuse the CPU VMID if SMMU supports 16bit VMID?

Thanks,
Sameer

Quoted 38 lines
Quoted 32 lines
Quoted 24 lines
Quoted 24 lines
xen/drivers/passthrough/arm/smmu-v3.c +0 -0
          kfree(smmu_domain);
    }

   +#if 0
   static int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain, struct io_pgtable_cfg *pgtbl_cfg)
   {

xen/drivers/passthrough/arm/smmu-v3.c +2 -4
@@ -1488,33 +1737,30 @@ out_free_asid:
        arm_smmu_bitmap_free(smmu->asid_map, asid);
        return ret;
    }
+#endif
    -static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain,
-                       struct io_pgtable_cfg *pgtbl_cfg)
+static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain)
    {
-    int vmid;
-    struct arm_smmu_device *smmu = smmu_domain->smmu;
        struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
    -    vmid = arm_smmu_bitmap_alloc(smmu->vmid_map, smmu->vmid_bits);
-    if (vmid < 0)
Quoted 8 lines

xen/drivers/passthrough/arm/smmu-v3.c out_free_asid: · line 1748

Quoted 6 lines
xen/drivers/passthrough/arm/smmu-v3.c +2 -3
-        return vmid;
+    /* Xen: Set the values as needed */
    -    cfg->vmid    = (u16)vmid;
-    cfg->vttbr    = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
-    cfg->vtcr    = pgtbl_cfg->arm_lpae_s2_cfg.vtcr;
+    cfg->vmid    = cfg->domain->arch.p2m.vmid;

See my comment above.

Quoted 1 line

I am wondering why we are considering this value invalid? Since the page tables are shared we can use the vmid from the domain. Is the concern regarding the size?

See above.

Cheers,

Signature · 6 lines
 Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel
#32 lore raw

Re: [RFC v2 7/7] xen/iommu: smmu-v3: Add Xen specific code to enable the ported driver

Hi Sameer,

Quoted 4 lines · Sameer
Quoted 3 lines · Julien Grall
Quoted 2 lines · Sameer
Quoted 1 line · Julien Grall

Please use #if 0 rather than removing the code + comment on top. But I am not sure why you drop the S2 free code. Shouldn't we allocate VMID from the SMMU?

Quoted 8 lines
Quoted 7 lines
Quoted 1 line

I am picking up the vmid from the domain I am sharing the page tables with. domain->arch.p2m.vmid. This seemed valid. Please let me know if we want to generate a different vmid?

The processor and the SMMU may support either 8 or 16bits VMID but I don't think any thing in the spec says that both processor and SMMU must support the same value.

So it would be possible to have a processor supporting 16-bits VMID and the SMMU only 8-bits VMID.

Adding a check at the SMMU initialization might be a solution. But as the code for allocating the VMID is already present, then I would prefer to we stick with different the VMID.

Sure. For this iteration I will generate an SMMU specific VMID. Do you think it would be an optimization to reuse the CPU VMID if SMMU supports 16bit VMID?

The only benefit I can see is if the SMMU support broadcast TLB maintenance. AFAICT, the driver would need to configure the SMMU for that.

However, I think we should first focus on getting a basic SMMUv3 driver in Xen. Once this is done, we can look at optimizing the driver.

Cheers,

Signature · 6 lines
Julien Grall

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel
#33 lore raw
In reply to Sameer Goel

Hi Sameer,

Quoted 25 lines · Sameer Goel

This change incoporates most of the review comments from [1] and adds the proposed SMMUv3 driver.

List of changes:
- Introduce the iommu_fwspec implementation - No change from the last RFC
- IORT port from linux. The differences are as under:
* Modified the code for creating the SMMU devices. This code also initializes the discoverd SMMU devices.
* MSI code is left as is, but this code is untested.
* IORT node data parsing is delegated to the driver. Looking for comments
on enabling the code in IORT driver. This will need a standard resource object. (Direct port from Linux or a new define for Xen?)
* Assumptions on PCI IORT SMMU interaction. PCI assign device will call iort_iommu_configure to setup the streamids.Then it will call SMMU assign device with the right struct device argument.
- SMMUv3 port from Linux. The list of changes are as under:
* The Xen iommu_ops list is at parity with SMMUv2.
* There is generally no need for an IOMMU group, but have kept a dummy define for now.
* Have commented out the S1 translation code.
* MSI code is commented out.
* Page table ops are commented out as the driver shares the page tables with the cpu.
* The list of SMMU devices is maintained from the driver code.

Quoted 2 lines

Open questions:
- IORT regeneration for DOM0. I was hoping to get some update on [2].

Please see v2 patch set
https://lists.xen.org/archives/html/xen-devel/2017-09/msg01143.html

Quoted 32 lines

- We also need a notification framework to get the Named node information from DSDT.
- Should we port over code for non-hsared page tables from the kernel or leverage [3].

[1] "[RFC 0/6] IORT support and introduce fwspec"
[2] "[Xen-devel] [RFC] [PATCH] arm-acpi: Hide SMMU from IORT for hardware domain"
[3] "Non-shared" IOMMU support on ARM"

Sameer Goel (7):
passthrough/arm: Modify SMMU driver to use generic device definition
arm64: Add definitions for fwnode_handle xen/passthrough/arm: Introduce iommu_fwspec
ACPI: arm: Support for IORT
acpi:arm64: Add support for parsing IORT table
Add verbatim copy of arm-smmu-v3.c from Linux
xen/iommu: smmu-v3: Add Xen specific code to enable the ported driver

  xen/arch/arm/setup.c                  |    3 +
  xen/drivers/acpi/Makefile             |    1 +
  xen/drivers/acpi/arm/Makefile         |    1 +
  xen/drivers/acpi/arm/iort.c           |  986 ++++++++++
  xen/drivers/passthrough/arm/Makefile  |    1 +
  xen/drivers/passthrough/arm/iommu.c   |   66 +
  xen/drivers/passthrough/arm/smmu-v3.c | 3412 +++++++++++++++++++++++++++++++++
  xen/drivers/passthrough/arm/smmu.c    |   13 +-
  xen/include/acpi/acpi_iort.h          |   61 +
  xen/include/asm-arm/device.h          |    5 +
  xen/include/xen/acpi.h                |   21 +
  xen/include/xen/fwnode.h              |   33 +
  xen/include/xen/iommu.h               |   29 +
  xen/include/xen/pci.h                 |    8 +
  14 files changed, 4634 insertions(+), 6 deletions(-)
Quoted 6 lines

create mode 100644 xen/drivers/acpi/arm/Makefile create mode 100644 xen/drivers/acpi/arm/iort.c create mode 100644 xen/drivers/passthrough/arm/smmu-v3.c create mode 100644 xen/include/acpi/acpi_iort.h create mode 100644 xen/include/xen/fwnode.h

_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel