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 v2 2/5] pci: add validation for fixed BAR configuration
Date: Wed, 26 Aug 2026 19:40:21 -0500 [thread overview]
Message-ID: <20260827004024.598351-3-tdave@nvidia.com> (raw)
In-Reply-To: <20260827004024.598351-1-tdave@nvidia.com>
Validate the fixed BAR configuration specified through the pci-bars
property on PCI devices and the fixed-bar property on PCIe root ports.
When fixed-bar=on is set on a root port, every device in its hierarchy
with a memory BAR must have pci-bars= specified.
For any device with pci-bars= specified, validate that every memory BAR
has an assigned address, and that each assigned address:
- is aligned to its BAR size;
- is within the appropriate PCIe MMIO aperture; and
- does not overlap any other fixed BAR.
Abort QEMU if any validation fails.
Signed-off-by: Tushar Dave <tdave@nvidia.com>
---
hw/pci/meson.build | 1 +
hw/pci/pci-fixed-bar-validate.c | 279 ++++++++++++++++++++++++++++++++
hw/pci/pci-fixed-bar-validate.h | 21 +++
3 files changed, 301 insertions(+)
create mode 100644 hw/pci/pci-fixed-bar-validate.c
create mode 100644 hw/pci/pci-fixed-bar-validate.h
diff --git a/hw/pci/meson.build b/hw/pci/meson.build
index a6cbd89c0a..44b94a24b4 100644
--- a/hw/pci/meson.build
+++ b/hw/pci/meson.build
@@ -17,6 +17,7 @@ pci_ss.add(files(
pci_ss.add(files('pcie.c', 'pcie_aer.c'))
pci_ss.add(files('pcie_doe.c'))
system_ss.add(when: 'CONFIG_PCI_EXPRESS', if_true: files('pcie_port.c', 'pcie_host.c'))
+system_ss.add(when: 'CONFIG_PCI_EXPRESS', if_true: files('pci-fixed-bar-validate.c'))
system_ss.add_all(when: 'CONFIG_PCI', if_true: pci_ss)
stub_ss.add(files('pci-stub.c'))
diff --git a/hw/pci/pci-fixed-bar-validate.c b/hw/pci/pci-fixed-bar-validate.c
new file mode 100644
index 0000000000..a8cce3d296
--- /dev/null
+++ b/hw/pci/pci-fixed-bar-validate.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved
+ * BAR address validation for fixed-BAR placement.
+ *
+ * Written by Tushar Dave
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/pci/pcie_port.h"
+#include "hw/pci/pci_host.h"
+#include "qemu/error-report.h"
+#include "qemu/range.h"
+#include "pci-internal.h"
+#include "pci-fixed-bar-validate.h"
+
+/*
+ * Claimed BAR ranges — detect inter-device and inter-hierarchy overlaps
+ * across all fixed BARs system-wide.
+ */
+
+typedef struct {
+ uint64_t start;
+ uint64_t end;
+ const char *owner; /* device name, for error messages */
+ int bar;
+} FixedClaim;
+
+static GArray *fixed_claims;
+
+static void fixed_claims_init(void)
+{
+ if (fixed_claims) {
+ g_array_free(fixed_claims, true);
+ }
+ fixed_claims = g_array_new(false, true, sizeof(FixedClaim));
+}
+
+static void fixed_claims_free(void)
+{
+ g_array_free(fixed_claims, true);
+ fixed_claims = NULL;
+}
+
+static bool fixed_claims_overlap(uint64_t start, uint64_t end,
+ const char **owner_out, int *bar_out,
+ uint64_t *start_out, uint64_t *end_out)
+{
+ FixedClaim *c;
+ guint i;
+
+ for (i = 0; i < fixed_claims->len; i++) {
+ c = &g_array_index(fixed_claims, FixedClaim, i);
+ if (ranges_overlap(start, end - start + 1,
+ c->start, c->end - c->start + 1)) {
+ *owner_out = c->owner;
+ *bar_out = c->bar;
+ *start_out = c->start;
+ *end_out = c->end;
+ return true;
+ }
+ }
+ return false;
+}
+
+static void fixed_claims_add(uint64_t start, uint64_t end,
+ const char *owner, int bar)
+{
+ FixedClaim cl;
+
+ cl.start = start;
+ cl.end = end;
+ cl.owner = owner;
+ cl.bar = bar;
+ g_array_append_val(fixed_claims, cl);
+}
+
+static bool validate_bars(PCIDevice *pdev, FixedBarsInfo *info,
+ bool is_fixed_subtree)
+{
+ const char *devname = DEVICE(pdev)->id ? DEVICE(pdev)->id : pdev->name;
+ const char *overlap_owner;
+ const char *wname;
+ bool has_pci_bars = (pdev->fixed_bar_addrs != NULL);
+ bool is_64bit;
+ uint64_t addr, end, wbase, wlim, overlap_start, overlap_end;
+ PCIIORegion *r;
+ int overlap_bar;
+ int i;
+
+ if (is_fixed_subtree && !has_pci_bars) {
+ error_report("pci-bars: %s [%02x:%02x.%x] under fixed-bar root port "
+ "has memory BARs but no pci-bars= specified",
+ devname, pci_dev_bus_num(pdev),
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+ exit(1);
+ }
+
+ if (!has_pci_bars) {
+ return false;
+ }
+
+ /* Completeness: every memory BAR must have an address. */
+ for (i = 0; i < PCI_NUM_REGIONS - 1; i++) {
+ r = &pdev->io_regions[i];
+ if (!r->size || (r->type & PCI_BASE_ADDRESS_SPACE_IO)) {
+ continue;
+ }
+ if (pdev->fixed_bar_addrs[i] == PCI_BAR_UNMAPPED) {
+ error_report("pci-bars: %s [%02x:%02x.%x] BAR%d "
+ "missing from pci-bars=",
+ devname, pci_dev_bus_num(pdev),
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), i);
+ exit(1);
+ }
+ }
+
+ /* Per-BAR checks: alignment, MMIO window, overlap. */
+ for (i = 0; i < PCI_NUM_REGIONS - 1; i++) {
+ r = &pdev->io_regions[i];
+ if (!r->size || (r->type & PCI_BASE_ADDRESS_SPACE_IO)) {
+ continue;
+ }
+
+ is_64bit = !!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64);
+ addr = (uint64_t)pdev->fixed_bar_addrs[i];
+
+ if (r->size - 1 > UINT64_MAX - addr) {
+ error_report("pci-bars: %s [%02x:%02x.%x] BAR%d "
+ "addr=0x%"PRIx64" + size=0x%"PRIx64" overflows",
+ devname, pci_dev_bus_num(pdev),
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+ i, addr, r->size);
+ exit(1);
+ }
+ end = addr + r->size - 1;
+
+ /*
+ * wbase/wlim are both inclusive bounds (mmio*_limit = base+size-1,
+ * set by the caller alongside mmio*_base), matching end's own
+ * inclusive computation above.
+ */
+ if (is_64bit) {
+ wbase = info->mmio64_base;
+ wlim = info->mmio64_limit;
+ wname = "64-bit MMIO";
+ } else {
+ wbase = info->mmio32_base;
+ wlim = info->mmio32_limit;
+ wname = "32-bit MMIO";
+ }
+
+ if (addr & (r->size - 1)) {
+ error_report("pci-bars: %s [%02x:%02x.%x] BAR%d "
+ "addr=0x%"PRIx64" not aligned to size=0x%"PRIx64,
+ devname, pci_dev_bus_num(pdev),
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+ i, addr, r->size);
+ exit(1);
+ }
+
+ if (addr < wbase || end > wlim) {
+ error_report("pci-bars: %s [%02x:%02x.%x] BAR%d "
+ "[0x%"PRIx64"..0x%"PRIx64"] outside %s window "
+ "[0x%"PRIx64"..0x%"PRIx64"]",
+ devname, pci_dev_bus_num(pdev),
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+ i, addr, end, wname, wbase, wlim);
+ exit(1);
+ }
+
+ if (fixed_claims_overlap(addr, end, &overlap_owner, &overlap_bar,
+ &overlap_start, &overlap_end)) {
+ error_report("pci-bars: %s [%02x:%02x.%x] BAR%d "
+ "[0x%"PRIx64"..0x%"PRIx64"] overlaps %s BAR%d "
+ "[0x%"PRIx64"..0x%"PRIx64"]",
+ devname, pci_dev_bus_num(pdev),
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+ i, addr, end, overlap_owner, overlap_bar,
+ overlap_start, overlap_end);
+ exit(1);
+ }
+
+ fixed_claims_add(addr, end, devname, i);
+ }
+
+ return true;
+}
+
+/*
+ * True if bus, or any of its ancestor buses, hangs off a fixed-bar=on
+ * root port. Walks up via bus->parent_dev / pci_get_bus().
+ */
+static bool bus_is_fixed_subtree(PCIBus *bus)
+{
+ PCIDevice *parent;
+
+ while (bus) {
+ parent = bus->parent_dev;
+ if (!parent) {
+ return false;
+ }
+ if (object_dynamic_cast(OBJECT(parent), TYPE_PCIE_ROOT_PORT) &&
+ PCIE_SLOT(parent)->fixed_bar) {
+ return true;
+ }
+ bus = pci_get_bus(parent);
+ }
+ return false;
+}
+
+static void scan_bus(PCIBus *bus, void *opaque);
+
+static void scan_bus_device(PCIBus *bus, PCIDevice *pdev, void *opaque)
+{
+ FixedBarsInfo *info = opaque;
+ bool has_mem_bar = false;
+ PCIIORegion *r;
+ PCIBus *sec;
+ int i;
+
+ for (i = 0; i < PCI_NUM_REGIONS - 1; i++) {
+ r = &pdev->io_regions[i];
+ if (r->size && !(r->type & PCI_BASE_ADDRESS_SPACE_IO)) {
+ has_mem_bar = true;
+ break;
+ }
+ }
+
+ if (has_mem_bar) {
+ if (validate_bars(pdev, info, bus_is_fixed_subtree(bus))) {
+ info->any_fixed = true;
+ }
+ }
+
+ if (!object_dynamic_cast(OBJECT(pdev), TYPE_PCI_BRIDGE)) {
+ return;
+ }
+ sec = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev));
+ if (sec) {
+ scan_bus(sec, opaque);
+ }
+}
+
+static void scan_bus(PCIBus *bus, void *opaque)
+{
+ pci_for_each_device_under_bus(bus, scan_bus_device, opaque);
+}
+
+static void scan_all_host_bridges(FixedBarsInfo *info)
+{
+ PCIHostState *hb;
+
+ fixed_claims_init();
+ QLIST_FOREACH(hb, &pci_host_bridges, next) {
+ if (hb->bus) {
+ scan_bus(hb->bus, info);
+ }
+ }
+ fixed_claims_free();
+}
+
+/*
+ * fixed_bars_validate - scan all PCI devices, validate fixed BAR addresses.
+ *
+ * @info: carries MMIO window bounds for validation.
+ *
+ * Returns true if any fixed BAR devices were found, false if there is
+ * nothing to do. Aborts on any validation error.
+ */
+bool fixed_bars_validate(FixedBarsInfo *info)
+{
+ info->any_fixed = false;
+ scan_all_host_bridges(info);
+ return info->any_fixed;
+}
diff --git a/hw/pci/pci-fixed-bar-validate.h b/hw/pci/pci-fixed-bar-validate.h
new file mode 100644
index 0000000000..297d1480c0
--- /dev/null
+++ b/hw/pci/pci-fixed-bar-validate.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved
+ *
+ * Written by Tushar Dave
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef HW_PCI_FIXED_BAR_VALIDATE_H
+#define HW_PCI_FIXED_BAR_VALIDATE_H
+
+typedef struct {
+ bool any_fixed;
+ uint64_t mmio32_base;
+ uint64_t mmio32_limit;
+ uint64_t mmio64_base;
+ uint64_t mmio64_limit;
+} FixedBarsInfo;
+
+bool fixed_bars_validate(FixedBarsInfo *info);
+
+#endif /* HW_PCI_FIXED_BAR_VALIDATE_H */
--
2.34.1
next prev parent reply other threads:[~2026-08-27 0:42 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-27 0:40 [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement Tushar Dave
2026-08-27 0:40 ` [RFC PATCH v2 1/5] hw/pci: add fixed-bar and pci-bars properties Tushar Dave
2026-08-27 0:40 ` Tushar Dave [this message]
2026-08-27 0:40 ` [RFC PATCH v2 3/5] pci: add fixed BAR fw_cfg blob export Tushar Dave
2026-08-27 0:40 ` [RFC PATCH v2 4/5] hw/arm/virt: export fixed BAR metadata via fw_cfg Tushar Dave
2026-08-27 0:40 ` [RFC PATCH v2 5/5] hw/arm/virt: add highmem-mmio-base property Tushar Dave
2026-08-27 7:18 ` [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement Gerd Hoffmann
2026-08-27 13:47 ` Alex Williamson
2026-08-27 14:38 ` [edk2-devel] " Ard Biesheuvel
2026-08-28 15:49 ` Tushar Dave
2026-08-31 13:42 ` Gerd Hoffmann
2026-09-01 21:58 ` Tushar Dave
2026-08-31 13:24 ` Gerd Hoffmann
2026-09-01 22:27 ` Tushar Dave
2026-09-02 6:15 ` Gerd Hoffmann
2026-09-02 15:24 ` Alex Williamson
2026-09-03 9:34 ` Gerd Hoffmann
2026-09-02 16:30 ` Tushar Dave
2026-09-03 9:47 ` Gerd Hoffmann
2026-09-04 13:46 ` Tushar Dave
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=20260827004024.598351-3-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox