[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v3 6/8] xen/pci: initialize BARs


  • To: "xen-devel@xxxxxxxxxxxxxxxxxxxx" <xen-devel@xxxxxxxxxxxxxxxxxxxx>
  • From: Mykyta Poturai <Mykyta_Poturai@xxxxxxxx>
  • Date: Tue, 18 Nov 2025 13:36:35 +0000
  • Accept-language: en-US
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=epam.com; dmarc=pass action=none header.from=epam.com; dkim=pass header.d=epam.com; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=Q5toO1p7HM6sy4CzbYFdVDRmwNAwzJNlRltMnSAEZo4=; b=ch2yLWxiIM2sXJwKQ3Z61UJWBIgcS2DR4NpKN3kxxGbC08YxXh+jjTBrrzJXXBijV2MYVrQBQ4/3eoQPmQFbfkEdmfkkfSxLaITuvAOXm8JuFBliiOv0VVzb7KAP5wVaQp54GkllZYiOzXZ14vqEa1SzYrZnoNN5U/my+9TeY3Y/e/B6YswT5XIA3DcMHcuHsvzMrsBUFWDL/QoEM6uHLBwSBxwygpKJxqpqit3WmDNTzai9TA2sgT5m06oQGvyPWF3cLWOMApobsXamQXGjURRjGLh2oNp1VOMzxSZ4mwPZTJmQ2QnuWtCaV1Z5/ZAM217e6W/xD6KPJGorwuPjxg==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=gcVm8N55xdEMAEBPDMW6b+jg0V7djeIZHf0XPEIF0wqAhiuH+vy/zQ6Blva++d+1vOzG3K235KS/dEXmSdRR7ZopmEI+AwZ0MiZ/u4XIYghN6P0fcfSCZvYSjjqGqfqOZn094sZkAcSlObiVKrCkW8WjhUZaBuw7GPblQLWizij61kvoUHextBhapRhjSG02MuO86TD7IB/sg0YlQTSwDVBJqkWNrjEsUqhrWIhUjQXUwqNT7wJcDD4l7m2+evhkaKWc3ElMXuvPXNZv6QrKf8WNCoWgaXhTSi6arsLC3VvC2Qf2Z8gArrIu9bSYTDTXI/zv0+4bkA4jJ90GdEOJsw==
  • Authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=epam.com;
  • Cc: Stewart Hildebrand <stewart.hildebrand@xxxxxxx>, Stefano Stabellini <sstabellini@xxxxxxxxxx>, Julien Grall <julien@xxxxxxx>, Bertrand Marquis <bertrand.marquis@xxxxxxx>, Michal Orzel <michal.orzel@xxxxxxx>, Volodymyr Babchuk <Volodymyr_Babchuk@xxxxxxxx>, Andrew Cooper <andrew.cooper3@xxxxxxxxxx>, Anthony PERARD <anthony.perard@xxxxxxxxxx>, Jan Beulich <jbeulich@xxxxxxxx>, Roger Pau Monné <roger.pau@xxxxxxxxxx>, Mykyta Poturai <Mykyta_Poturai@xxxxxxxx>
  • Delivery-date: Tue, 18 Nov 2025 13:36:49 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>
  • Thread-index: AQHcWJBbzP55AknCK0ePM8b3+jQIaw==
  • Thread-topic: [PATCH v3 6/8] xen/pci: initialize BARs

From: Stewart Hildebrand <stewart.hildebrand@xxxxxxx>

A PCI device must have valid BARs in order to assign it to a domain.  On
ARM, firmware is unlikely to have initialized the BARs, so we must do
this in Xen. During setup_hwdom_pci_devices(), check if each BAR is
valid. If the BAR happens to already be valid, remove the BAR range from
a rangeset of valid PCI ranges so as to avoid overlap when reserving a
new BAR. If not valid, reserve a new BAR address from the rangeset and
write it to the device.

