All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tushar Dave <tdave@nvidia.com>
To: qemu-devel@nongnu.org
Cc: alwilliamson@nvidia.com, jgg@nvidia.com, skolothumtho@nvidia.com,
	qemu-arm@nongnu.org, peter.maydell@linaro.org, mst@redhat.com,
	marcel.apfelbaum@gmail.com, devel@edk2.groups.io
Subject: [RFC PATCH 5/8] hw/pci: allocate remaining BARs for buses without fixed BARs
Date: Fri,  8 May 2026 13:37:14 -0500	[thread overview]
Message-ID: <20260508183717.193630-6-tdave@nvidia.com> (raw)
In-Reply-To: <20260508183717.193630-1-tdave@nvidia.com>

This phase performs PCI BAR allocation for buses without fixed BAR
assignments. It respects existing allocations and does not disturb
already programmed BARs or bridge windows.

It computes remaining MMIO64 requirements, assigns BARs, and extends
bridge prefetch windows if required.

Signed-off-by: Tushar Dave <tdave@nvidia.com>
---
 hw/pci/pci-resource.c | 355 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 354 insertions(+), 1 deletion(-)

diff --git a/hw/pci/pci-resource.c b/hw/pci/pci-resource.c
index de98924aa6..e2d2adc7de 100644
--- a/hw/pci/pci-resource.c
+++ b/hw/pci/pci-resource.c
@@ -371,6 +371,358 @@ static void finalize_bridge_window(PCIBus *bus, uint64_t min_addr, uint64_t max_
     }
 }
 
+/* Returns true if this 64-bit pref BAR is already assigned */
+static bool bar_is_assigned(PCIDevice *dev, int bar_idx, GHashTable *had_fixed)
+{
+    PCIIORegion *r = &dev->io_regions[bar_idx];
+    uint32_t lo;
+    uint32_t hi;
+
+    if (!is_64bit_pref_bar(r)) {
+        return false;
+    }
+    if (dev->fixed_bar_addrs &&
+        dev->fixed_bar_addrs[bar_idx] != PCI_BAR_UNMAPPED) {
+        return true;
+    }
+    if (bar_idx >= PCI_ROM_SLOT - 1) {
+        return false; /* 64-bit BAR uses two slots */
+    }
+    lo = pci_get_long(dev->config + PCI_BASE_ADDRESS_0 + bar_idx * 4);
+    if (!(lo & PCI_BASE_ADDRESS_MEM_TYPE_64)) {
+        return (lo & PCI_BASE_ADDRESS_MEM_MASK) != 0;
+    }
+    hi = pci_get_long(dev->config + PCI_BASE_ADDRESS_0 + bar_idx * 4 + 4);
+    return (((uint64_t)hi << 32) | (lo & PCI_BASE_ADDRESS_MEM_MASK)) != 0;
+}
+
+/* Return BAR address from config, or 0 if unassigned. */
+static uint64_t get_bar_addr_from_config(PCIDevice *dev, int bar_idx)
+{
+    PCIIORegion *r = &dev->io_regions[bar_idx];
+    uint32_t lo;
+    uint32_t hi;
+
+    if (!r->size || bar_idx >= PCI_ROM_SLOT - 1) {
+        return 0;
+    }
+    lo = pci_get_long(dev->config + PCI_BASE_ADDRESS_0 + bar_idx * 4);
+    if (lo & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+        hi = pci_get_long(dev->config + PCI_BASE_ADDRESS_0 + bar_idx * 4 + 4);
+        return ((uint64_t)hi << 32) | (lo & PCI_BASE_ADDRESS_MEM_MASK);
+    }
+    return lo & PCI_BASE_ADDRESS_MEM_MASK;
+}
+
+/* Total size of unassigned 64-bit pref BARs in this bus and its subtree. */
+static uint64_t size_entire_subtree(PCIBus *bus, GHashTable *had_fixed)
+{
+    uint64_t total = 0;
+
+    for (int devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
+        PCIDevice *d = bus->devices[devfn];
+        if (!d) {
+            continue;
+        }
+        for (int i = 0; i < PCI_ROM_SLOT; i++) {
+            PCIIORegion *r = &d->io_regions[i];
+            if (!is_64bit_pref_bar(r)) {
+                continue;
+            }
+            if (bar_is_assigned(d, i, had_fixed)) {
+                continue;
+            }
+            total += r->size;
+        }
+        if (IS_PCI_BRIDGE(d)) {
+            total += size_entire_subtree(pci_bridge_get_sec_bus(PCI_BRIDGE(d)), had_fixed);
+        }
+    }
+    return total;
+}
+
+/* Highest end address of any assigned BAR or bridge window in this bus and subtree. */
+static uint64_t find_highest_assigned_in_bus(PCIBus *bus)
+{
+    uint64_t highest = 0;
+    uint64_t base;
+    uint64_t limit;
+    uint64_t addr;
+
+    for (int devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
+        PCIDevice *d = bus->devices[devfn];
+        if (!d) {
+            continue;
+        }
+        if (IS_PCI_BRIDGE(d)) {
+            PCIBus *sec = pci_bridge_get_sec_bus(PCI_BRIDGE(d));
+            PCIDevice *bridge_dev = pci_bridge_get_device(sec);
+            if (bridge_dev) {
+                base = pci_bridge_get_base(bridge_dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
+                limit = pci_bridge_get_limit(bridge_dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
+                if (limit > base) {
+                    highest = MAX(highest, limit);
+                }
+                highest = MAX(highest, find_highest_assigned_in_bus(sec));
+            }
+            continue;
+        }
+        for (int i = 0; i < PCI_ROM_SLOT; i++) {
+            PCIIORegion *r = &d->io_regions[i];
+            if (!is_64bit_pref_bar(r)) {
+                continue;
+            }
+            addr = 0;
+            if (d->fixed_bar_addrs &&
+                d->fixed_bar_addrs[i] != PCI_BAR_UNMAPPED) {
+                addr = d->fixed_bar_addrs[i];
+            } else {
+                addr = get_bar_addr_from_config(d, i);
+            }
+            if (addr != 0 && r->size) {
+                highest = MAX(highest, addr + r->size - 1);
+            }
+        }
+    }
+    return highest;
+}
+
+/* Next free address in root MMIO64. */
+static uint64_t next_free_from_root(hwaddr mmio64_base, hwaddr mmio64_size)
+{
+    uint64_t mmio_start = mmio64_base;
+    uint64_t mmio_end = mmio64_base + mmio64_size - 1;
+    uint64_t highest;
+
+    highest = mmio_start - 1;
+    if (fixed_claim_regions) {
+        for (guint i = 0; i < fixed_claim_regions->len; i++) {
+            FixedClaim *c = &g_array_index(fixed_claim_regions, FixedClaim, i);
+            if (c->end >= mmio_start && c->start <= mmio_end) {
+                highest = MAX(highest, c->end);
+            }
+        }
+    }
+    return ROUND_UP(highest + 1, 0x1000); /* 4K align for new window */
+}
+
+static bool
+pci_bus_phase3_ensure_parent_prefetch_window(PCIBus *bus, PciProgramCtx *pctx,
+                                             PCIDevice *parent_bridge, uint64_t mmio_end)
+{
+    PCIBus *parent_bus;
+    PCIDevice *grandparent;
+    uint64_t parent_win_base, parent_win_limit, next_in_subtree;
+    uint64_t required, window_base, window_limit;
+    bool window_not_programmed;
+    bool parent_in_mmio64;
+
+    window_base = pci_bridge_get_base(parent_bridge, PCI_BASE_ADDRESS_MEM_PREFETCH);
+    window_limit = pci_bridge_get_limit(parent_bridge, PCI_BASE_ADDRESS_MEM_PREFETCH);
+    window_not_programmed = (window_base >= window_limit) ||
+                            (window_base < pctx->mmio64_base) || (window_limit > mmio_end);
+    if (!window_not_programmed) {
+        return true;
+    }
+
+    required = size_entire_subtree(bus, pctx->had_fixed);
+    if (required == 0) {
+        return false;
+    }
+    required = ROUND_UP(required, 0x1000);
+
+    parent_bus = pci_get_bus(parent_bridge);
+    grandparent = parent_bus ? pci_bridge_get_device(parent_bus) : NULL;
+    if (!grandparent) {
+        window_base = next_free_from_root(pctx->mmio64_base, pctx->mmio64_size);
+        window_limit = window_base + required - 1;
+        if (window_limit > mmio_end) {
+            error_report("bus [%02x] out of root MMIO64 space", pci_bus_num(bus));
+            exit(1);
+        }
+    } else {
+        parent_win_base = pci_bridge_get_base(grandparent, PCI_BASE_ADDRESS_MEM_PREFETCH);
+        parent_win_limit = pci_bridge_get_limit(grandparent, PCI_BASE_ADDRESS_MEM_PREFETCH);
+        parent_in_mmio64 = (parent_win_limit > parent_win_base) &&
+                           (parent_win_base >= pctx->mmio64_base) && (parent_win_limit <= mmio_end);
+        if (!parent_in_mmio64) {
+            window_base = next_free_from_root(pctx->mmio64_base, pctx->mmio64_size);
+            window_limit = window_base + required - 1;
+            if (window_limit > mmio_end) {
+                error_report("bus [%02x] out of root MMIO64 space", pci_bus_num(bus));
+                exit(1);
+            }
+        } else {
+            next_in_subtree = ROUND_UP(
+                find_highest_assigned_in_bus(parent_bus) + 1, 0x1000);
+            window_base = MAX(parent_win_base, next_in_subtree);
+            window_limit = window_base + required - 1;
+            if (window_limit > parent_win_limit) {
+                error_report("bus [%02x] no room in parent bridge window", pci_bus_num(bus));
+                exit(1);
+            }
+        }
+    }
+    finalize_bridge_window(bus, window_base, window_limit);
+    return true;
+}
+
+static GArray *pci_bus_phase3_collect_unassigned_bars(PCIBus *bus, PciProgramCtx *pctx,
+                                                      uint64_t *out_total_size)
+{
+    PCIDevice *d;
+    PCIIORegion *r;
+    GArray *bars;
+    uint64_t required;
+    int devfn, i;
+
+    required = 0;
+    bars = g_array_new(false, false, sizeof(BarEntry));
+    for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
+        d = bus->devices[devfn];
+        if (!d) {
+            continue;
+        }
+        for (i = 0; i < PCI_ROM_SLOT; i++) {
+            r = &d->io_regions[i];
+            if (!is_64bit_pref_bar(r) || bar_is_assigned(d, i, pctx->had_fixed)) {
+                continue;
+            }
+            required += r->size;
+            g_array_append_val(
+                bars, ((BarEntry){ .dev = d, .bar_idx = i, .size = r->size }));
+        }
+    }
+    *out_total_size = required;
+    return bars;
+}
+
+static void
+pci_bus_phase3_extend_window_for_bars(PCIBus *bus, PciProgramCtx *pctx,
+                                      PCIDevice *parent_bridge, uint64_t mmio_end,
+                                      uint64_t current, uint64_t required,
+                                      uint64_t window_base, uint64_t *window_limit,
+                                      GArray *bars_this_bus)
+{
+    uint64_t parent_limit, gp_base, gp_limit, new_limit;
+    PCIBus *parent_bus;
+    PCIDevice *grandparent;
+
+    if (current + required <= *window_limit) {
+        return;
+    }
+
+    parent_bus = pci_get_bus(parent_bridge);
+    grandparent = parent_bus ? pci_bridge_get_device(parent_bus) : NULL;
+    parent_limit = mmio_end;
+    if (grandparent) {
+        gp_base = pci_bridge_get_base(grandparent, PCI_BASE_ADDRESS_MEM_PREFETCH);
+        gp_limit = pci_bridge_get_limit(grandparent, PCI_BASE_ADDRESS_MEM_PREFETCH);
+        if (gp_limit > gp_base && gp_base >= pctx->mmio64_base) {
+            parent_limit = gp_limit;
+        }
+    }
+    new_limit = current + required - 1;
+    if (new_limit > parent_limit) {
+        error_report("bus [%02x] out of MMIO space (required 0x%" PRIx64 ")", pci_bus_num(bus),
+                    required);
+        g_array_free(bars_this_bus, true);
+        exit(1);
+    }
+    if (new_limit > *window_limit) {
+        pci_update_prefetch_window(bus, window_base, new_limit);
+        fixed_claim_regions_add(*window_limit + 1, new_limit, parent_bridge, -1);
+        *window_limit = new_limit;
+    }
+}
+
+static void
+pci_bus_phase3_program_bars_and_update_bridge(PCIBus *bus, PCIDevice *parent_bridge,
+                                              uint64_t window_base, uint64_t window_limit,
+                                              uint64_t start_addr, GArray *bars)
+{
+    guint b;
+    BarEntry *be;
+    PCIIORegion *r;
+    uint64_t addr, bar_end, high;
+    PhysBAR pbars_array[PCI_ROM_SLOT];
+
+    g_array_sort(bars, compare_bar_size_desc);
+    addr = start_addr;
+    for (b = 0; b < bars->len; b++) {
+        be = &g_array_index(bars, BarEntry, b);
+        r = &be->dev->io_regions[be->bar_idx];
+        addr = ROUND_UP(addr, r->size);
+        bar_end = addr + r->size - 1;
+        memset(pbars_array, 0, sizeof(pbars_array));
+        pbars_array[be->bar_idx].addr = addr;
+        pbars_array[be->bar_idx].end = bar_end;
+        pbars_array[be->bar_idx].flags = IORESOURCE_PREFETCH;
+        pci_program_prefetch_bars(be->dev, pbars_array);
+        addr = bar_end + 1;
+    }
+    high = find_highest_assigned_in_bus(bus);
+    if (high > window_limit) {
+        pci_update_prefetch_window(bus, window_base, high);
+        fixed_claim_regions_add(window_limit + 1, high, parent_bridge, -1);
+    }
+    g_array_free(bars, true);
+}
+
+/* Allocate and program 64-bit pref BARs for a bus with no fixed-BAR devices. */
+static void pci_bus_phase3_allocate_bars(PCIBus *bus, PciProgramCtx *pctx)
+{
+    uint64_t mmio_end, window_base, window_limit, current, required;
+    PCIDevice *parent_bridge;
+    GArray *bars;
+
+    parent_bridge = pci_bridge_get_device(bus);
+    if (!parent_bridge) {
+        return; /* Root bus has no bridge; skip */
+    }
+
+    mmio_end = pctx->mmio64_base + pctx->mmio64_size - 1;
+    if (!pci_bus_phase3_ensure_parent_prefetch_window(bus, pctx, parent_bridge, mmio_end)) {
+        return;
+    }
+    window_base = pci_bridge_get_base(parent_bridge, PCI_BASE_ADDRESS_MEM_PREFETCH);
+    window_limit = pci_bridge_get_limit(parent_bridge, PCI_BASE_ADDRESS_MEM_PREFETCH);
+    current = ROUND_UP(find_highest_assigned_in_bus(bus) + 1, 0x1000);
+    if (current < window_base) {
+        current = window_base;
+    }
+
+    bars = pci_bus_phase3_collect_unassigned_bars(bus, pctx, &required);
+    if (bars->len == 0) {
+        g_array_free(bars, true);
+        return;
+    }
+    pci_bus_phase3_extend_window_for_bars(bus, pctx, parent_bridge, mmio_end, current,
+                                              required, window_base, &window_limit, bars);
+    pci_bus_phase3_program_bars_and_update_bridge(
+        bus, parent_bridge, window_base, window_limit, current, bars);
+}
+
+/* Run once per bus; act only when the bus has no fixed-BAR devices. */
+static void pci_bus_phase3_allocate_no_fixed_bars(PCIBus *bus, void *opaque)
+{
+    PciProgramCtx *pctx = (PciProgramCtx *)opaque;
+    bool bus_has_fixed = false;
+
+    for (int devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
+        PCIDevice *d = bus->devices[devfn];
+        if (d && g_hash_table_contains(pctx->had_fixed, d)) {
+            bus_has_fixed = true;
+            break;
+        }
+    }
+
+    if (bus_has_fixed) {
+        return;
+    }
+    pci_bus_phase3_allocate_bars(bus, pctx);
+}
+
 static bool pci_bus_phase2_fill_bar_lists(PCIBus *bus, PciProgramCtx *pctx,
                                           GArray *fixed_bars, GArray *remaining_bars)
 {
@@ -649,7 +1001,8 @@ void pci_fixed_bar_allocator(PCIBus *root, const PciFixedBarMmioParams *mmio)
     /* Phase 2: pack remaining 64-bit prefetchable BARs and size parent bridge window */
     pci_for_each_bus(bus, pci_bus_phase2_pack_remaining_bars, &pctx);
 
-    /* Phase 3: buses with no fixed-BAR devices; final bridge pass: follow-up */
+    /* Phase 3: allocate BARs for buses that have no fixed-BAR devices */
+    pci_for_each_bus(bus, pci_bus_phase3_allocate_no_fixed_bars, &pctx);
 
     /* Cleanup */
     g_hash_table_destroy(pctx.had_fixed);
-- 
2.34.1



  parent reply	other threads:[~2026-05-08 20:43 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-08 18:37 [RFC PATCH 0/8] hw/arm/virt, hw/pci: PCI pre-enumeration and fixed BAR allocation Tushar Dave
2026-05-08 18:37 ` [RFC PATCH 1/8] hw/pci: add fixed-bars property to allow fixed BAR addresses Tushar Dave
2026-05-08 18:37 ` [RFC PATCH 2/8] hw/pci: enumerate PCI bus and program bridge bus numbers Tushar Dave
2026-05-08 18:37 ` [RFC PATCH 3/8] hw/pci: introduce allocator for fixed BAR placement Tushar Dave
2026-05-08 18:37 ` [RFC PATCH 4/8] hw/pci: pack remaining BARs and update bridge windows Tushar Dave
2026-05-08 18:37 ` Tushar Dave [this message]
2026-05-08 18:37 ` [RFC PATCH 6/8] hw/pci: finalize bridge prefetch windows after BAR allocation Tushar Dave
2026-05-08 18:37 ` [RFC PATCH 7/8] hw/arm/virt: add pcie-mmio-window machine property Tushar Dave
2026-05-08 18:37 ` [RFC PATCH 8/8] hw/arm/virt: add pci-pre-enum " Tushar Dave
2026-05-11  7:46 ` [RFC PATCH 0/8] hw/arm/virt, hw/pci: PCI pre-enumeration and fixed BAR allocation Peter Maydell
2026-05-11 12:26   ` Jason Gunthorpe
2026-05-11 18:38     ` Mohamed Mediouni
2026-05-11 20:28       ` Jason Gunthorpe
2026-05-11  9:09 ` Michael S. Tsirkin
2026-05-11 18:10   ` Tushar Dave
2026-05-11 22:09     ` Michael S. Tsirkin
2026-05-11 11:43 ` [edk2-devel] " Ard Biesheuvel
2026-05-12 17:25   ` Tushar Dave
2026-05-12 23:06     ` Alex Williamson
2026-05-12 23:12       ` Michael S. Tsirkin
2026-05-12 23:57         ` Alex Williamson
2026-05-13 11:36           ` Jason Gunthorpe
2026-05-13 14:25           ` Ard Biesheuvel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260508183717.193630-6-tdave@nvidia.com \
    --to=tdave@nvidia.com \
    --cc=alwilliamson@nvidia.com \
    --cc=devel@edk2.groups.io \
    --cc=jgg@nvidia.com \
    --cc=marcel.apfelbaum@gmail.com \
    --cc=mst@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-arm@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=skolothumtho@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.