PCI: dwc: Validate iATU outbound mappings against hardware constraints
authorSerge Semin <Sergey.Semin@baikalelectronics.ru>
Fri, 24 Jun 2022 14:39:45 +0000 (17:39 +0300)
committerBjorn Helgaas <bhelgaas@google.com>
Mon, 1 Aug 2022 20:15:32 +0000 (15:15 -0500)
Make __dw_pcie_prog_outbound_atu() check the requested region base and size
against what the hardware can support.  Return error if the region is not
correctly aligned or of a supported size.

[bhelgaas: commit log]
Link: https://lore.kernel.org/r/20220624143947.8991-14-Sergey.Semin@baikalelectronics.ru
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
drivers/pci/controller/dwc/pcie-designware.c
drivers/pci/controller/dwc/pcie-designware.h

index cb6244db92695381dcdaaba67bef0a23b390b235..c6725c519a479976f34c4dbb2973eec974fab66b 100644 (file)
@@ -8,6 +8,7 @@
  * Author: Jingoo Han <jg1.han@samsung.com>
  */
 
+#include <linux/align.h>
 #include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/of.h>
@@ -305,9 +306,9 @@ static inline u32 dw_pcie_enable_ecrc(u32 val)
        return val | PCIE_ATU_TD;
 }
 
-static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
-                                       int index, int type, u64 cpu_addr,
-                                       u64 pci_addr, u64 size)
+static int __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
+                                      int index, int type, u64 cpu_addr,
+                                      u64 pci_addr, u64 size)
 {
        u32 retries, val;
        u64 limit_addr;
@@ -317,6 +318,12 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
 
        limit_addr = cpu_addr + size - 1;
 
+       if ((limit_addr & ~pci->region_limit) != (cpu_addr & ~pci->region_limit) ||
+           !IS_ALIGNED(cpu_addr, pci->region_align) ||
+           !IS_ALIGNED(pci_addr, pci->region_align) || !size) {
+               return -EINVAL;
+       }
+
        dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_LOWER_BASE,
                              lower_32_bits(cpu_addr));
        dw_pcie_writel_atu_ob(pci, index, PCIE_ATU_UPPER_BASE,
@@ -350,27 +357,29 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
        for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
                val = dw_pcie_readl_atu_ob(pci, index, PCIE_ATU_REGION_CTRL2);
                if (val & PCIE_ATU_ENABLE)
-                       return;
+                       return 0;
 
                mdelay(LINK_WAIT_IATU);
        }
 
        dev_err(pci->dev, "Outbound iATU is not being enabled\n");
+
+       return -ETIMEDOUT;
 }
 
-void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
-                              u64 cpu_addr, u64 pci_addr, u64 size)
+int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
+                             u64 cpu_addr, u64 pci_addr, u64 size)
 {
-       __dw_pcie_prog_outbound_atu(pci, 0, index, type,
-                                   cpu_addr, pci_addr, size);
+       return __dw_pcie_prog_outbound_atu(pci, 0, index, type,
+                                          cpu_addr, pci_addr, size);
 }
 
-void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
-                                 int type, u64 cpu_addr, u64 pci_addr,
-                                 u64 size)
+int dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
+                                int type, u64 cpu_addr, u64 pci_addr,
+                                u64 size)
 {
-       __dw_pcie_prog_outbound_atu(pci, func_no, index, type,
-                                   cpu_addr, pci_addr, size);
+       return __dw_pcie_prog_outbound_atu(pci, func_no, index, type,
+                                          cpu_addr, pci_addr, size);
 }
 
 static inline u32 dw_pcie_readl_atu_ib(struct dw_pcie *pci, u32 index, u32 reg)
@@ -389,6 +398,9 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
 {
        u32 retries, val;
 
+       if (!IS_ALIGNED(cpu_addr, pci->region_align))
+               return -EINVAL;
+
        dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET,
                              lower_32_bits(cpu_addr));
        dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET,
index 25c86771c810fd3f4fb41e0f7a91d9998efb3c52..60f1ddc54933a4f3c2dd7e059e6a3bb87ac87cc5 100644 (file)
@@ -304,12 +304,10 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val);
 int dw_pcie_link_up(struct dw_pcie *pci);
 void dw_pcie_upconfig_setup(struct dw_pcie *pci);
 int dw_pcie_wait_for_link(struct dw_pcie *pci);
-void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index,
-                              int type, u64 cpu_addr, u64 pci_addr,
-                              u64 size);
-void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
-                                 int type, u64 cpu_addr, u64 pci_addr,
-                                 u64 size);
+int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
+                             u64 cpu_addr, u64 pci_addr, u64 size);
+int dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
+                                int type, u64 cpu_addr, u64 pci_addr, u64 size);
 int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
                             int type, u64 cpu_addr, u8 bar);
 void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);