Merge branch 'pci/endpoint'

- Destroy the EPC device in devm_pci_epc_destroy(), which previously didn't
  call devres_release() (Zijun Hu)

- Simplify pci_epc_get() with class_find_device_by_name() (Zijun Hu)

- Finish virtual EP removal in pci_epf_remove_vepf(), which previously
  caused a subsequent pci_epf_add_vepf() to fail with -EBUSY (Zijun Hu)

- Write BAR_MASK before iATU registers in pci_epc_set_bar() so we don't
  depend on the BAR_MASK reset value being larger than the requested BAR
  size (Niklas Cassel)

- Prevent changing BAR size/flags in pci_epc_set_bar() to prevent reads
  from bypassing the iATU if we reduced the BAR size (Niklas Cassel)

- Verify address alignment when programming iATU so we don't attempt to
  write bits that are read-only because of the BAR size, which could lead
  to directing accesses to the wrong address (Niklas Cassel)

- Implement artpec6 pci_epc_features so we can rely on all drivers
  supporting it so we can use it in EPC core code (Niklas Cassel)

- Check for BARs of fixed size to prevent endpoint drivers from trying to
  change their size (Niklas Cassel)

- Verify that requested BAR size is a power of two when endpoint driver
  sets the BAR (Niklas Cassel)

* pci/endpoint:
  PCI: endpoint: Verify that requested BAR size is a power of two
  PCI: endpoint: Add size check for fixed size BARs in pci_epc_set_bar()
  PCI: artpec6: Implement dw_pcie_ep operation get_features
  PCI: dwc: ep: Add 'address' alignment to 'size' check in dw_pcie_prog_ep_inbound_atu()
  PCI: dwc: ep: Prevent changing BAR size/flags in pci_epc_set_bar()
  PCI: dwc: ep: Write BAR_MASK before iATU registers in pci_epc_set_bar()
  PCI: endpoint: Finish virtual EP removal in pci_epf_remove_vepf()
  PCI: endpoint: Simplify pci_epc_get()
  PCI: endpoint: Destroy the EPC device in devm_pci_epc_destroy()
  PCI: endpoint: Replace magic number '6' by PCI_STD_NUM_BARS
This commit is contained in:
Bjorn Helgaas 2025-01-23 13:04:52 -06:00
commit 74855f6697
7 changed files with 78 additions and 38 deletions

View File

@ -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)

View File

@ -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;
@ -222,20 +223,31 @@ 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;
/*
* 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 BAR size and
* BAR flags do not 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;
/*
* 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;
}
reg = PCI_BASE_ADDRESS_0 + (4 * bar);
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;
if (ep->epf_bar[bar])
return 0;
dw_pcie_dbi_ro_wr_en(pci);
dw_pcie_ep_writel_dbi2(ep, func_no, reg, lower_32_bits(size - 1));
@ -246,9 +258,21 @@ 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,
size);
if (ret)
return ret;
ep->epf_bar[bar] = epf_bar;
return 0;
}

View File

@ -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,

View File

@ -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);

View File

@ -60,26 +60,17 @@ struct pci_epc *pci_epc_get(const char *epc_name)
int ret = -EINVAL;
struct pci_epc *epc;
struct device *dev;
struct class_dev_iter iter;
class_dev_iter_init(&iter, &pci_epc_class, NULL, NULL);
while ((dev = class_dev_iter_next(&iter))) {
if (strcmp(epc_name, dev_name(dev)))
continue;
dev = class_find_device_by_name(&pci_epc_class, epc_name);
if (!dev)
goto err;
epc = to_pci_epc(dev);
if (!try_module_get(epc->ops->owner)) {
ret = -EINVAL;
goto err;
}
class_dev_iter_exit(&iter);
get_device(&epc->dev);
epc = to_pci_epc(dev);
if (try_module_get(epc->ops->owner))
return epc;
}
err:
class_dev_iter_exit(&iter);
put_device(dev);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(pci_epc_get);
@ -609,10 +600,20 @@ 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 (!is_power_of_2(epf_bar->size))
return -EINVAL;
if ((epf_bar->barno == BAR_5 && flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ||
@ -942,7 +943,7 @@ void devm_pci_epc_destroy(struct device *dev, struct pci_epc *epc)
{
int r;
r = devres_destroy(dev, devm_pci_epc_release, devm_pci_epc_match,
r = devres_release(dev, devm_pci_epc_release, devm_pci_epc_match,
epc);
dev_WARN_ONCE(dev, r, "couldn't find PCI EPC resource\n");
}

View File

@ -202,6 +202,7 @@ void pci_epf_remove_vepf(struct pci_epf *epf_pf, struct pci_epf *epf_vf)
mutex_lock(&epf_pf->lock);
clear_bit(epf_vf->vfunc_no, &epf_pf->vfunction_num_map);
epf_vf->epf_pf = NULL;
list_del(&epf_vf->list);
mutex_unlock(&epf_pf->lock);
}

View File

@ -157,7 +157,7 @@ struct pci_epf {
struct device dev;
const char *name;
struct pci_epf_header *header;
struct pci_epf_bar bar[6];
struct pci_epf_bar bar[PCI_STD_NUM_BARS];
u8 msi_interrupts;
u16 msix_interrupts;
u8 func_no;
@ -174,7 +174,7 @@ struct pci_epf {
/* Below members are to attach secondary EPC to an endpoint function */
struct pci_epc *sec_epc;
struct list_head sec_epc_list;
struct pci_epf_bar sec_epc_bar[6];
struct pci_epf_bar sec_epc_bar[PCI_STD_NUM_BARS];
u8 sec_epc_func_no;
struct config_group *group;
unsigned int is_bound;