SMMUv3 driver and the supporting framework
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
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
@@ -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
RFC v2 2/7 arm64: Add definitions for fwnode_handle
In reply to Sameer GoelThis 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
@@ -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
@@ -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
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_handleSigned-off-by Sameer Goel <[email protected]>--- 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.hxen/include/asm-arm/device.h +1
-0@@ -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
-0struct dev_archdata archdata; };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").
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
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.hin 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_handleSigned-off-by Sameer Goel <[email protected]>---
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@@ -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_TREEQuoted 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_nodehere.Quoted 2 lines
#endif
+ structfwnode_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_archdataarchdata;
};
diff --gita/xen/include/xen/fwnode.hb/xen/include/xen/fwnode.hnew file mode 100644
index 0000000..0fed958xen/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_typealready disappeared from Linux (see commitdb3e50f3234b"device property: Get rid of structfwnode_handletype 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
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.hin 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_handleSigned-off-by Sameer Goel <[email protected]>--- 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.hxen/include/asm-arm/device.h +1
-0@@ -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_nodehere.
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
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.hin 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_handleSigned-off-by Sameer Goel <[email protected]>---
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.hxen/include/asm-arm/device.h +1
-0@@ -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_TREEQuoted 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_nodehere.
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
Quoted 2 lines
#endif
+ structfwnode_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
RFC v2 3/7 xen/passthrough/arm: Introduce iommu_fwspec
In reply to Sameer GoelIntroduce 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
@@ -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
@@ -21,6 +21,7 @@ struct devicestruct 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
@@ -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
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: Introduceiommu_fwspecSigned-off-by Sameer Goel <[email protected]>--- 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
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@@ -21,6 +21,7 @@ struct devicestruct 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
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
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: Introduceiommu_fwspecSigned-off-by Sameer Goel <[email protected]>---
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@@ -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_handleQuoted 3 lines
*fwnode)
+{
+ returniommu_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 structiommu_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
Re: [RFC v2 3/7] xen/passthrough/arm: Introduce iommu_fwspec
In reply to Julien GrallQuoted 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: Introduceiommu_fwspecSigned-off-by Sameer Goel <[email protected]>---
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
-0xen/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@@ -21,6 +21,7 @@ struct devicestruct
dt_device_node*of_node; /* Used by drivers imported from Linux */ #endif
structfwnode_handle*fwnode; /*fw device node identifier */ + structiommu_fwspec*iommu_fwspec;
structdev_archdataarchdata;
};
diff --gita/xen/include/xen/iommu.hb/xen/include/xen/iommu.hindex 0dac4f3..34e8d68 100644xen/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
RFC v2 4/7 ACPI: arm: Support for IORT
In reply to Sameer GoelVerbatim 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
@@ -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
RFC v2 5/7 acpi:arm64: Add support for parsing IORT table
In reply to Sameer GoelAdd 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
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
Signed-off-by Sameer Goel <[email protected]>--- 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@@ -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@@ -1,5 +1,6 @@ subdir-y += tables subdir-y += utilities +subdir-$(CONFIG_ARM) += arm subdir-$(CONFIG_X86) += apei obj-bin-y += tables.init.oxen/drivers/acpi/arm/Makefile new file +1
-0@@ -0,0 +1 @@ +obj-y += iort.oxen/drivers/acpi/arm/iort.c +99
-74@@ -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@@ -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@@ -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@@ -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@@ -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@@ -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
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,
ManishQuoted 480 lines
Signed-off-by Sameer Goel <[email protected]>---
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@@ -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 100644xen/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.oxen/drivers/acpi/arm/Makefile new file +1
-0@@ -0,0 +1 @@ +obj-y += iort.oxen/drivers/acpi/arm/iort.c +8
-0@@ -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) { structacpi_iort_root_complex*pci_rc;
- structpci_bus*bus;
+ structpci_dev*pci_dev;
- bus =to_pci_bus(dev);
+pci_dev=to_pci_dev(dev);
pci_rc= (structacpi_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_idfunction
+ */
+#if 0
static
structacpi_iort_node*iort_node_get_id(structacpi_iort_node*node, u32*id_out, u8type_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 structacpi_iort_node*iort_find_dev_node(struct device *dev) {
structpci_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 0int 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__initacpi_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@@ -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@@ -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) intiort_register_domain_token(int trans_id, struct fwnode_handle *fw_node); voidiort_deregister_domain_token(int trans_id);
structfwnode_handle*iort_find_domain_token(inttrans_id);xen/include/acpi/acpi_iort.h +2
-1-#ifdef CONFIG_ACPI_IORT +#endif +#ifdef CONFIG_ARM_64 void
acpi_iort_init(void);
booliort_node_match(u8 type);
+#if 0
u32iort_msi_map_rid(struct device *dev, u32 req_id);
structirq_domain*iort_get_device_domain(struct device *dev, u32req_id); /* IOMMU interface */
voidiort_set_dma_mask(struct device *dev);
+#endif
const structiommu_ops*iort_iommu_configure(struct device *dev); #else
static inline voidacpi_iort_init(void){ }
static inline booliort_node_match(u8 type){ return false; } +#if 0
static inline u32iort_msi_map_rid(struct device *dev, u32 req_id){ returnreq_id; }
static inline structirq_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 structiommu_ops*iort_iommu_configure(struct device *dev) { return NULL; }
#endif
-#defineIORT_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@@ -7,6 +7,7 @@enum
device_type
{
DEV_DT,
+DEV_ACPI,
};
structdev_archdata{xen/include/asm-arm/device.h +0
-0@@ -20,6 +21,7 @@ struct device #ifdef CONFIG_HAS_DEVICE_TREEstruct
dt_device_node*of_node; /* Used by drivers imported from Linux */ #endif
+ void*acpi_node; /*Current use case isacpi_iort_node*/ structfwnode_handle*fwnode; /*fw device node identifier */ structiommu_fwspec*iommu_fwspec;
structdev_archdataarchdata;xen/include/xen/acpi.h +20
-0@@ -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@@ -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_ARMxen/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
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,
ManishQuoted 480 lines
Signed-off-by Sameer Goel <[email protected]>--- 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@@ -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 100644xen/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.oxen/drivers/acpi/arm/Makefile new file +1
-0@@ -0,0 +1 @@ +obj-y += iort.oxen/drivers/acpi/arm/iort.c +50
-11@@ -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_idfunction
+ */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@@ -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@@ -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@@ -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@@ -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@@ -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
Re: [RFC v2 5/7] acpi:arm64: Add support for parsing IORT table
In reply to Sameer GoelHi 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.Signed-off-by Sameer Goel <[email protected]>--- 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@@ -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@@ -1,5 +1,6 @@ subdir-y += tables subdir-y += utilities +subdir-$(CONFIG_ARM) += arm subdir-$(CONFIG_X86) += apei obj-bin-y += tables.init.oxen/drivers/acpi/arm/Makefile new file +1
-0@@ -0,0 +1 @@ +obj-y += iort.oxen/drivers/acpi/arm/iort.c +8
-1@@ -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
-2pci_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
-0static 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
-2u32 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
-1node =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@@ -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
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
-3void 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@@ -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
-0struct fwnode_handle *fwnode;/*fw device node identifier */ struct iommu_fwspec *iommu_fwspec;struct dev_archdata archdata;xen/include/xen/acpi.h +1
-0@@ -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
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
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.Signed-off-by Sameer Goel <[email protected]>---
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@@ -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 100644xen/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.oxen/drivers/acpi/arm/Makefile new file +1
-0@@ -0,0 +1 @@ +obj-y += iort.oxen/drivers/acpi/arm/iort.c +8
-0@@ -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) { structacpi_iort_root_complex*pci_rc;
- structpci_bus*bus;
+ structpci_dev*pci_dev;Do you really need to modify the code? Wouldn't it be possible to do
Quoted 1 line
#define
pci_buspci_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= (structacpi_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_idfunctionMissing full stop + TODO.
Ok.
Quoted 10 lines
Quoted 10 lines
+ */
+#if 0
static
structacpi_iort_node*iort_node_get_id(structacpi_iort_node*node, u32*id_out, u8type_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
-0static 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 structacpi_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
-0static 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
-1u32 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
-1node = 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__initacpi_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@@ -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_devWhy 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
-0You 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); voidiort_deregister_domain_token(int trans_id);
structfwnode_handle*iort_find_domain_token(inttrans_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_IORTwithCONFIG_ARM_64. You should instead take advantage of theKconfigto add a new configACPI_IORTand 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);
booliort_node_match(u8 type);
+#if 0
u32iort_msi_map_rid(struct device *dev, u32 req_id);
structirq_domain*iort_get_device_domain(struct device *dev, u32req_id); /* IOMMU interface */
voidiort_set_dma_mask(struct device *dev);
+#endif
const structiommu_ops*iort_iommu_configure(struct device *dev); #else
static inline voidacpi_iort_init(void){ }
static inline booliort_node_match(u8 type){ return false; } +#if 0
static inline u32iort_msi_map_rid(struct device *dev, u32 req_id){ returnreq_id; }
static inline structirq_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 structiommu_ops*iort_iommu_configure(struct device *dev) { return NULL; }
#endif
-#defineIORT_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@@ -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_TREEstruct
dt_device_node*of_node; /* Used by drivers imported from Linux */ #endif
+ void*acpi_node; /*Current use case isacpi_iort_node*/Can you explain why you need that? After the creation of fwnode, I was expecting
of_nodeto disappear. So I don't really fancy seeacpi_nodehere 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 */ structiommu_fwspec*iommu_fwspec;
structdev_archdataarchdata;xen/include/xen/acpi.h +1
-0@@ -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
-0xen/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_devinasm-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_ARMxen/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
+
#definefor_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
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_ONQuoted 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_buspci_devThe
pci_devis 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@@ -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_devWhy this change?
Quoted 1 line
I had redefine the
to_pci_devto get the actualpci_devstruct. 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
Re: [RFC v2 5/7] acpi:arm64: Add support for parsing IORT table
In reply to Sameer GoelHi 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
Re: [RFC v2 5/7] acpi:arm64: Add support for parsing IORT table
In reply to Sameer GoelHi 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.Signed-off-by Sameer Goel <[email protected]>
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@@ -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@@ -1,5 +1,6 @@ subdir-y += tables subdir-y += utilities +subdir-$(CONFIG_ARM) += arm subdir-$(CONFIG_X86) += apei obj-bin-y += tables.init.oxen/drivers/acpi/arm/Makefile new file +1
-0@@ -0,0 +1 @@ +obj-y += iort.oxen/drivers/acpi/arm/iort.c +99
-74@@ -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@@ -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@@ -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@@ -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@@ -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@@ -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
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.Signed-off-by Sameer Goel <[email protected]>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.htmlI 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
ManishQuoted 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@@ -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 100644xen/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.oxen/drivers/acpi/arm/Makefile new file +1
-0@@ -0,0 +1 @@ +obj-y += iort.oxen/drivers/acpi/arm/iort.c +8
-0@@ -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) { structacpi_iort_root_complex*pci_rc;
- structpci_bus*bus;
+ structpci_dev*pci_dev;
- bus =to_pci_bus(dev);
+pci_dev=to_pci_dev(dev);
pci_rc= (structacpi_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_idfunction
+ */
+#if 0
static
structacpi_iort_node*iort_node_get_id(structacpi_iort_node*node, u32*id_out, u8type_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 structacpi_iort_node*iort_find_dev_node(struct device *dev) {
structpci_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 0int 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__initacpi_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@@ -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@@ -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) intiort_register_domain_token(int trans_id, struct fwnode_handle *fw_node); voidiort_deregister_domain_token(int trans_id);
structfwnode_handle*iort_find_domain_token(inttrans_id);xen/include/acpi/acpi_iort.h +2
-1-#ifdef CONFIG_ACPI_IORT +#endif +#ifdef CONFIG_ARM_64 void
acpi_iort_init(void);
booliort_node_match(u8 type);
+#if 0
u32iort_msi_map_rid(struct device *dev, u32 req_id);
structirq_domain*iort_get_device_domain(struct device *dev, u32req_id); /* IOMMU interface */
voidiort_set_dma_mask(struct device *dev);
+#endif
const structiommu_ops*iort_iommu_configure(struct device *dev); #else
static inline voidacpi_iort_init(void){ }
static inline booliort_node_match(u8 type){ return false; } +#if 0
static inline u32iort_msi_map_rid(struct device *dev, u32 req_id){ returnreq_id; }
static inline structirq_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 structiommu_ops*iort_iommu_configure(struct device *dev) { return NULL; }
#endif
-#defineIORT_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@@ -7,6 +7,7 @@enum
device_type
{
DEV_DT,
+DEV_ACPI,
};
structdev_archdata{xen/include/asm-arm/device.h +0
-0@@ -20,6 +21,7 @@ struct device #ifdef CONFIG_HAS_DEVICE_TREEstruct
dt_device_node*of_node; /* Used by drivers imported from Linux */ #endif
+ void*acpi_node; /*Current use case isacpi_iort_node*/ structfwnode_handle*fwnode; /*fw device node identifier */ structiommu_fwspec*iommu_fwspec;
structdev_archdataarchdata;xen/include/xen/acpi.h +20
-0@@ -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@@ -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_ARMxen/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
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.Signed-off-by Sameer Goel <[email protected]>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.htmlI 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
RFC v2 6/7 Add verbatim copy of arm-smmu-v3.c from Linux
In reply to Sameer GoelBased 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
@@ -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 (®ion->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
RFC v2 7/7 xen/iommu: smmu-v3: Add Xen specific code to enable the ported driver
In reply to Sameer GoelThis 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
@@ -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
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 */Signed-off-by Sameer Goel <[email protected]>--- 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@@ -1,2 +1,3 @@ obj-y += iommu.o obj-y += smmu.o +obj-y += smmu-v3.oxen/drivers/passthrough/arm/smmu-v3.c +475
-116@@ -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_matchstatic 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
Re: [RFC v2 7/7] xen/iommu: smmu-v3: Add Xen specific code to enable the ported driver
In reply to Sameer GoelHi 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 */Signed-off-by Sameer Goel <[email protected]>--- 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@@ -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@@ -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
-0static 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
-1while (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
-1return -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
-1wfe (); - }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
-1udelay (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
-1break ;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
-0val |=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
-2strtab = &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
-1sid, 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
-16static 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
-4kfree (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
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_maintainersto 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 */Signed-off-by Sameer Goel <[email protected]>---
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@@ -1,2 +1,3 @@obj-y += iommu.o
obj-y += smmu.o
+obj-y += smmu-v3.oDo we want SMMUv3 to be built on Arm32? Maybe we should introduce a new
Kconfigto 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@@ -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_nodexen/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_unlockmutex 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 commonWARN_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_tref;
+ /* Used to linkiommu_domaincontexts 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_IDR00x0xen/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
enumpri_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
-1struct 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 */
structarm_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
-1while (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
-0return -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
-1wfe(); - } 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 Xencpu_relaxis 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
-1udelay(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 XenWhy 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
-0val |= 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
-2strtab = &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);
_xzalloccan fail andvirt_to_maddrwill 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
-1static 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
-0sid, 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 voidarm_smmu_tlb_sync(void *cookie){
structarm_smmu_domain*smmu_domain= cookie;__arm_smmu_tlb_sync(smmu_domain->smmu); }
+#endif
static voidarm_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 voidarm_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) 0Please 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
-0kfree(smmu_domain); }+#if 0
static intarm_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
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_nodexen/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_unlockmutex 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, theIORESOURCE_IRQimplementation 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 commonWARN_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 Xencpu_relaxis 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);
_xzalloccan fail andvirt_to_maddrwill 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
-1static 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 forARM_64which 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 0Please explain the #if 0
Done.
Quoted 31 lines
Quoted 31 lines
xen/drivers/passthrough/arm/smmu-v3.c +1
-20static 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
-4kfree(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
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 Xencpu_relaxis 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
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 Xencpu_relaxis 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
Re: [RFC v2 7/7] xen/iommu: smmu-v3: Add Xen specific code to enable the ported driver
In reply to Julien GrallQuoted 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_nodexen/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_unlockmutex 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, theIORESOURCE_IRQimplementation 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 commonWARN_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_ONCEin 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 Xencpu_relaxis 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);
_xzalloccan fail andvirt_to_maddrwill 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 forARM_64which 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 structiommu_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
-0kfree(smmu_domain); }+#if 0
static intarm_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
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
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 theiommu_fwspecimplementation - 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 calliort_iommu_configureto 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 Xeniommu_opslist 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 forfwnode_handlexen/passthrough/arm: Introduceiommu_fwspec
ACPI: arm: Support for IORT
acpi:arm64: Add support for parsing IORT table
Add verbatim copy ofarm-smmu-v3.cfrom Linux
xen/iommu: smmu-v3: Add Xen specific code to enable the ported driverxen/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.ccreate mode 100644xen/drivers/passthrough/arm/smmu-v3.ccreate mode 100644xen/include/acpi/acpi_iort.hcreate mode 100644xen/include/xen/fwnode.h
_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xen.org/xen-devel