@@ -33,6 +33,8 @@
#include "arm-smmu-v3.h"
#include "../../dma-iommu.h"
+u32 msi_iova_base;
+
static bool disable_msipolling;
module_param(disable_msipolling, bool, 0444);
MODULE_PARM_DESC(disable_msipolling,
@@ -3541,8 +3543,8 @@ static void arm_smmu_get_resv_regions(struct device *dev,
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, GFP_KERNEL);
+ region = iommu_alloc_resv_region(msi_iova_base, MSI_IOVA_LENGTH, prot,
+ IOMMU_RESV_SW_MSI, GFP_KERNEL);
if (!region)
return;
@@ -4570,6 +4572,9 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev,
struct device *dev = &pdev->dev;
u32 cells;
int ret = -EINVAL;
+ u32 msi_iova_ptr;
+
+ iommu_configure_msi_iova(dev, "arm,smmu-faulty-msi-iova", msi_iova_ptr);
if (of_property_read_u32(dev->of_node, "#iommu-cells", &cells))
dev_err(dev, "missing #iommu-cells property\n");
@@ -50,6 +50,8 @@
*/
#define QCOM_DUMMY_VAL -1
+u32 msi_iova_base;
+
static int force_stage;
module_param(force_stage, int, S_IRUGO);
MODULE_PARM_DESC(force_stage,
@@ -1594,8 +1596,8 @@ static void arm_smmu_get_resv_regions(struct device *dev,
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, GFP_KERNEL);
+ region = iommu_alloc_resv_region(msi_iova_base, MSI_IOVA_LENGTH, prot,
+ IOMMU_RESV_SW_MSI, GFP_KERNEL);
if (!region)
return;
@@ -2030,6 +2032,9 @@ static int arm_smmu_device_dt_probe(struct arm_smmu_device *smmu,
const struct arm_smmu_match_data *data;
struct device *dev = smmu->dev;
bool legacy_binding;
+ u32 *msi_iova_ptr = &msi_iova_base;
+
+ iommu_configure_msi_iova(dev, "arm,smmu-faulty-msi-iova", msi_iova_ptr);
if (of_property_read_u32(dev->of_node, "#global-interrupts", global_irqs))
return dev_err_probe(dev, -ENODEV,
@@ -1531,10 +1531,59 @@ static inline void iommu_debugfs_setup(void) {}
#ifdef CONFIG_IOMMU_DMA
#define MSI_IOVA_BASE 0x8000000
+#define MSI_IOVA_BASE2 0xa0000000
#define MSI_IOVA_LENGTH 0x100000
+static inline u32 select_msi_iova_base(u32 erratic_iova_addr)
+{
+ phys_addr_t start, end, msi_iova_end;
+
+ if (!erratic_iova_addr)
+ return MSI_IOVA_BASE;
+
+ start = erratic_iova_addr;
+ end = start + MSI_IOVA_LENGTH - 1;
+ msi_iova_end = MSI_IOVA_BASE + MSI_IOVA_LENGTH - 1;
+
+ /* return non-overlapping address */
+ return (start > MSI_IOVA_BASE ||
+ end < msi_iova_end) ? MSI_IOVA_BASE : MSI_IOVA_BASE2;
+}
+
+static inline void iommu_configure_msi_iova(struct device *iommu_dev,
+ const char *faulty_msi_iova_prop,
+ u32 *msi_iova)
+{
+ static bool is_msi_iova_selected;
+ u32 faulty_msi_iova_from_dt;
+ int rc;
+
+ rc = of_property_read_u32(iommu_dev->of_node, faulty_msi_iova_prop,
+ &faulty_msi_iova_from_dt);
+ if (!is_msi_iova_selected) {
+ *msi_iova = select_msi_iova_base(rc ? 0 : faulty_msi_iova_from_dt);
+ dev_dbg(iommu_dev, "setting custom MSI IOVA base to 0x%x\n", *msi_iova);
+ is_msi_iova_selected = true;
+ return;
+ }
+
+ dev_dbg(iommu_dev, "custom MSI IOVA base already set to 0x%x\n", *msi_iova);
+}
+
int iommu_get_msi_cookie(struct iommu_domain *domain, dma_addr_t base);
#else /* CONFIG_IOMMU_DMA */
+
+static inline u32 select_msi_iova_base(u32 erratic_iova_addr)
+{
+}
+
+static inline void iommu_configure_msi_iova(struct device *iommu_dev,
+ const char *faulty_msi_iova_prop,
+ u32 *msi_iova)
+{
+}
+
+
static inline int iommu_get_msi_cookie(struct iommu_domain *domain, dma_addr_t base)
{
return -ENODEV;
Currently ARM SMMU drivers hardcode PCI MSI IOVA address. Not all the platform have same memory mappings and some platform could have this address already being mapped for something else. This can lead to collision and as a consequence the MSI IOVA addr range is never reserved. Fix this by adding one more MSI_IOVA base address, so that if the platforms can select suitable PCI MSI IOVA address if SMMU dts node has "arm,smmu-faulty-msi-iova". If this property is not found in the dtb for the given platform then the driver falls back on the default MSI IOVA address. Signed-off-by: Shyam Saini <shyamsaini@linux.microsoft.com> --- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 9 +++- drivers/iommu/arm/arm-smmu/arm-smmu.c | 9 +++- include/linux/iommu.h | 49 +++++++++++++++++++++ 3 files changed, 63 insertions(+), 4 deletions(-)