Avaliable ranges are read from DT during init and stored in two distinct
rangesets, one for prefetchable and one for non-prefetchable BARs.

Signed-off-by: Stewart Hildebrand <stewart.hildebrand@xxxxxxx>
Signed-off-by: Mykyta Poturai <mykyta_poturai@xxxxxxxx>
---
v2->v3:
* drop hwdom_uses_vpci
* check that rangeset can handle u64
* rework rangeset manipulaiton
* mark more functions as __init
* move bar init to arm files
* style fixes

v1->v2:
* move hwdom_uses_vpci to this patch
* fixup error reporting
---
 xen/arch/arm/include/asm/pci.h     |   7 ++
 xen/arch/arm/pci/pci-host-common.c |  92 ++++++++++++++++++++++++
 xen/arch/arm/pci/pci.c             | 110 +++++++++++++++++++++++++++++
 xen/common/rangeset.c              |  62 ++++++++++++++--
 xen/include/xen/rangeset.h         |  11 +++
 5 files changed, 277 insertions(+), 5 deletions(-)

diff --git a/xen/arch/arm/include/asm/pci.h b/xen/arch/arm/include/asm/pci.h
index 7289f7688b..ac4e87f9c1 100644
--- a/xen/arch/arm/include/asm/pci.h
+++ b/xen/arch/arm/include/asm/pci.h
@@ -74,6 +74,8 @@ struct pci_host_bridge {
     struct pci_config_window *child_cfg;
     const struct pci_ops *child_ops;
     void *priv;                      /* Private data of the bridge. */
+    struct rangeset *bar_ranges;
+    struct rangeset *bar_ranges_prefetch;
 };
 
 struct pci_ops {
@@ -154,6 +156,11 @@ void pci_generic_init_bus_range_child(struct 
dt_device_node *dev,
 
 bool arch_pci_device_physdevop(void);
 
+uint64_t pci_get_new_bar_addr(const struct pci_dev *pdev, uint64_t size,
+                              bool is_64bit, bool prefetch);
+int pci_reserve_bar_range(const struct pci_dev *pdev, uint64_t addr,
+                          uint64_t size, bool prefetch);
+
 #else   /*!CONFIG_HAS_PCI*/
 
 #define pci_scan_enabled false
diff --git a/xen/arch/arm/pci/pci-host-common.c 
b/xen/arch/arm/pci/pci-host-common.c
index de30fb0aec..28c26af9eb 100644
--- a/xen/arch/arm/pci/pci-host-common.c
+++ b/xen/arch/arm/pci/pci-host-common.c
@@ -21,6 +21,7 @@
 #include <xen/rwlock.h>
 #include <xen/sched.h>
 #include <xen/vmap.h>
+#include <xen/resource.h>
 
 #include <asm/setup.h>
 
@@ -232,6 +233,25 @@ static int pci_bus_find_domain_nr(struct dt_device_node 
*dev)
     return domain;
 }
 
+static int __init add_bar_range(const struct dt_device_node *dev,
+                                uint32_t flags, uint64_t addr, uint64_t len,
+                                void *data)
+{
+    struct pci_host_bridge *bridge = data;
+
+    /* Ensure we are not using bits in a rangeset */
+    BUILD_BUG_ON(sizeof(unsigned long) != sizeof(uint64_t));
+
+    if ( !(flags & IORESOURCE_MEM) )
+        return 0;
+
+    if ( flags & IORESOURCE_PREFETCH )
+        return rangeset_add_range(bridge->bar_ranges_prefetch, addr,
+                                  addr + len - 1);
+    else
+        return rangeset_add_range(bridge->bar_ranges, addr, addr + len - 1);
+}
+
 struct pci_host_bridge * __init
 pci_host_common_probe(struct dt_device_node *dev,
                       const struct pci_ecam_ops *ops,
@@ -283,6 +303,18 @@ pci_host_common_probe(struct dt_device_node *dev,
         bridge->child_ops = &child_ops->pci_ops;
     }
 
+    bridge->bar_ranges = rangeset_new(NULL, "BAR ranges",
+                                      RANGESETF_prettyprint_hex);
+    bridge->bar_ranges_prefetch = rangeset_new(NULL,
+                                               "BAR ranges (prefetchable)",
+                                               RANGESETF_prettyprint_hex);
+    if ( bridge->bar_ranges && bridge->bar_ranges_prefetch )
+    {
+        err = dt_for_each_range(bridge->dt_node, add_bar_range, bridge);
+        if ( err )
+            goto err_child;
+    }
+
     pci_add_host_bridge(bridge);
     pci_add_segment(bridge->segment);
 
@@ -476,6 +508,66 @@ bool pci_check_bar(const struct pci_dev *pdev, mfn_t 
start, mfn_t end)
 
     return bar_data.is_valid;
 }
+
+/*
+ * Find suitable place for an uninitialized bar of specified size in the
+ * host bridge ranges
+ */
+uint64_t __init pci_get_new_bar_addr(const struct pci_dev *pdev, uint64_t size,
+                                     bool is_64bit, bool prefetch)
+{
+    struct pci_host_bridge *bridge;
+    struct rangeset *range;
+    uint64_t addr = 0, end = GB(4);
+
+    /* Make sure we can store addr in a rangeset */
+    BUILD_BUG_ON(sizeof(addr) != sizeof(unsigned long));
+
+    bridge = pci_find_host_bridge(pdev->seg, pdev->bus);
+    if ( !bridge )
+        return 0;
+
+    range = prefetch ? bridge->bar_ranges_prefetch : bridge->bar_ranges;
+
+    if ( size < PAGE_SIZE )
+        size = PAGE_SIZE;
+
+    if ( is_64bit )
+    {
+        addr = GB(4);
+        end = ~0;
+    }
+
+    if ( !rangeset_claim_aligned_range(range, size, &addr, end) )
+        return addr;
+
+    printk(XENLOG_ERR "Failed to claim BAR range %lx-%lx from rangeset\n",
+           addr, addr + size - 1);
+
+    return 0;
+}
+
+/*
+ * Remove already used memory from the host bridge bar ranges
+ */
+int __init pci_reserve_bar_range(const struct pci_dev *pdev, uint64_t addr,
+                                 uint64_t size, bool prefetch)
+{
+    struct pci_host_bridge *bridge;
+    struct rangeset *range;
+
+    /* Make sure we can store addr in a rangeset */
+    BUILD_BUG_ON(sizeof(addr) != sizeof(unsigned long));
+
+    bridge = pci_find_host_bridge(pdev->seg, pdev->bus);
+    if ( !bridge )
+        return 0;
+
+    range = prefetch ? bridge->bar_ranges_prefetch : bridge->bar_ranges;
+
+    return rangeset_remove_range(range, addr, addr + size - 1);
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/arch/arm/pci/pci.c b/xen/arch/arm/pci/pci.c
index 951639eb3f..0330220e93 100644
--- a/xen/arch/arm/pci/pci.c
+++ b/xen/arch/arm/pci/pci.c
@@ -95,6 +95,108 @@ boolean_param("pci-passthrough", pci_passthrough_enabled);
 __ro_after_init bool pci_scan_enabled;
 boolean_param("pci-scan", pci_scan_enabled);
 
+typedef int (*bar_callback_t)(struct pci_dev *, uint8_t, uint64_t, uint64_t,
+                              bool, bool);
+
+static int __init reserve_bar_range(struct pci_dev *pdev, uint8_t reg,
+                                    uint64_t addr, uint64_t size, bool 
is_64bit,
+                                    bool prefetch)
+{
+    if ( pci_check_bar(pdev, maddr_to_mfn(addr),
+                       maddr_to_mfn(addr + size - 1)) )
+        return pci_reserve_bar_range(pdev, addr, size, prefetch);
+    return 0;
+}
+
+static int __init setup_bar(struct pci_dev *pdev, uint8_t reg, uint64_t addr,
+                            uint64_t size, bool is_64bit, bool prefetch)
+{
+    if ( !pci_check_bar(pdev, maddr_to_mfn(addr),
+                        maddr_to_mfn(addr + size - 1)) )
+    {
+        uint16_t cmd = pci_conf_read16(pdev->sbdf, PCI_COMMAND);
+
+        addr = pci_get_new_bar_addr(pdev, size, is_64bit, prefetch);
+        if ( !addr )
+            return -ENOMEM;
+
+        pci_conf_write16(pdev->sbdf, PCI_COMMAND,
+                         cmd & ~(PCI_COMMAND_MEMORY | PCI_COMMAND_IO));
+
+        pci_conf_write32(pdev->sbdf, reg,
+                         (addr & GENMASK(31, 0)) |
+                         (is_64bit ? PCI_BASE_ADDRESS_MEM_TYPE_64 : 0));
+
+        if ( is_64bit )
+            pci_conf_write32(pdev->sbdf, reg + 4, addr >> 32);
+
+        pci_conf_write16(pdev->sbdf, PCI_COMMAND, cmd);
+    }
+
+    return 0;
+}
+
+static int __init bars_iterate(struct pci_dev *pdev, void *arg)
+{
+    unsigned int i, barsize, ret = 0, num_bars = PCI_HEADER_NORMAL_NR_BARS;
+    uint64_t addr, size;
+    bar_callback_t cb = arg;
+
+    if ( (pci_conf_read8(pdev->sbdf, PCI_HEADER_TYPE) & 0x7f) ==
+         PCI_HEADER_TYPE_NORMAL )
+    {
+        for ( i = 0; i < num_bars; i += barsize )
+        {
+            uint8_t reg = PCI_BASE_ADDRESS_0 + i * 4;
+            bool prefetch;
+
+            if ( (pci_conf_read32(pdev->sbdf, reg) & PCI_BASE_ADDRESS_SPACE) ==
+                 PCI_BASE_ADDRESS_SPACE_IO )
+            {
+                barsize = 1;
+                continue;
+            }
+
+            barsize = pci_size_mem_bar(pdev->sbdf, reg, &addr, &size,
+                                       (i == num_bars - 1) ? PCI_BAR_LAST : 0);
+
+            if ( !size )
+                continue;
+
+            prefetch = pci_conf_read32(pdev->sbdf, reg) &
+                       PCI_BASE_ADDRESS_MEM_PREFETCH;
+
+            ret = cb(pdev, reg, addr, size, barsize == 2, prefetch);
+            if ( ret )
+                return ret;
+        }
+    }
+
+    return ret;
+}
+
+static int __init pci_setup_bars(void)
+{
+    int ret;
+    /* We can't change the signature of bars_iterate to only accept
+     * bar_callback_t, so use intermediate variables to ensure callback
+     * signatures are always correct
+     */
+    bar_callback_t cb_reserve = reserve_bar_range;
+    bar_callback_t cb_setup = setup_bar;
+
+    pcidevs_lock();
+    ret = pci_iterate_devices(bars_iterate, cb_reserve);
+    if ( ret )
+        goto out;
+
+    ret = pci_iterate_devices(bars_iterate, cb_setup);
+
+out:
+    pcidevs_unlock();
+    return ret;
+}
+
 static int __init pci_init(void)
 {
     int ret;
@@ -129,6 +231,14 @@ static int __init pci_init(void)
             printk(XENLOG_ERR "PCI: Failed to scan PCI devices (rc=%d)\n", 
ret);
             return 0;
         }
+
+        ret = pci_setup_bars();
+
+        if ( ret < 0 )
+        {
+            printk(XENLOG_ERR "PCI: Failed to configure BARs (rc=%d)\n", ret);
+            return 0;
+        }
     }
 
     return 0;
diff --git a/xen/common/rangeset.c b/xen/common/rangeset.c
index 0e3b9acd35..6a0c20ab41 100644
--- a/xen/common/rangeset.c
+++ b/xen/common/rangeset.c
@@ -180,16 +180,13 @@ int rangeset_add_range(
     return rc;
 }
 
-int rangeset_remove_range(
-    struct rangeset *r, unsigned long s, unsigned long e)
+static int remove_range(struct rangeset *r, unsigned long s, unsigned long e)
 {
     struct range *x, *y, *t;
     int rc = 0;
 
     ASSERT(s <= e);
 
-    write_lock(&r->lock);
-
     x = find_range(r, s);
     y = find_range(r, e);
 
@@ -244,8 +241,18 @@ int rangeset_remove_range(
             destroy_range(r, x);
     }
 
- out:
+out:
+    return rc;
+}
+
+int rangeset_remove_range(struct rangeset *r, unsigned long s, unsigned long e)
+{
+    int rc = 0;
+
+    write_lock(&r->lock);
+    rc = remove_range(r, s, e);
     write_unlock(&r->lock);
+
     return rc;
 }
 
@@ -357,6 +364,51 @@ int rangeset_claim_range(struct rangeset *r, unsigned long 
size,
     return 0;
 }
 
+int rangeset_claim_aligned_range(struct rangeset *r, unsigned long size,
+                                 unsigned long *s, unsigned long e)
+{
+    struct range *x;
+    int rc = 0;
+
+    /* Power of 2 check */
+    if ( (size & (size - 1)) != 0 && size != 0 )
+    {
+        *s = 0;
+        return -EINVAL;
+    }
+
+    if ( e < *s )
+        return -EINVAL;
+
+    write_lock(&r->lock);
+
+    for ( x = first_range(r); x; x = next_range(r, x) )
+    {
+        /* Assumes size is a power of 2 */
+        unsigned long start_aligned = ROUNDUP(x->s, size);
+
+        if ( x->e > start_aligned &&
+             (x->e - start_aligned) >= size &&
+             start_aligned >= *s &&
+             start_aligned + size <= e)
+        {
+            rc = remove_range(r, start_aligned, start_aligned + size - 1);
+            if ( !rc )
+                *s = start_aligned;
+            else
+                *s = 0;
+
+            write_unlock(&r->lock);
+            return rc;
+        }
+    }
+
+    *s = 0;
+
+    write_unlock(&r->lock);
+    return -ENOSPC;
+}
+
 int rangeset_consume_ranges(struct rangeset *r,
                             int (*cb)(unsigned long s, unsigned long e,
                                       void *ctxt, unsigned long *c),
diff --git a/xen/include/xen/rangeset.h b/xen/include/xen/rangeset.h
index 817505badf..dcef96cb2c 100644
--- a/xen/include/xen/rangeset.h
+++ b/xen/include/xen/rangeset.h
@@ -61,6 +61,17 @@ int __must_check rangeset_add_range(
     struct rangeset *r, unsigned long s, unsigned long e);
 int __must_check rangeset_claim_range(struct rangeset *r, unsigned long size,
                                       unsigned long *s);
+
+/*
+ * Find a range subset that starts at or after s, ends before e,
+ * and is aligned to the size.
+ * If such subset is present it is removed from the rangeset and
+ * it's start is written to s, otherwise s is set to 0.
+ */
+int __must_check rangeset_claim_aligned_range(struct rangeset *r,
+                                              unsigned long size,
+                                              unsigned long *s,
+                                              unsigned long e);
 int __must_check rangeset_remove_range(
     struct rangeset *r, unsigned long s, unsigned long e);
 bool __must_check rangeset_contains_range(
-- 
2.51.2



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.