diff --git a/xen/arch/arm/include/asm/pci.h b/xen/arch/arm/include/asm/pci.h index 9e8b17c827..37a6f14dd4 100644 --- a/xen/arch/arm/include/asm/pci.h +++ b/xen/arch/arm/include/asm/pci.h @@ -69,6 +69,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. */ }; @@ -82,6 +85,9 @@ struct pci_ops { bool (*need_p2m_hwdom_mapping)(struct domain *d, struct pci_host_bridge *bridge, uint64_t addr); + void (*init_bus_range)(struct dt_device_node *dev, + struct pci_host_bridge *bridge, + struct pci_config_window *cfg); }; /* @@ -98,8 +104,10 @@ 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, @@ -135,6 +143,14 @@ bool pci_check_bar(const struct pci_dev *pdev, mfn_t start, mfn_t end); static inline int pci_sanitize_bar_memory(struct rangeset *r) { return 0; } +void pci_generic_init_bus_range(struct dt_device_node *dev, + struct pci_host_bridge *bridge, + struct pci_config_window *cfg); + +void pci_generic_init_bus_range_child(struct dt_device_node *dev, + struct pci_host_bridge *bridge, + struct pci_config_window *cfg); + #else /*!CONFIG_HAS_PCI*/ struct pci_dev; diff --git a/xen/arch/arm/pci/ecam.c b/xen/arch/arm/pci/ecam.c index 3987f96b01..c979af7302 100644 --- a/xen/arch/arm/pci/ecam.c +++ b/xen/arch/arm/pci/ecam.c @@ -60,6 +60,7 @@ const struct pci_ecam_ops pci_generic_ecam_ops = { .read = pci_generic_config_read, .write = pci_generic_config_write, .need_p2m_hwdom_mapping = pci_ecam_need_p2m_hwdom_mapping, + .init_bus_range = pci_generic_init_bus_range, } }; diff --git a/xen/arch/arm/pci/pci-access.c b/xen/arch/arm/pci/pci-access.c index 9f9aac43d7..4a94867501 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 inline void __iomem *map_bus(struct pci_host_bridge *bridge, + pci_sbdf_t sbdf, uint32_t reg) +{ + 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 53953d4895..487c545f3a 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) +void __init pci_generic_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,35 @@ 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; } +} + +void __init pci_generic_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) +{ + int err, cfg_reg_idx; + paddr_t addr, size; + struct pci_config_window *cfg; + + cfg = xzalloc(struct pci_config_window); + if ( !cfg ) + return NULL; + if ( !ops || !ops->pci_ops.init_bus_range ) + goto err_exit; + + ops->pci_ops.init_bus_range(dev, bridge, cfg); if ( ops->cfg_reg_index ) { @@ -208,8 +232,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 +250,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); if ( !cfg ) { err = -ENOMEM; @@ -242,10 +268,28 @@ 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); + 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 +324,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 +395,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 +411,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); + 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..2c4afa7a19 100644 --- a/xen/arch/arm/pci/pci-host-zynqmp.c +++ b/xen/arch/arm/pci/pci-host-zynqmp.c @@ -35,6 +35,7 @@ const struct pci_ecam_ops nwl_pcie_ops = { .read = pci_generic_config_read, .write = pci_generic_config_write, .need_p2m_hwdom_mapping = pci_ecam_need_p2m_hwdom_mapping, + .init_bus_range = pci_generic_init_bus_range, } }; @@ -47,7 +48,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 0ce11ffcc5..d41aa383a8 100644 --- a/xen/arch/arm/vpci.c +++ b/xen/arch/arm/vpci.c @@ -8,15 +8,17 @@ #include 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,18 +26,14 @@ 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 */ unsigned long data; - ASSERT(!bridge == !is_hardware_domain(v->domain)); - if ( vpci_ecam_read(sbdf, ECAM_REG_OFFSET(info->gpa), 1U << info->dabt.size, &data) ) { @@ -48,33 +46,86 @@ 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); + pci_sbdf_t sbdf = vpci_sbdf_from_gpa(bridge, info->gpa, true); ASSERT(!bridge == !is_hardware_domain(v->domain)); + 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, false); + + ASSERT(!bridge == !is_hardware_domain(v->domain)); + + 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); + + ASSERT(!bridge == !is_hardware_domain(v->domain)); + + 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); + + ASSERT(!bridge == !is_hardware_domain(v->domain)); + + 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 *child_cfg = bridge->child_cfg; + + register_mmio_handler(d, &vpci_mmio_handler_child, child_cfg->phys_addr, + child_cfg->size, bridge); + count++; + } + + return count; } int domain_vpci_init(struct domain *d) @@ -105,8 +156,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)