diff --git a/xen/arch/arm/include/asm/pci.h b/xen/arch/arm/include/asm/pci.h
index e1f63d75e3..0012c2ae9e 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 {
     struct pci_config_window* cfg;   /* Pointer to the bridge config window */
     const struct pci_ops *ops;
     void *priv;                      /* Private data of the bridge. */
+    /* Child bus */
+    struct pci_config_window* child_cfg;
+    const struct pci_ops *child_ops;
 };
 
 struct pci_ops {
@@ -97,15 +100,20 @@ 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,
-                                              size_t priv_sz);
+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,
+                      size_t priv_sz);
 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..5486881c5c 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);
+}
+
 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..4da607ac3f 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)
+{
+    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 e4e05d1176..3824146561 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,9 +233,11 @@ 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,
-                                              size_t priv_sz)
+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,
+                      size_t priv_sz)
 {
     struct pci_host_bridge *bridge;
     struct pci_config_window *cfg;
@@ -225,7 +252,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;
@@ -245,6 +272,21 @@ struct pci_host_bridge *pci_host_common_probe(struct dt_device_node *dev,
 
     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;
+    }
+
     if ( priv_sz )
     {
         bridge->priv = xzalloc_bytes(priv_sz);
@@ -262,6 +304,9 @@ struct pci_host_bridge *pci_host_common_probe(struct dt_device_node *dev,
 err_priv:
     xfree(bridge->priv);
 
+err_child:
+    xfree(bridge->cfg);
+
 err_exit:
     xfree(bridge);
 
@@ -296,9 +341,11 @@ 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;
@@ -364,6 +411,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++ )
         {
@@ -379,7 +427,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 dde6a79a8e..08d94879b7 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, 0));
+    return PTR_RET(pci_host_common_probe(dev, &pci_generic_ecam_ops, NULL, 0));
 }
 
 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 c796447ac4..0b51ff6bd9 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, 0));
+    return PTR_RET(pci_host_common_probe(dev, &nwl_pcie_ops, NULL, 0));
 }
 
 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..05af4ef390 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,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, 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;
+
+    if ( !vpci_sbdf_from_gpa(v->domain, bridge, info->gpa,
+                             true, &sbdf) )
+        return 0;
+
+    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;
+
+    if ( !vpci_sbdf_from_gpa(v->domain, bridge, info->gpa,
+                             false, &sbdf) )
+        return 0;
+
+    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 +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)
