[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v3 3/4] xen/arm: add support for PCI child bus
On Wed, 9 Apr 2025, Mykyta Poturai wrote: > From: Oleksandr Andrushchenko <oleksandr_andrushchenko@xxxxxxxx> > > PCI host bridges often have different ways to access the root and child > bus configuration spaces. One of the examples is Designware's host bridge > and its multiple clones [1]. > > Linux kernel implements this by instantiating a child bus when device > drivers provide not only the usual pci_ops to access ECAM space (this is > the case for the generic host bridge), but also means to access the child > bus which has a dedicated configuration space and own implementation for > accessing the bus, e.g. child_ops. > > For Xen it is not feasible to fully implement PCI bus infrastructure as > Linux kernel does, but still child bus can be supported. > > Add support for the PCI child bus which includes the following changes: > - introduce bus mapping functions depending on SBDF > - assign bus start and end for the child bus and re-configure the same for > the parent (root) bus > - make pci_find_host_bridge be aware of multiple busses behind the same bridge > - update pci_host_bridge_mappings, so it also doesn't map to guest the memory > spaces belonging to the child bus > - make pci_host_common_probe accept one more pci_ops structure for the child > bus > - install MMIO handlers for the child bus > - re-work vpci_mmio_{write|read} with parent and child approach in mind > > [1] https://elixir.bootlin.com/linux/v5.15/source/drivers/pci/controller/dwc > > Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@xxxxxxxx> > Signed-off-by: Mykyta Poturai <mykyta_poturai@xxxxxxxx> > --- > v2->v3: > * no change > > v1->v2: > * fixed compilation issues > --- > xen/arch/arm/include/asm/pci.h | 12 +++- > xen/arch/arm/pci/ecam.c | 17 ++++-- > xen/arch/arm/pci/pci-access.c | 37 +++++++++++-- > xen/arch/arm/pci/pci-host-common.c | 86 +++++++++++++++++++++++------ > xen/arch/arm/pci/pci-host-generic.c | 2 +- > xen/arch/arm/pci/pci-host-zynqmp.c | 2 +- > xen/arch/arm/vpci.c | 83 ++++++++++++++++++++++------ > 7 files changed, 192 insertions(+), 47 deletions(-) > > diff --git a/xen/arch/arm/include/asm/pci.h b/xen/arch/arm/include/asm/pci.h > index 3d2ca9b5b0..addec31791 100644 > --- a/xen/arch/arm/include/asm/pci.h > +++ b/xen/arch/arm/include/asm/pci.h > @@ -67,6 +67,9 @@ struct pci_host_bridge { > uint16_t segment; /* Segment number */ > struct pci_config_window* cfg; /* Pointer to the bridge config window > */ > const struct pci_ops *ops; > + /* Child bus */ > + struct pci_config_window *child_cfg; > + const struct pci_ops *child_ops; > void *priv; /* Private data of the bridge. */ > }; > > @@ -96,14 +99,19 @@ struct pci_ecam_ops { > /* Default ECAM ops */ > extern const struct pci_ecam_ops pci_generic_ecam_ops; > > -struct pci_host_bridge *pci_host_common_probe(struct dt_device_node *dev, > - const struct pci_ecam_ops > *ops); > +struct pci_host_bridge * > +pci_host_common_probe(struct dt_device_node *dev, > + const struct pci_ecam_ops *ops, > + const struct pci_ecam_ops *child_ops); > int pci_generic_config_read(struct pci_host_bridge *bridge, pci_sbdf_t sbdf, > uint32_t reg, uint32_t len, uint32_t *value); > int pci_generic_config_write(struct pci_host_bridge *bridge, pci_sbdf_t sbdf, > uint32_t reg, uint32_t len, uint32_t value); > void __iomem *pci_ecam_map_bus(struct pci_host_bridge *bridge, > pci_sbdf_t sbdf, uint32_t where); > +void __iomem *pci_ecam_map_bus_generic(const struct pci_config_window *cfg, > + const struct pci_ecam_ops *ops, > + pci_sbdf_t sbdf, uint32_t where); > bool pci_ecam_need_p2m_hwdom_mapping(struct domain *d, > struct pci_host_bridge *bridge, > uint64_t addr); > diff --git a/xen/arch/arm/pci/ecam.c b/xen/arch/arm/pci/ecam.c > index 3987f96b01..ad0b2c35f9 100644 > --- a/xen/arch/arm/pci/ecam.c > +++ b/xen/arch/arm/pci/ecam.c > @@ -20,12 +20,10 @@ > /* > * Function to implement the pci_ops->map_bus method. > */ > -void __iomem *pci_ecam_map_bus(struct pci_host_bridge *bridge, > - pci_sbdf_t sbdf, uint32_t where) > +void __iomem *pci_ecam_map_bus_generic(const struct pci_config_window *cfg, > + const struct pci_ecam_ops *ops, > + pci_sbdf_t sbdf, uint32_t where) > { > - const struct pci_config_window *cfg = bridge->cfg; > - const struct pci_ecam_ops *ops = > - container_of(bridge->ops, const struct pci_ecam_ops, pci_ops); > unsigned int devfn_shift = ops->bus_shift - 8; > void __iomem *base; > unsigned int busn = sbdf.bus; > @@ -39,6 +37,15 @@ void __iomem *pci_ecam_map_bus(struct pci_host_bridge > *bridge, > return base + (sbdf.devfn << devfn_shift) + where; > } > > +void __iomem *pci_ecam_map_bus(struct pci_host_bridge *bridge, pci_sbdf_t > sbdf, > + uint32_t where) > +{ > + const struct pci_ecam_ops *ops = > + container_of(bridge->ops, const struct pci_ecam_ops, pci_ops); > + > + return pci_ecam_map_bus_generic(bridge->cfg, ops, sbdf, where); > +} It doesn't look like this change is necessary? It doesn't seem that pci_ecam_map_bus_generic is used anywhere? > bool __init pci_ecam_need_p2m_hwdom_mapping(struct domain *d, > struct pci_host_bridge *bridge, > uint64_t addr) > diff --git a/xen/arch/arm/pci/pci-access.c b/xen/arch/arm/pci/pci-access.c > index 9f9aac43d7..cc4787f2b5 100644 > --- a/xen/arch/arm/pci/pci-access.c > +++ b/xen/arch/arm/pci/pci-access.c > @@ -18,10 +18,31 @@ > #define INVALID_VALUE (~0U) > #define PCI_ERR_VALUE(len) GENMASK(0, len * 8) > > +static const struct pci_ops *get_ops(struct pci_host_bridge *bridge, > + pci_sbdf_t sbdf) > +{ > + if ( bridge->child_ops ) > + { > + struct pci_config_window *cfg = bridge->child_cfg; > + > + if ( (sbdf.bus >= cfg->busn_start) && (sbdf.bus <= cfg->busn_end) ) > + return bridge->child_ops; > + } > + return bridge->ops; > +} > + > +static void __iomem *map_bus(struct pci_host_bridge *bridge, pci_sbdf_t sbdf, > + uint32_t reg) NIT: I would do static inline here > +{ > + const struct pci_ops *ops = get_ops(bridge, sbdf); > + > + return ops->map_bus(bridge, sbdf, reg); > +} > + > int pci_generic_config_read(struct pci_host_bridge *bridge, pci_sbdf_t sbdf, > uint32_t reg, uint32_t len, uint32_t *value) > { > - void __iomem *addr = bridge->ops->map_bus(bridge, sbdf, reg); > + void __iomem *addr = map_bus(bridge, sbdf, reg); > > if ( !addr ) > { > @@ -50,7 +71,7 @@ int pci_generic_config_read(struct pci_host_bridge *bridge, > pci_sbdf_t sbdf, > int pci_generic_config_write(struct pci_host_bridge *bridge, pci_sbdf_t sbdf, > uint32_t reg, uint32_t len, uint32_t value) > { > - void __iomem *addr = bridge->ops->map_bus(bridge, sbdf, reg); > + void __iomem *addr = map_bus(bridge, sbdf, reg); > > if ( !addr ) > return -ENODEV; > @@ -78,14 +99,16 @@ static uint32_t pci_config_read(pci_sbdf_t sbdf, unsigned > int reg, > { > uint32_t val = PCI_ERR_VALUE(len); > struct pci_host_bridge *bridge = pci_find_host_bridge(sbdf.seg, > sbdf.bus); > + const struct pci_ops *ops; > > if ( unlikely(!bridge) ) > return val; > > - if ( unlikely(!bridge->ops->read) ) > + ops = get_ops(bridge, sbdf); > + if ( unlikely(!ops->read) ) > return val; > > - bridge->ops->read(bridge, sbdf, reg, len, &val); > + ops->read(bridge, sbdf, reg, len, &val); > > return val; > } > @@ -94,14 +117,16 @@ static void pci_config_write(pci_sbdf_t sbdf, unsigned > int reg, > unsigned int len, uint32_t val) > { > struct pci_host_bridge *bridge = pci_find_host_bridge(sbdf.seg, > sbdf.bus); > + const struct pci_ops *ops; > > if ( unlikely(!bridge) ) > return; > > - if ( unlikely(!bridge->ops->write) ) > + ops = get_ops(bridge, sbdf); > + if ( unlikely(!ops->write) ) > return; > > - bridge->ops->write(bridge, sbdf, reg, len, val); > + ops->write(bridge, sbdf, reg, len, val); > } > > /* > diff --git a/xen/arch/arm/pci/pci-host-common.c > b/xen/arch/arm/pci/pci-host-common.c > index 7ce9675551..2b058b5f5e 100644 > --- a/xen/arch/arm/pci/pci-host-common.c > +++ b/xen/arch/arm/pci/pci-host-common.c > @@ -57,17 +57,12 @@ static void pci_ecam_free(struct pci_config_window *cfg) > xfree(cfg); > } > > -static struct pci_config_window * __init > -gen_pci_init(struct dt_device_node *dev, const struct pci_ecam_ops *ops) > +static void __init gen_pci_init_bus_range(struct dt_device_node *dev, > + struct pci_host_bridge *bridge, > + struct pci_config_window *cfg) > { > - int err, cfg_reg_idx; > u32 bus_range[2]; > - paddr_t addr, size; > - struct pci_config_window *cfg; > - > - cfg = xzalloc(struct pci_config_window); > - if ( !cfg ) > - return NULL; > + int err; > > err = dt_property_read_u32_array(dev, "bus-range", bus_range, > ARRAY_SIZE(bus_range)); > @@ -82,6 +77,36 @@ gen_pci_init(struct dt_device_node *dev, const struct > pci_ecam_ops *ops) > if ( cfg->busn_end > cfg->busn_start + 0xff ) > cfg->busn_end = cfg->busn_start + 0xff; > } > +} > + > +static void __init gen_pci_init_bus_range_child(struct dt_device_node *dev, > + struct pci_host_bridge > *bridge, > + struct pci_config_window > *cfg) > +{ > + cfg->busn_start = bridge->cfg->busn_start + 1; > + cfg->busn_end = bridge->cfg->busn_end; > + bridge->cfg->busn_end = bridge->cfg->busn_start; > + > + printk(XENLOG_INFO "Root bus end updated: [bus %x-%x]\n", > + bridge->cfg->busn_start, bridge->cfg->busn_end); > +} > + > +static struct pci_config_window *__init > +gen_pci_init(struct dt_device_node *dev, struct pci_host_bridge *bridge, > + const struct pci_ecam_ops *ops, > + void (*init_bus_range)(struct dt_device_node *dev, > + struct pci_host_bridge *bridge, > + struct pci_config_window *cfg)) > +{ > + int err, cfg_reg_idx; > + paddr_t addr, size; > + struct pci_config_window *cfg; > + > + cfg = xzalloc(struct pci_config_window); > + if ( !cfg ) > + return NULL; > + > + init_bus_range(dev, bridge, cfg); > > if ( ops->cfg_reg_index ) > { > @@ -208,8 +233,10 @@ static int pci_bus_find_domain_nr(struct dt_device_node > *dev) > return domain; > } > > -struct pci_host_bridge *pci_host_common_probe(struct dt_device_node *dev, > - const struct pci_ecam_ops *ops) > +struct pci_host_bridge * > +pci_host_common_probe(struct dt_device_node *dev, > + const struct pci_ecam_ops *ops, > + const struct pci_ecam_ops *child_ops) > { > struct pci_host_bridge *bridge; > struct pci_config_window *cfg; > @@ -224,7 +251,7 @@ struct pci_host_bridge *pci_host_common_probe(struct > dt_device_node *dev, > return ERR_PTR(-ENOMEM); > > /* Parse and map our Configuration Space windows */ > - cfg = gen_pci_init(dev, ops); > + cfg = gen_pci_init(dev, bridge, ops, gen_pci_init_bus_range); > if ( !cfg ) > { > err = -ENOMEM; > @@ -242,10 +269,29 @@ struct pci_host_bridge *pci_host_common_probe(struct > dt_device_node *dev, > BUG(); > } > bridge->segment = domain; > + > + if ( child_ops ) > + { > + /* Parse and map child's Configuration Space windows */ > + cfg = gen_pci_init(dev, bridge, child_ops, > + gen_pci_init_bus_range_child); > + if ( !cfg ) > + { > + err = -ENOMEM; > + goto err_child; > + } > + > + bridge->child_cfg = cfg; > + bridge->child_ops = &child_ops->pci_ops; > + } > + > pci_add_host_bridge(bridge); > > return bridge; > > +err_child: > + xfree(bridge->cfg); > + > err_exit: > xfree(bridge); > > @@ -280,9 +326,12 @@ struct pci_host_bridge *pci_find_host_bridge(uint16_t > segment, uint8_t bus) > { > if ( bridge->segment != segment ) > continue; > - if ( (bus < bridge->cfg->busn_start) || (bus > > bridge->cfg->busn_end) ) > - continue; > - return bridge; > + if ( bridge->child_cfg && (bus >= bridge->child_cfg->busn_start) && > + (bus <= bridge->child_cfg->busn_end) ) > + return bridge; > + if ( (bus >= bridge->cfg->busn_start) && > + (bus <= bridge->cfg->busn_end) ) > + return bridge; > } > > return NULL; > @@ -348,6 +397,7 @@ int __init pci_host_bridge_mappings(struct domain *d) > { > const struct dt_device_node *dev = bridge->dt_node; > unsigned int i; > + bool need_mapping; > > for ( i = 0; i < dt_number_of_address(dev); i++ ) > { > @@ -363,7 +413,11 @@ int __init pci_host_bridge_mappings(struct domain *d) > return err; > } > > - if ( bridge->ops->need_p2m_hwdom_mapping(d, bridge, addr) ) > + need_mapping = bridge->ops->need_p2m_hwdom_mapping(d, bridge, > addr); > + if ( need_mapping && bridge->child_ops ) > + need_mapping = > + bridge->child_ops->need_p2m_hwdom_mapping(d, bridge, > addr); Is this because the child bus address range is always a subset of the parent address range? If not, we would need to check either of them independently (|| instead of &&). > + if ( need_mapping ) > { > err = map_range_to_domain(dev, addr, size, &mr_data); > if ( err ) > diff --git a/xen/arch/arm/pci/pci-host-generic.c > b/xen/arch/arm/pci/pci-host-generic.c > index a6ffbda174..47cf144831 100644 > --- a/xen/arch/arm/pci/pci-host-generic.c > +++ b/xen/arch/arm/pci/pci-host-generic.c > @@ -29,7 +29,7 @@ static const struct dt_device_match __initconstrel > gen_pci_dt_match[] = > static int __init pci_host_generic_probe(struct dt_device_node *dev, > const void *data) > { > - return PTR_RET(pci_host_common_probe(dev, &pci_generic_ecam_ops)); > + return PTR_RET(pci_host_common_probe(dev, &pci_generic_ecam_ops, NULL)); > } > > DT_DEVICE_START(pci_gen, "PCI HOST GENERIC", DEVICE_PCI_HOSTBRIDGE) > diff --git a/xen/arch/arm/pci/pci-host-zynqmp.c > b/xen/arch/arm/pci/pci-host-zynqmp.c > index a38f2e019e..75d1235aaf 100644 > --- a/xen/arch/arm/pci/pci-host-zynqmp.c > +++ b/xen/arch/arm/pci/pci-host-zynqmp.c > @@ -47,7 +47,7 @@ static const struct dt_device_match __initconstrel > nwl_pcie_dt_match[] = > static int __init pci_host_generic_probe(struct dt_device_node *dev, > const void *data) > { > - return PTR_RET(pci_host_common_probe(dev, &nwl_pcie_ops)); > + return PTR_RET(pci_host_common_probe(dev, &nwl_pcie_ops, NULL)); > } > > DT_DEVICE_START(pci_gen, "PCI HOST ZYNQMP", DEVICE_PCI_HOSTBRIDGE) > diff --git a/xen/arch/arm/vpci.c b/xen/arch/arm/vpci.c > index b63a356bb4..468aee1db7 100644 > --- a/xen/arch/arm/vpci.c > +++ b/xen/arch/arm/vpci.c > @@ -8,15 +8,17 @@ > #include <asm/mmio.h> > > static pci_sbdf_t vpci_sbdf_from_gpa(const struct pci_host_bridge *bridge, > - paddr_t gpa) > + paddr_t gpa, bool use_root) > { > pci_sbdf_t sbdf; > > if ( bridge ) > { > - sbdf.sbdf = VPCI_ECAM_BDF(gpa - bridge->cfg->phys_addr); > + const struct pci_config_window *cfg = use_root ? bridge->cfg > + : bridge->child_cfg; > + sbdf.sbdf = VPCI_ECAM_BDF(gpa - cfg->phys_addr); > sbdf.seg = bridge->segment; > - sbdf.bus += bridge->cfg->busn_start; > + sbdf.bus += cfg->busn_start; > } > else > sbdf.sbdf = VPCI_ECAM_BDF(gpa - GUEST_VPCI_ECAM_BASE); > @@ -24,11 +26,9 @@ static pci_sbdf_t vpci_sbdf_from_gpa(const struct > pci_host_bridge *bridge, > return sbdf; > } > > -static int vpci_mmio_read(struct vcpu *v, mmio_info_t *info, > - register_t *r, void *p) > +static int vpci_mmio_read(struct vcpu *v, mmio_info_t *info, register_t *r, > + pci_sbdf_t sbdf) > { > - struct pci_host_bridge *bridge = p; > - pci_sbdf_t sbdf = vpci_sbdf_from_gpa(bridge, info->gpa); > const unsigned int access_size = (1U << info->dabt.size) * 8; > const register_t invalid = GENMASK_ULL(access_size - 1, 0); > /* data is needed to prevent a pointer cast on 32bit */ > @@ -46,31 +46,78 @@ static int vpci_mmio_read(struct vcpu *v, mmio_info_t > *info, > return 0; > } > > -static int vpci_mmio_write(struct vcpu *v, mmio_info_t *info, > - register_t r, void *p) > +static int vpci_mmio_read_root(struct vcpu *v, mmio_info_t *info, register_t > *r, > + void *p) > +{ > + struct pci_host_bridge *bridge = p; > + pci_sbdf_t sbdf = vpci_sbdf_from_gpa(bridge, info->gpa, true); > + > + return vpci_mmio_read(v, info, r, sbdf); > +} > + > +static int vpci_mmio_read_child(struct vcpu *v, mmio_info_t *info, > + register_t *r, void *p) > { > struct pci_host_bridge *bridge = p; > - pci_sbdf_t sbdf = vpci_sbdf_from_gpa(bridge, info->gpa); > + pci_sbdf_t sbdf = vpci_sbdf_from_gpa(bridge, info->gpa, false); > + > + return vpci_mmio_read(v, info, r, sbdf); > +} > > +static int vpci_mmio_write(struct vcpu *v, mmio_info_t *info, register_t r, > + pci_sbdf_t sbdf) > +{ > return vpci_ecam_write(sbdf, ECAM_REG_OFFSET(info->gpa), > 1U << info->dabt.size, r); > } > > +static int vpci_mmio_write_root(struct vcpu *v, mmio_info_t *info, > register_t r, > + void *p) > +{ > + struct pci_host_bridge *bridge = p; > + pci_sbdf_t sbdf = vpci_sbdf_from_gpa(bridge, info->gpa, true); > + > + return vpci_mmio_write(v, info, r, sbdf); > +} > + > +static int vpci_mmio_write_child(struct vcpu *v, mmio_info_t *info, > + register_t r, void *p) > +{ > + struct pci_host_bridge *bridge = p; > + pci_sbdf_t sbdf = vpci_sbdf_from_gpa(bridge, info->gpa, false); > + > + return vpci_mmio_write(v, info, r, sbdf); > +} > + > static const struct mmio_handler_ops vpci_mmio_handler = { > - .read = vpci_mmio_read, > - .write = vpci_mmio_write, > + .read = vpci_mmio_read_root, > + .write = vpci_mmio_write_root, > +}; > + > +static const struct mmio_handler_ops vpci_mmio_handler_child = { > + .read = vpci_mmio_read_child, > + .write = vpci_mmio_write_child, > }; > > static int vpci_setup_mmio_handler_cb(struct domain *d, > struct pci_host_bridge *bridge) > { > struct pci_config_window *cfg = bridge->cfg; > + int count = 1; > > register_mmio_handler(d, &vpci_mmio_handler, > cfg->phys_addr, cfg->size, bridge); > > - /* We have registered a single MMIO handler. */ > - return 1; > + if ( bridge->child_ops ) > + { > + struct pci_config_window *cfg = bridge->child_cfg; > + > + register_mmio_handler(d, &vpci_mmio_handler_child, cfg->phys_addr, > + cfg->size, bridge); > + count++; > + } > + > + return count; > } > > int domain_vpci_init(struct domain *d) > @@ -101,8 +148,12 @@ int domain_vpci_init(struct domain *d) > static int vpci_get_num_handlers_cb(struct domain *d, > struct pci_host_bridge *bridge) > { > - /* Each bridge has a single MMIO handler for the configuration space. */ > - return 1; > + int count = 1; > + > + if ( bridge->child_cfg ) > + count++; > + > + return count; > } > > unsigned int domain_vpci_get_num_mmio_handlers(struct domain *d) > -- > 2.34.1 >
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |