* [PATCH v4 1/5] PCI: dwc: ep: iATU registers must be written after the BAR_MASK
2024-11-22 11:57 [PATCH v4 0/5] PCI endpoint additional pci_epc_set_bar() checks Niklas Cassel
@ 2024-11-22 11:57 ` Niklas Cassel
2024-11-27 7:04 ` Manivannan Sadhasivam
2024-11-22 11:57 ` [PATCH v4 2/5] PCI: dwc: ep: Add 'address' alignment to 'size' check in dw_pcie_prog_ep_inbound_atu() Niklas Cassel
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Niklas Cassel @ 2024-11-22 11:57 UTC (permalink / raw)
To: Jingoo Han, Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
Kishon Vijay Abraham I
Cc: Damien Le Moal, Frank Li, Jesper Nilsson, Niklas Cassel,
linux-pci
The DWC Databook description for the LWR_TARGET_RW and LWR_TARGET_HW fields
in the IATU_LWR_TARGET_ADDR_OFF_INBOUND_i registers state that:
"Field size depends on log2(BAR_MASK+1) in BAR match mode."
I.e. only the upper bits are writable, and the number of writable bits is
dependent on the configured BAR_MASK.
If we do not write the BAR_MASK before writing the iATU registers, we are
relying the reset value of the BAR_MASK being larger than the requested
size of the first set_bar() call. The reset value of the BAR_MASK is SoC
dependent.
Thus, if the first set_bar() call requests a size that is larger than the
reset value of the BAR_MASK, the iATU will try to write to read-only bits,
which will cause the iATU to end up redirecting to a physical address that
is different from the address that was intended.
Thus, we should always write the iATU registers after writing the BAR_MASK.
Since set_bar() supports dynamically changing the physical address of a
BAR, this change is slightly bigger than a single line change.
While at it, add comments which clarifies the support for dynamically
changing the physical address of a BAR. (Which was previously missing.)
Fixes: f8aed6ec624f ("PCI: dwc: designware: Add EP mode support")
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
.../pci/controller/dwc/pcie-designware-ep.c | 46 ++++++++++++++-----
1 file changed, 34 insertions(+), 12 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 507e40bd18c8..34d60142ffb5 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -222,19 +222,30 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
if ((flags & PCI_BASE_ADDRESS_MEM_TYPE_64) && (bar & 1))
return -EINVAL;
- reg = PCI_BASE_ADDRESS_0 + (4 * bar);
-
- if (!(flags & PCI_BASE_ADDRESS_SPACE))
- type = PCIE_ATU_TYPE_MEM;
- else
- type = PCIE_ATU_TYPE_IO;
+ /*
+ * Certain EPF drivers dynamically change the physical address of a BAR
+ * (i.e. they call set_bar() twice, without ever calling clear_bar(), as
+ * calling clear_bar() would clear the BAR's PCI address assigned by the
+ * host).
+ */
+ if (ep->epf_bar[bar]) {
+ /*
+ * We can only dynamically change a BAR if the new configuration
+ * doesn't fundamentally differ from the existing configuration.
+ */
+ if (ep->epf_bar[bar]->barno != bar ||
+ ep->epf_bar[bar]->size != size ||
+ ep->epf_bar[bar]->flags != flags)
+ return -EINVAL;
- ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar);
- if (ret)
- return ret;
+ /*
+ * When dynamically changing a BAR, skip writing the BAR reg, as
+ * that would clear the BAR's PCI address assigned by the host.
+ */
+ goto config_atu;
+ }
- if (ep->epf_bar[bar])
- return 0;
+ reg = PCI_BASE_ADDRESS_0 + (4 * bar);
dw_pcie_dbi_ro_wr_en(pci);
@@ -246,9 +257,20 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
dw_pcie_ep_writel_dbi(ep, func_no, reg + 4, 0);
}
- ep->epf_bar[bar] = epf_bar;
dw_pcie_dbi_ro_wr_dis(pci);
+config_atu:
+ if (!(flags & PCI_BASE_ADDRESS_SPACE))
+ type = PCIE_ATU_TYPE_MEM;
+ else
+ type = PCIE_ATU_TYPE_IO;
+
+ ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar);
+ if (ret)
+ return ret;
+
+ ep->epf_bar[bar] = epf_bar;
+
return 0;
}
--
2.47.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v4 1/5] PCI: dwc: ep: iATU registers must be written after the BAR_MASK
2024-11-22 11:57 ` [PATCH v4 1/5] PCI: dwc: ep: iATU registers must be written after the BAR_MASK Niklas Cassel
@ 2024-11-27 7:04 ` Manivannan Sadhasivam
0 siblings, 0 replies; 11+ messages in thread
From: Manivannan Sadhasivam @ 2024-11-27 7:04 UTC (permalink / raw)
To: Niklas Cassel
Cc: Jingoo Han, Lorenzo Pieralisi, Krzysztof Wilczyński,
Rob Herring, Bjorn Helgaas, Kishon Vijay Abraham I,
Damien Le Moal, Frank Li, Jesper Nilsson, linux-pci
On Fri, Nov 22, 2024 at 12:57:10PM +0100, Niklas Cassel wrote:
> The DWC Databook description for the LWR_TARGET_RW and LWR_TARGET_HW fields
> in the IATU_LWR_TARGET_ADDR_OFF_INBOUND_i registers state that:
> "Field size depends on log2(BAR_MASK+1) in BAR match mode."
>
> I.e. only the upper bits are writable, and the number of writable bits is
> dependent on the configured BAR_MASK.
>
> If we do not write the BAR_MASK before writing the iATU registers, we are
> relying the reset value of the BAR_MASK being larger than the requested
> size of the first set_bar() call. The reset value of the BAR_MASK is SoC
> dependent.
>
> Thus, if the first set_bar() call requests a size that is larger than the
> reset value of the BAR_MASK, the iATU will try to write to read-only bits,
> which will cause the iATU to end up redirecting to a physical address that
> is different from the address that was intended.
>
> Thus, we should always write the iATU registers after writing the BAR_MASK.
>
> Since set_bar() supports dynamically changing the physical address of a
> BAR, this change is slightly bigger than a single line change.
>
> While at it, add comments which clarifies the support for dynamically
> changing the physical address of a BAR. (Which was previously missing.)
>
This is not strictly relevant to this patch, so should be separate.
> Fixes: f8aed6ec624f ("PCI: dwc: designware: Add EP mode support")
> Signed-off-by: Niklas Cassel <cassel@kernel.org>
LGTM! This patch should only have the iATU config change and should be
backported also.
- Mani
> ---
> .../pci/controller/dwc/pcie-designware-ep.c | 46 ++++++++++++++-----
> 1 file changed, 34 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> index 507e40bd18c8..34d60142ffb5 100644
> --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> @@ -222,19 +222,30 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> if ((flags & PCI_BASE_ADDRESS_MEM_TYPE_64) && (bar & 1))
> return -EINVAL;
>
> - reg = PCI_BASE_ADDRESS_0 + (4 * bar);
> -
> - if (!(flags & PCI_BASE_ADDRESS_SPACE))
> - type = PCIE_ATU_TYPE_MEM;
> - else
> - type = PCIE_ATU_TYPE_IO;
> + /*
> + * Certain EPF drivers dynamically change the physical address of a BAR
> + * (i.e. they call set_bar() twice, without ever calling clear_bar(), as
> + * calling clear_bar() would clear the BAR's PCI address assigned by the
> + * host).
> + */
> + if (ep->epf_bar[bar]) {
> + /*
> + * We can only dynamically change a BAR if the new configuration
> + * doesn't fundamentally differ from the existing configuration.
> + */
> + if (ep->epf_bar[bar]->barno != bar ||
> + ep->epf_bar[bar]->size != size ||
> + ep->epf_bar[bar]->flags != flags)
> + return -EINVAL;
>
> - ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar);
> - if (ret)
> - return ret;
> + /*
> + * When dynamically changing a BAR, skip writing the BAR reg, as
> + * that would clear the BAR's PCI address assigned by the host.
> + */
> + goto config_atu;
> + }
>
> - if (ep->epf_bar[bar])
> - return 0;
> + reg = PCI_BASE_ADDRESS_0 + (4 * bar);
>
> dw_pcie_dbi_ro_wr_en(pci);
>
> @@ -246,9 +257,20 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> dw_pcie_ep_writel_dbi(ep, func_no, reg + 4, 0);
> }
>
> - ep->epf_bar[bar] = epf_bar;
> dw_pcie_dbi_ro_wr_dis(pci);
>
> +config_atu:
> + if (!(flags & PCI_BASE_ADDRESS_SPACE))
> + type = PCIE_ATU_TYPE_MEM;
> + else
> + type = PCIE_ATU_TYPE_IO;
> +
> + ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar);
> + if (ret)
> + return ret;
> +
> + ep->epf_bar[bar] = epf_bar;
> +
> return 0;
> }
>
> --
> 2.47.0
>
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v4 2/5] PCI: dwc: ep: Add 'address' alignment to 'size' check in dw_pcie_prog_ep_inbound_atu()
2024-11-22 11:57 [PATCH v4 0/5] PCI endpoint additional pci_epc_set_bar() checks Niklas Cassel
2024-11-22 11:57 ` [PATCH v4 1/5] PCI: dwc: ep: iATU registers must be written after the BAR_MASK Niklas Cassel
@ 2024-11-22 11:57 ` Niklas Cassel
2024-11-27 7:11 ` Manivannan Sadhasivam
2024-11-22 11:57 ` [PATCH v4 3/5] PCI: artpec6: Implement dw_pcie_ep operation get_features Niklas Cassel
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Niklas Cassel @ 2024-11-22 11:57 UTC (permalink / raw)
To: Jingoo Han, Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas
Cc: Damien Le Moal, Frank Li, Jesper Nilsson, Niklas Cassel,
linux-pci
dw_pcie_prog_ep_inbound_atu() is used to program an inbound iATU in
"BAR Match Mode".
A memory address returned by e.g. kmalloc() is guaranteed to have natural
alignment (aligned to the size of the allocation). It is however not
guaranteed that pci_epc_set_bar() (and thus dw_pcie_prog_ep_inbound_atu())
is supplied an address that has natural alignment. (An EPF driver can send
in an arbitrary physical address to pci_epc_set_bar().)
The DWC Databook description for the LWR_TARGET_RW and LWR_TARGET_HW fields
in the IATU_LWR_TARGET_ADDR_OFF_INBOUND_i registers state that:
"Field size depends on log2(BAR_MASK+1) in BAR match mode."
I.e. only the upper bits are writable, and the number of writable bits is
dependent on the configured BAR_MASK.
Add a check to ensure that the physical address programmed in the iATU is
aligned to the size of the BAR (BAR_MASK+1), as without this, we can get
hard to debug errors, as we could write to bits that are read-only (without
getting a write error), which could cause the iATU to end up redirecting to
a physical address that is different from the address that we intended.
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
drivers/pci/controller/dwc/pcie-designware-ep.c | 8 +++++---
drivers/pci/controller/dwc/pcie-designware.c | 5 +++--
drivers/pci/controller/dwc/pcie-designware.h | 2 +-
3 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 34d60142ffb5..ff4350e7adb6 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -128,7 +128,8 @@ static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
}
static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
- dma_addr_t cpu_addr, enum pci_barno bar)
+ dma_addr_t cpu_addr, enum pci_barno bar,
+ size_t size)
{
int ret;
u32 free_win;
@@ -145,7 +146,7 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
}
ret = dw_pcie_prog_ep_inbound_atu(pci, func_no, free_win, type,
- cpu_addr, bar);
+ cpu_addr, bar, size);
if (ret < 0) {
dev_err(pci->dev, "Failed to program IB window\n");
return ret;
@@ -265,7 +266,8 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
else
type = PCIE_ATU_TYPE_IO;
- ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar);
+ ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar,
+ size);
if (ret)
return ret;
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 6d6cbc8b5b2c..3c683b6119c3 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -597,11 +597,12 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
}
int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
- int type, u64 cpu_addr, u8 bar)
+ int type, u64 cpu_addr, u8 bar, size_t size)
{
u32 retries, val;
- if (!IS_ALIGNED(cpu_addr, pci->region_align))
+ if (!IS_ALIGNED(cpu_addr, pci->region_align) ||
+ !IS_ALIGNED(cpu_addr, size))
return -EINVAL;
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET,
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 347ab74ac35a..fc0872711672 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -491,7 +491,7 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
u64 cpu_addr, u64 pci_addr, u64 size);
int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
- int type, u64 cpu_addr, u8 bar);
+ int type, u64 cpu_addr, u8 bar, size_t size);
void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);
void dw_pcie_setup(struct dw_pcie *pci);
void dw_pcie_iatu_detect(struct dw_pcie *pci);
--
2.47.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v4 2/5] PCI: dwc: ep: Add 'address' alignment to 'size' check in dw_pcie_prog_ep_inbound_atu()
2024-11-22 11:57 ` [PATCH v4 2/5] PCI: dwc: ep: Add 'address' alignment to 'size' check in dw_pcie_prog_ep_inbound_atu() Niklas Cassel
@ 2024-11-27 7:11 ` Manivannan Sadhasivam
0 siblings, 0 replies; 11+ messages in thread
From: Manivannan Sadhasivam @ 2024-11-27 7:11 UTC (permalink / raw)
To: Niklas Cassel
Cc: Jingoo Han, Lorenzo Pieralisi, Krzysztof Wilczyński,
Rob Herring, Bjorn Helgaas, Damien Le Moal, Frank Li,
Jesper Nilsson, linux-pci
On Fri, Nov 22, 2024 at 12:57:11PM +0100, Niklas Cassel wrote:
> dw_pcie_prog_ep_inbound_atu() is used to program an inbound iATU in
> "BAR Match Mode".
>
> A memory address returned by e.g. kmalloc() is guaranteed to have natural
> alignment (aligned to the size of the allocation). It is however not
> guaranteed that pci_epc_set_bar() (and thus dw_pcie_prog_ep_inbound_atu())
> is supplied an address that has natural alignment. (An EPF driver can send
> in an arbitrary physical address to pci_epc_set_bar().)
>
> The DWC Databook description for the LWR_TARGET_RW and LWR_TARGET_HW fields
> in the IATU_LWR_TARGET_ADDR_OFF_INBOUND_i registers state that:
> "Field size depends on log2(BAR_MASK+1) in BAR match mode."
>
> I.e. only the upper bits are writable, and the number of writable bits is
> dependent on the configured BAR_MASK.
>
> Add a check to ensure that the physical address programmed in the iATU is
> aligned to the size of the BAR (BAR_MASK+1), as without this, we can get
> hard to debug errors, as we could write to bits that are read-only (without
> getting a write error), which could cause the iATU to end up redirecting to
> a physical address that is different from the address that we intended.
>
> Signed-off-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
- Mani
> ---
> drivers/pci/controller/dwc/pcie-designware-ep.c | 8 +++++---
> drivers/pci/controller/dwc/pcie-designware.c | 5 +++--
> drivers/pci/controller/dwc/pcie-designware.h | 2 +-
> 3 files changed, 9 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> index 34d60142ffb5..ff4350e7adb6 100644
> --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> @@ -128,7 +128,8 @@ static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> }
>
> static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
> - dma_addr_t cpu_addr, enum pci_barno bar)
> + dma_addr_t cpu_addr, enum pci_barno bar,
> + size_t size)
> {
> int ret;
> u32 free_win;
> @@ -145,7 +146,7 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
> }
>
> ret = dw_pcie_prog_ep_inbound_atu(pci, func_no, free_win, type,
> - cpu_addr, bar);
> + cpu_addr, bar, size);
> if (ret < 0) {
> dev_err(pci->dev, "Failed to program IB window\n");
> return ret;
> @@ -265,7 +266,8 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> else
> type = PCIE_ATU_TYPE_IO;
>
> - ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar);
> + ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar,
> + size);
> if (ret)
> return ret;
>
> diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
> index 6d6cbc8b5b2c..3c683b6119c3 100644
> --- a/drivers/pci/controller/dwc/pcie-designware.c
> +++ b/drivers/pci/controller/dwc/pcie-designware.c
> @@ -597,11 +597,12 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
> }
>
> int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
> - int type, u64 cpu_addr, u8 bar)
> + int type, u64 cpu_addr, u8 bar, size_t size)
> {
> u32 retries, val;
>
> - if (!IS_ALIGNED(cpu_addr, pci->region_align))
> + if (!IS_ALIGNED(cpu_addr, pci->region_align) ||
> + !IS_ALIGNED(cpu_addr, size))
> return -EINVAL;
>
> dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET,
> diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
> index 347ab74ac35a..fc0872711672 100644
> --- a/drivers/pci/controller/dwc/pcie-designware.h
> +++ b/drivers/pci/controller/dwc/pcie-designware.h
> @@ -491,7 +491,7 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
> int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
> u64 cpu_addr, u64 pci_addr, u64 size);
> int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
> - int type, u64 cpu_addr, u8 bar);
> + int type, u64 cpu_addr, u8 bar, size_t size);
> void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);
> void dw_pcie_setup(struct dw_pcie *pci);
> void dw_pcie_iatu_detect(struct dw_pcie *pci);
> --
> 2.47.0
>
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v4 3/5] PCI: artpec6: Implement dw_pcie_ep operation get_features
2024-11-22 11:57 [PATCH v4 0/5] PCI endpoint additional pci_epc_set_bar() checks Niklas Cassel
2024-11-22 11:57 ` [PATCH v4 1/5] PCI: dwc: ep: iATU registers must be written after the BAR_MASK Niklas Cassel
2024-11-22 11:57 ` [PATCH v4 2/5] PCI: dwc: ep: Add 'address' alignment to 'size' check in dw_pcie_prog_ep_inbound_atu() Niklas Cassel
@ 2024-11-22 11:57 ` Niklas Cassel
2024-11-27 7:12 ` Manivannan Sadhasivam
2024-11-22 11:57 ` [PATCH v4 4/5] PCI: endpoint: Add size check for fixed size BARs in pci_epc_set_bar() Niklas Cassel
2024-11-22 11:57 ` [PATCH v4 5/5] PCI: endpoint: Verify that requested BAR size is a power of two Niklas Cassel
4 siblings, 1 reply; 11+ messages in thread
From: Niklas Cassel @ 2024-11-22 11:57 UTC (permalink / raw)
To: Jesper Nilsson, Lorenzo Pieralisi, Krzysztof Wilczyński,
Manivannan Sadhasivam, Rob Herring, Bjorn Helgaas
Cc: Damien Le Moal, Frank Li, Niklas Cassel, linux-arm-kernel,
linux-pci
All non-DWC EPC drivers implement (struct pci_epc *)->ops->get_features().
All DWC EPC drivers implement (struct dw_pcie_ep *)->ops->get_features(),
except for pcie-artpec6.c.
epc_features has been required in pci-epf-test.c since commit 6613bc2301ba
("PCI: endpoint: Fix NULL pointer dereference for ->get_features()").
A follow-up commit will make further use of epc_features in EPC core code.
Implement epc_features in the only EPC driver where it is currently not
implemented.
Signed-off-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Acked-by: Jesper Nilsson <jesper.nilsson@axis.com>
---
drivers/pci/controller/dwc/pcie-artpec6.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c
index f8e7283dacd4..234c8cbcae3a 100644
--- a/drivers/pci/controller/dwc/pcie-artpec6.c
+++ b/drivers/pci/controller/dwc/pcie-artpec6.c
@@ -369,9 +369,22 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
return 0;
}
+static const struct pci_epc_features artpec6_pcie_epc_features = {
+ .linkup_notifier = false,
+ .msi_capable = true,
+ .msix_capable = false,
+};
+
+static const struct pci_epc_features *
+artpec6_pcie_get_features(struct dw_pcie_ep *ep)
+{
+ return &artpec6_pcie_epc_features;
+}
+
static const struct dw_pcie_ep_ops pcie_ep_ops = {
.init = artpec6_pcie_ep_init,
.raise_irq = artpec6_pcie_raise_irq,
+ .get_features = artpec6_pcie_get_features,
};
static int artpec6_pcie_probe(struct platform_device *pdev)
--
2.47.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v4 3/5] PCI: artpec6: Implement dw_pcie_ep operation get_features
2024-11-22 11:57 ` [PATCH v4 3/5] PCI: artpec6: Implement dw_pcie_ep operation get_features Niklas Cassel
@ 2024-11-27 7:12 ` Manivannan Sadhasivam
0 siblings, 0 replies; 11+ messages in thread
From: Manivannan Sadhasivam @ 2024-11-27 7:12 UTC (permalink / raw)
To: Niklas Cassel
Cc: Jesper Nilsson, Lorenzo Pieralisi, Krzysztof Wilczyński,
Rob Herring, Bjorn Helgaas, Damien Le Moal, Frank Li,
linux-arm-kernel, linux-pci
On Fri, Nov 22, 2024 at 12:57:12PM +0100, Niklas Cassel wrote:
> All non-DWC EPC drivers implement (struct pci_epc *)->ops->get_features().
> All DWC EPC drivers implement (struct dw_pcie_ep *)->ops->get_features(),
> except for pcie-artpec6.c.
>
> epc_features has been required in pci-epf-test.c since commit 6613bc2301ba
> ("PCI: endpoint: Fix NULL pointer dereference for ->get_features()").
>
> A follow-up commit will make further use of epc_features in EPC core code.
>
> Implement epc_features in the only EPC driver where it is currently not
> implemented.
>
> Signed-off-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
- Mani
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Acked-by: Jesper Nilsson <jesper.nilsson@axis.com>
> ---
> drivers/pci/controller/dwc/pcie-artpec6.c | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
>
> diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c
> index f8e7283dacd4..234c8cbcae3a 100644
> --- a/drivers/pci/controller/dwc/pcie-artpec6.c
> +++ b/drivers/pci/controller/dwc/pcie-artpec6.c
> @@ -369,9 +369,22 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
> return 0;
> }
>
> +static const struct pci_epc_features artpec6_pcie_epc_features = {
> + .linkup_notifier = false,
> + .msi_capable = true,
> + .msix_capable = false,
> +};
> +
> +static const struct pci_epc_features *
> +artpec6_pcie_get_features(struct dw_pcie_ep *ep)
> +{
> + return &artpec6_pcie_epc_features;
> +}
> +
> static const struct dw_pcie_ep_ops pcie_ep_ops = {
> .init = artpec6_pcie_ep_init,
> .raise_irq = artpec6_pcie_raise_irq,
> + .get_features = artpec6_pcie_get_features,
> };
>
> static int artpec6_pcie_probe(struct platform_device *pdev)
> --
> 2.47.0
>
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v4 4/5] PCI: endpoint: Add size check for fixed size BARs in pci_epc_set_bar()
2024-11-22 11:57 [PATCH v4 0/5] PCI endpoint additional pci_epc_set_bar() checks Niklas Cassel
` (2 preceding siblings ...)
2024-11-22 11:57 ` [PATCH v4 3/5] PCI: artpec6: Implement dw_pcie_ep operation get_features Niklas Cassel
@ 2024-11-22 11:57 ` Niklas Cassel
2024-11-27 7:13 ` Manivannan Sadhasivam
2024-11-22 11:57 ` [PATCH v4 5/5] PCI: endpoint: Verify that requested BAR size is a power of two Niklas Cassel
4 siblings, 1 reply; 11+ messages in thread
From: Niklas Cassel @ 2024-11-22 11:57 UTC (permalink / raw)
To: Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas
Cc: Damien Le Moal, Frank Li, Jesper Nilsson, Niklas Cassel,
linux-pci
A BAR of type BAR_FIXED has a fixed BAR size (the size cannot be changed).
When using pci_epf_alloc_space() to allocate backing memory for a BAR,
pci_epf_alloc_space() will always set the size to the fixed BAR size if
the BAR type is BAR_FIXED (and will give an error if you the requested size
is larger than the fixed BAR size).
However, some drivers might not call pci_epf_alloc_space() before calling
pci_epc_set_bar(), so add a check in pci_epc_set_bar() to ensure that an
EPF driver cannot set a size different from the fixed BAR size, if the BAR
type is BAR_FIXED.
The pci_epc_function_is_valid() check is removed because this check is now
done by pci_epc_get_features().
Signed-off-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/pci/endpoint/pci-epc-core.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index bed7c7d1fe3c..c69c133701c9 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -609,10 +609,17 @@ EXPORT_SYMBOL_GPL(pci_epc_clear_bar);
int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
struct pci_epf_bar *epf_bar)
{
- int ret;
+ const struct pci_epc_features *epc_features;
+ enum pci_barno bar = epf_bar->barno;
int flags = epf_bar->flags;
+ int ret;
- if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
+ epc_features = pci_epc_get_features(epc, func_no, vfunc_no);
+ if (!epc_features)
+ return -EINVAL;
+
+ if (epc_features->bar[bar].type == BAR_FIXED &&
+ (epc_features->bar[bar].fixed_size != epf_bar->size))
return -EINVAL;
if ((epf_bar->barno == BAR_5 && flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ||
--
2.47.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v4 4/5] PCI: endpoint: Add size check for fixed size BARs in pci_epc_set_bar()
2024-11-22 11:57 ` [PATCH v4 4/5] PCI: endpoint: Add size check for fixed size BARs in pci_epc_set_bar() Niklas Cassel
@ 2024-11-27 7:13 ` Manivannan Sadhasivam
0 siblings, 0 replies; 11+ messages in thread
From: Manivannan Sadhasivam @ 2024-11-27 7:13 UTC (permalink / raw)
To: Niklas Cassel
Cc: Krzysztof Wilczyński, Kishon Vijay Abraham I, Bjorn Helgaas,
Damien Le Moal, Frank Li, Jesper Nilsson, linux-pci
On Fri, Nov 22, 2024 at 12:57:13PM +0100, Niklas Cassel wrote:
> A BAR of type BAR_FIXED has a fixed BAR size (the size cannot be changed).
>
> When using pci_epf_alloc_space() to allocate backing memory for a BAR,
> pci_epf_alloc_space() will always set the size to the fixed BAR size if
> the BAR type is BAR_FIXED (and will give an error if you the requested size
> is larger than the fixed BAR size).
>
> However, some drivers might not call pci_epf_alloc_space() before calling
> pci_epc_set_bar(), so add a check in pci_epc_set_bar() to ensure that an
> EPF driver cannot set a size different from the fixed BAR size, if the BAR
> type is BAR_FIXED.
>
> The pci_epc_function_is_valid() check is removed because this check is now
> done by pci_epc_get_features().
>
> Signed-off-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
- Mani
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
> ---
> drivers/pci/endpoint/pci-epc-core.c | 11 +++++++++--
> 1 file changed, 9 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
> index bed7c7d1fe3c..c69c133701c9 100644
> --- a/drivers/pci/endpoint/pci-epc-core.c
> +++ b/drivers/pci/endpoint/pci-epc-core.c
> @@ -609,10 +609,17 @@ EXPORT_SYMBOL_GPL(pci_epc_clear_bar);
> int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> struct pci_epf_bar *epf_bar)
> {
> - int ret;
> + const struct pci_epc_features *epc_features;
> + enum pci_barno bar = epf_bar->barno;
> int flags = epf_bar->flags;
> + int ret;
>
> - if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
> + epc_features = pci_epc_get_features(epc, func_no, vfunc_no);
> + if (!epc_features)
> + return -EINVAL;
> +
> + if (epc_features->bar[bar].type == BAR_FIXED &&
> + (epc_features->bar[bar].fixed_size != epf_bar->size))
> return -EINVAL;
>
> if ((epf_bar->barno == BAR_5 && flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ||
> --
> 2.47.0
>
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v4 5/5] PCI: endpoint: Verify that requested BAR size is a power of two
2024-11-22 11:57 [PATCH v4 0/5] PCI endpoint additional pci_epc_set_bar() checks Niklas Cassel
` (3 preceding siblings ...)
2024-11-22 11:57 ` [PATCH v4 4/5] PCI: endpoint: Add size check for fixed size BARs in pci_epc_set_bar() Niklas Cassel
@ 2024-11-22 11:57 ` Niklas Cassel
2024-11-27 7:14 ` Manivannan Sadhasivam
4 siblings, 1 reply; 11+ messages in thread
From: Niklas Cassel @ 2024-11-22 11:57 UTC (permalink / raw)
To: Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas
Cc: Damien Le Moal, Frank Li, Jesper Nilsson, Niklas Cassel,
linux-pci
When allocating a BAR using pci_epf_alloc_space(), there are checks that
round up the size to a power of two.
However, there is no check in pci_epc_set_bar() which verifies that the
requested BAR size is a power of two.
Add a power of two check in pci_epc_set_bar(), so that we don't need to
add such a check in each and every PCI endpoint controller driver.
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
drivers/pci/endpoint/pci-epc-core.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index c69c133701c9..6062677e9ffe 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -622,6 +622,9 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
(epc_features->bar[bar].fixed_size != epf_bar->size))
return -EINVAL;
+ if (!is_power_of_2(epf_bar->size))
+ return -EINVAL;
+
if ((epf_bar->barno == BAR_5 && flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ||
(flags & PCI_BASE_ADDRESS_SPACE_IO &&
flags & PCI_BASE_ADDRESS_IO_MASK) ||
--
2.47.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v4 5/5] PCI: endpoint: Verify that requested BAR size is a power of two
2024-11-22 11:57 ` [PATCH v4 5/5] PCI: endpoint: Verify that requested BAR size is a power of two Niklas Cassel
@ 2024-11-27 7:14 ` Manivannan Sadhasivam
0 siblings, 0 replies; 11+ messages in thread
From: Manivannan Sadhasivam @ 2024-11-27 7:14 UTC (permalink / raw)
To: Niklas Cassel
Cc: Krzysztof Wilczyński, Kishon Vijay Abraham I, Bjorn Helgaas,
Damien Le Moal, Frank Li, Jesper Nilsson, linux-pci
On Fri, Nov 22, 2024 at 12:57:14PM +0100, Niklas Cassel wrote:
> When allocating a BAR using pci_epf_alloc_space(), there are checks that
> round up the size to a power of two.
>
> However, there is no check in pci_epc_set_bar() which verifies that the
> requested BAR size is a power of two.
>
> Add a power of two check in pci_epc_set_bar(), so that we don't need to
> add such a check in each and every PCI endpoint controller driver.
>
> Signed-off-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
- Mani
> ---
> drivers/pci/endpoint/pci-epc-core.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
> index c69c133701c9..6062677e9ffe 100644
> --- a/drivers/pci/endpoint/pci-epc-core.c
> +++ b/drivers/pci/endpoint/pci-epc-core.c
> @@ -622,6 +622,9 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> (epc_features->bar[bar].fixed_size != epf_bar->size))
> return -EINVAL;
>
> + if (!is_power_of_2(epf_bar->size))
> + return -EINVAL;
> +
> if ((epf_bar->barno == BAR_5 && flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ||
> (flags & PCI_BASE_ADDRESS_SPACE_IO &&
> flags & PCI_BASE_ADDRESS_IO_MASK) ||
> --
> 2.47.0
>
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply [flat|nested] 11+ messages in thread