QEMU-Arm Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement
@ 2026-08-27  0:40 Tushar Dave
  2026-08-27  0:40 ` [RFC PATCH v2 1/5] hw/pci: add fixed-bar and pci-bars properties Tushar Dave
                   ` (5 more replies)
  0 siblings, 6 replies; 20+ messages in thread
From: Tushar Dave @ 2026-08-27  0:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: alwilliamson, jgg, skolothumtho, qemu-arm, peter.maydell, mst,
	marcel.apfelbaum, devel

This RFC v2 is a follow-up to RFC v1 [1].

On some platforms, peer-to-peer (P2P) DMA between PCIe devices requires
guest physical addresses (GPAs) assigned to device BARs to match their
corresponding host physical addresses (HPAs).

RFC v1 proposed moving PCI enumeration and BAR assignment into QEMU
before firmware execution, with EDK2 operating in discovery-only mode
via PcdPciDisableBusEnumeration.

The feedback was that PCI enumeration and resource assignment should
remain in firmware rather than being performed by the VMM. The suggested
EDK2 mechanism was EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL, which
provides a per-device interception point for PCI resource assignment.

Following the feedback, RFC v2 keeps PCI enumeration and resource
assignment in firmware. QEMU only validates the user-provided fixed
BAR configuration and provides the required metadata to firmware
through the "etc/fixed-bars" fw_cfg file.

Two new PCI properties are introduced:

* fixed-bar=on on a PCIe root port marks its subordinate hierarchy for
  fixed BAR placement. Every device with memory BARs in that hierarchy
  must provide a complete pci-bars= configuration.

* pci-bars=barN@<addr>[,barM@<addr>]... on a PCI endpoint specifies the
  required address for each memory BAR. All memory BARs on the device
  must have an explicitly assigned address.

QEMU validates the user-specified configuration before passing it to
firmware. Validation checks BAR alignment, verifies that addresses are
within the configured PCIe MMIO apertures, and ensures that fixed BAR
ranges do not overlap. QEMU performs neither PCI enumeration nor BAR
allocation.

On the firmware side, a new DXE driver, QemuFixedBarsDxe, installs
EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL before PciBusDxe starts.
When PciBusDxe calls CheckDevice() for a discovered PCI function, the
driver returns ACPI address descriptors with _MIF|_MAF set for fixed
BARs. Two small changes to PciBusDxe preserve these fixed addresses and
program them into the BAR registers during BAR programming.

After PciEnumerationComplete, QemuFixedBarsDxe walks each fixed
root-port hierarchy and programs the bridge memory windows to cover
the fixed BAR ranges assigned to endpoint devices.

Example 1: VFIO passthrough devices under a fixed BAR root port.

    -device pcie-root-port,id=pcie.port1,bus=pcie.1,chassis=1,io-reserve=0,fixed-bar=on
    -device x3130-upstream,id=upstream1,bus=pcie.port1
    -device xio3130-downstream,id=downstream1_1,bus=upstream1,chassis=1,slot=2
    -device vfio-pci-nohotplug,host=0018:06:00.0,bus=downstream1_1,id=dev0
    -set device.dev0.pci-bars=bar0@0x48000000000,bar2@0x50000000000,bar4@0x58000000000

Example 2: Extending the PCIe MMIO aperture with highmem-mmio-base
(and highmem-mmio-size), using a mix of emulated and VFIO devices.

    -machine virt,...,highmem-mmio-base=0x400000000000,highmem-mmio-size=0x400000000000
    -device e1000,netdev=net0,bus=pcie.0,pci-bars=bar0@0x10000000
    -device pcie-root-port,id=pcie.port9,bus=pcie.9,chassis=4,io-reserve=0,fixed-bar=on
    -device x3130-upstream,id=upstream9,bus=pcie.port9
    -device xio3130-downstream,id=downstream9_1,bus=upstream9,chassis=4,slot=1
    -device vfio-pci,host=0012:03:00.1,bus=downstream9_1,id=nic1,pci-bars=bar0@0x7000c0000000
    -device xio3130-downstream,id=downstream9_2,bus=upstream9,chassis=4,slot=2
    -device vfio-pci-nohotplug,host=0019:06:00.0,bus=downstream9_2,id=dev1
    -set device.dev1.pci-bars=bar0@0x6d4000000000,bar2@0x6d8000000000,bar4@0x6e0000000000

The pci-bars= property is generic and may be used with any PCI endpoint
device, including emulated devices and devices with 32-bit memory BARs.

The optional highmem-mmio-base machine property allows the operator to
reposition the high PCIe MMIO window so that the requested BAR
addresses can be placed within the configured address space.

Limitations:
- I/O BARs and the expansion ROM BAR are not covered by pci-bars=.
- This has only been tested on the AArch64 virt machine.
- SR-IOV VF BARs are not covered.

Testing:

The series was tested on the AArch64 virt machine using multiple PCI
topologies, including emulated devices, VFIO passthrough devices, PCIe
switches, and fixed BAR root ports.

A git branch with this series applied is available at:
https://github.com/tdavenvidia/upstream-qemu/tree/RFC-v2-fixed-bar-upstream

[1] RFC v1 upstream thread:
https://lore.kernel.org/qemu-devel/20260508183717.193630-1-tdave@nvidia.com/

Tushar Dave (5):
  hw/pci: add fixed-bar and pci-bars properties
  pci: add validation for fixed BAR configuration
  pci: add fixed BAR fw_cfg blob export
  hw/arm/virt: export fixed BAR metadata via fw_cfg
  hw/arm/virt: add highmem-mmio-base property

 hw/arm/virt.c                   |  78 ++++++++-
 hw/pci-bridge/pcie_root_port.c  |   1 +
 hw/pci/meson.build              |   2 +
 hw/pci/pci-fixed-bar-blob.c     | 291 ++++++++++++++++++++++++++++++++
 hw/pci/pci-fixed-bar-validate.c | 279 ++++++++++++++++++++++++++++++
 hw/pci/pci-fixed-bar-validate.h |  21 +++
 hw/pci/pci-fixed-bar.h          |  60 +++++++
 hw/pci/pci.c                    | 129 ++++++++++++++
 include/hw/pci/pci_device.h     |  10 ++
 include/hw/pci/pcie_port.h      |   1 +
 10 files changed, 871 insertions(+), 1 deletion(-)
 create mode 100644 hw/pci/pci-fixed-bar-blob.c
 create mode 100644 hw/pci/pci-fixed-bar-validate.c
 create mode 100644 hw/pci/pci-fixed-bar-validate.h
 create mode 100644 hw/pci/pci-fixed-bar.h

-- 
2.34.1



^ permalink raw reply	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 1/5] hw/pci: add fixed-bar and pci-bars properties
  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 ` Tushar Dave
  2026-08-27  0:40 ` [RFC PATCH v2 2/5] pci: add validation for fixed BAR configuration Tushar Dave
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 20+ messages in thread
From: Tushar Dave @ 2026-08-27  0:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: alwilliamson, jgg, skolothumtho, qemu-arm, peter.maydell, mst,
	marcel.apfelbaum, devel

Introduce two PCI properties used to configure fixed BAR placement.

The fixed-bar property is added to pcie-root-port. It identifies a
root port whose subordinate PCI hierarchy participates in fixed BAR
placement.

The pci-bars property is added to PCI devices and accepts explicit
BAR addresses in the form:

    pci-bars=barN@<addr>[,barM@<addr>]...

The parser validates the property syntax and stores the user-provided
addresses on the PCIDevice for later use.

These properties are generic and can be used with both emulated and
VFIO-backed PCI devices.

Signed-off-by: Tushar Dave <tdave@nvidia.com>
---
 hw/pci-bridge/pcie_root_port.c |   1 +
 hw/pci/pci.c                   | 129 +++++++++++++++++++++++++++++++++
 include/hw/pci/pci_device.h    |  10 +++
 include/hw/pci/pcie_port.h     |   1 +
 4 files changed, 141 insertions(+)

diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c
index 7c3e78010b..cea39b92ca 100644
--- a/hw/pci-bridge/pcie_root_port.c
+++ b/hw/pci-bridge/pcie_root_port.c
@@ -151,6 +151,7 @@ static void rp_exit(PCIDevice *d)
 static const Property rp_props[] = {
     DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present,
                     QEMU_PCIE_SLTCAP_PCP_BITNR, true),
+    DEFINE_PROP_BOOL("fixed-bar", PCIESlot, fixed_bar, false),
 };
 
 static void rp_instance_post_init(Object *obj)
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index d3191609e2..1a326f6f91 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -50,6 +50,7 @@
 #include "hw/core/boards.h"
 #include "hw/nvram/fw_cfg.h"
 #include "qapi/error.h"
+#include "qapi/util.h"
 #include "qemu/cutils.h"
 #include "pci-internal.h"
 
@@ -88,6 +89,7 @@ static const Property pci_props[] = {
     DEFINE_PROP_STRING("romfile", PCIDevice, romfile),
     DEFINE_PROP_UINT32("romsize", PCIDevice, romsize, UINT32_MAX),
     DEFINE_PROP_INT32("rombar",  PCIDevice, rom_bar, -1),
+    DEFINE_PROP_STRING("pci-bars", PCIDevice, pci_bars),
     DEFINE_PROP_BIT("multifunction", PCIDevice, cap_present,
                     QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false),
     DEFINE_PROP_BIT("x-pcie-lnksta-dllla", PCIDevice, cap_present,
@@ -225,6 +227,125 @@ static void pci_bus_unrealize(BusState *qbus)
     vmstate_unregister(NULL, &vmstate_pcibus, bus);
 }
 
+#define PCI_BARS_SYNTAX "expected barN@<addr>[,barM@<addr>]*; "
+
+static int pci_parse_bar_token(const char *tok, Error **errp)
+{
+    int v = qapi_enum_parse(&OffAutoPCIBAR_lookup, tok, -1, errp);
+
+    if (v < 0) {
+        return -1;
+    }
+    if (v < OFF_AUTO_PCIBAR_BAR0) {
+        error_setg(errp, "pci-bars: " PCI_BARS_SYNTAX
+                   "invalid BAR '%s', expected bar0..bar5", tok);
+        return -1;
+    }
+    return v - OFF_AUTO_PCIBAR_BAR0;
+}
+
+/*
+ * Parse pci-bars=barN@<addr>[,barM@<addr>]*
+ * Stores parsed addresses into pci_dev->fixed_bar_addrs[].
+ * BAR existence is checked here against io_regions[], which is already
+ * populated by the device's own realize() at this point. Alignment and
+ * MMIO-window placement checks are deferred to blob-write time, when
+ * the full fixed-bar topology is available.
+ */
+static void pci_parse_pci_bars(PCIDevice *pci_dev, Error **errp)
+{
+    Error *local_err = NULL;
+    char **entries = NULL;
+    char **parts = NULL;
+    const char *endp;
+    char **e;
+    uint64_t bar_addr;
+    PCIIORegion *r;
+    int index;
+    int i, ret;
+
+    if (!pci_dev->pci_bars || !*pci_dev->pci_bars) {
+        return;
+    }
+    if (DEVICE(pci_dev)->hotplugged) {
+        error_setg(&local_err,
+                   "pci-bars is not supported on hot-plugged devices");
+        goto out;
+    }
+
+    entries = g_strsplit(pci_dev->pci_bars, ",", -1);
+    for (e = entries; e && *e; e++) {
+        const char *entry = g_strstrip(*e);
+        if (*entry == '\0') {
+            error_setg(&local_err,
+                       "pci-bars: " PCI_BARS_SYNTAX "empty field in list");
+            goto out;
+        }
+
+        parts = g_strsplit(entry, "@", 2);
+        if (!parts[0] || !parts[1]) {
+            error_setg(&local_err,
+                       "pci-bars: " PCI_BARS_SYNTAX "missing '@' in '%s'",
+                       entry);
+            goto out;
+        }
+
+        index = pci_parse_bar_token(parts[0], &local_err);
+        if (index < 0) {
+            goto out;
+        }
+
+        r = &pci_dev->io_regions[index];
+        if (!r->size) {
+            error_setg(&local_err, "pci-bars: bar%d does not exist on %s",
+                       index, pci_dev->name);
+            goto out;
+        }
+        if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
+            error_setg(&local_err, "pci-bars: bar%d on %s is an I/O BAR, "
+                       "not a memory BAR", index, pci_dev->name);
+            goto out;
+        }
+
+        ret = qemu_strtou64(parts[1], &endp, 0, &bar_addr);
+        if (ret) {
+            error_setg(&local_err,
+                       "pci-bars: " PCI_BARS_SYNTAX
+                       "unparseable address in '%s'", entry);
+            goto out;
+        }
+        if (*endp != '\0') {
+            error_setg(&local_err,
+                       "pci-bars: " PCI_BARS_SYNTAX
+                       "trailing data after address in '%s'", entry);
+            goto out;
+        }
+        g_clear_pointer(&parts, g_strfreev);
+
+        if (!pci_dev->fixed_bar_addrs) {
+            pci_dev->fixed_bar_addrs = g_new(pcibus_t, PCI_NUM_REGIONS - 1);
+            for (i = 0; i < PCI_NUM_REGIONS - 1; i++) {
+                pci_dev->fixed_bar_addrs[i] = PCI_BAR_UNMAPPED;
+            }
+        }
+        if (pci_dev->fixed_bar_addrs[index] != PCI_BAR_UNMAPPED) {
+            error_setg(&local_err,
+                       "pci-bars: bar%d specified more than once",
+                       index);
+            goto out;
+        }
+        pci_dev->fixed_bar_addrs[index] = (pcibus_t)bar_addr;
+    }
+
+out:
+    g_clear_pointer(&parts, g_strfreev);
+    g_strfreev(entries);
+    if (local_err) {
+        g_clear_pointer(&pci_dev->fixed_bar_addrs, g_free);
+        error_propagate(errp, local_err);
+    }
+}
+
 static int pcibus_num(PCIBus *bus)
 {
     if (pci_bus_is_root(bus)) {
@@ -1479,6 +1600,7 @@ static void pci_qdev_unrealize(DeviceState *dev)
     pci_unregister_io_regions(pci_dev);
     pci_del_option_rom(pci_dev);
     pcie_sriov_unregister_device(pci_dev);
+    g_clear_pointer(&pci_dev->fixed_bar_addrs, g_free);
 
     if (pc->exit) {
         pc->exit(pci_dev);
@@ -2376,6 +2498,13 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp)
         is_default_rom = true;
     }
 
+    pci_parse_pci_bars(pci_dev, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        pci_qdev_unrealize(DEVICE(pci_dev));
+        return;
+    }
+
     pci_add_option_rom(pci_dev, is_default_rom, &local_err);
     if (local_err) {
         error_propagate(errp, local_err);
diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h
index 5cac6e1688..0bdc7ebce3 100644
--- a/include/hw/pci/pci_device.h
+++ b/include/hw/pci/pci_device.h
@@ -187,6 +187,16 @@ struct PCIDevice {
     uint32_t max_bounce_buffer_size;
 
     char *sriov_pf;
+
+    /*
+     * pci-bars property holds user-supplied fixed BAR addresses.
+     * pci_bars is the raw property string (barN@<addr>,...).
+     * fixed_bar_addrs is the parsed array (PCI_NUM_REGIONS-1 entries);
+     * each slot is PCI_BAR_UNMAPPED or the address for that BAR.
+     * NULL when the property is not set.
+     */
+    char      *pci_bars;
+    pcibus_t  *fixed_bar_addrs;
 };
 
 static inline int pci_intx(PCIDevice *pci_dev)
diff --git a/include/hw/pci/pcie_port.h b/include/hw/pci/pcie_port.h
index b28af067a6..ef66c9768b 100644
--- a/include/hw/pci/pcie_port.h
+++ b/include/hw/pci/pcie_port.h
@@ -65,6 +65,7 @@ struct PCIESlot {
 
     /* broken ACPI hotplug compat knob to preserve 6.1 ABI intact */
     bool        hide_native_hotplug_cap;
+    bool        fixed_bar;
 
     QLIST_ENTRY(PCIESlot) next;
 };
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 2/5] pci: add validation for fixed BAR configuration
  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
  2026-08-27  0:40 ` [RFC PATCH v2 3/5] pci: add fixed BAR fw_cfg blob export Tushar Dave
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 20+ messages in thread
From: Tushar Dave @ 2026-08-27  0:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: alwilliamson, jgg, skolothumtho, qemu-arm, peter.maydell, mst,
	marcel.apfelbaum, devel

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



^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 3/5] pci: add fixed BAR fw_cfg blob export
  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 ` [RFC PATCH v2 2/5] pci: add validation for fixed BAR configuration Tushar Dave
@ 2026-08-27  0:40 ` Tushar Dave
  2026-08-27  0:40 ` [RFC PATCH v2 4/5] hw/arm/virt: export fixed BAR metadata via fw_cfg Tushar Dave
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 20+ messages in thread
From: Tushar Dave @ 2026-08-27  0:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: alwilliamson, jgg, skolothumtho, qemu-arm, peter.maydell, mst,
	marcel.apfelbaum, devel

Introduce a fixed BAR fw_cfg blob exporter that serializes PCI
device and BAR information into the "etc/fixed-bars" fw_cfg file.

The blob contains an entry for every PCI function with memory BARs.
Each entry identifies whether the device has a fixed BAR
configuration, and fixed BAR entries include the configured address,
size, and type information.

Signed-off-by: Tushar Dave <tdave@nvidia.com>
---
 hw/pci/meson.build          |   1 +
 hw/pci/pci-fixed-bar-blob.c | 291 ++++++++++++++++++++++++++++++++++++
 hw/pci/pci-fixed-bar.h      |  60 ++++++++
 3 files changed, 352 insertions(+)
 create mode 100644 hw/pci/pci-fixed-bar-blob.c
 create mode 100644 hw/pci/pci-fixed-bar.h

diff --git a/hw/pci/meson.build b/hw/pci/meson.build
index 44b94a24b4..5d975f27ef 100644
--- a/hw/pci/meson.build
+++ b/hw/pci/meson.build
@@ -18,6 +18,7 @@ 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(when: 'CONFIG_PCI_EXPRESS', if_true: files('pci-fixed-bar-blob.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-blob.c b/hw/pci/pci-fixed-bar-blob.c
new file mode 100644
index 0000000000..d60ee65bf4
--- /dev/null
+++ b/hw/pci/pci-fixed-bar-blob.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved
+ * Write validated fixed-BAR device info to the etc/fixed-bars fw_cfg blob.
+ *
+ * Written by Tushar Dave
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/nvram/fw_cfg.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/pci/pci_host.h"
+#include "hw/pci/pcie_port.h"
+#include "qemu/error-report.h"
+#include "pci-internal.h"
+#include "pci-fixed-bar.h"
+#include "pci-fixed-bar-validate.h"
+
+typedef struct {
+    uint8_t  bar;
+    uint32_t flags;
+    uint64_t addr;
+    uint64_t size;
+} BarInfo;
+
+/* One PCI function entry to be written to the blob. */
+typedef struct {
+    uint8_t  rp_bus;     /* primary bus of the root port this device is under */
+    uint16_t vendor_id;
+    uint16_t device_id;
+    bool     is_fixed;
+    GArray  *bars;  /* array of BarInfo */
+} DeviceInfo;
+
+typedef struct {
+    GArray *devices;
+    PCIBus *root_bus;
+} CollectCtx;
+
+static void collect_device(PCIBus *bus, PCIDevice *pdev, void *opaque);
+
+/* Visit every device on this bus, in devfn order. */
+static void collect_bus(PCIBus *bus, void *opaque)
+{
+    pci_for_each_device_under_bus(bus, collect_device, opaque);
+}
+
+/*
+ * Primary bus number of the nearest TYPE_PCIE_ROOT_PORT ancestor of bus
+ * — i.e. the bus the root port device itself lives on, not its
+ * secondary bus. Falls back to the host bridge's own bus number if bus
+ * isn't under any root port (e.g. a device directly on the host
+ * bridge's primary bus).
+ */
+static uint8_t find_root_port_bus(PCIBus *bus, PCIBus *host_bus)
+{
+    PCIDevice *parent;
+
+    while (bus) {
+        parent = bus->parent_dev;
+        if (!parent) {
+            break;
+        }
+        if (object_dynamic_cast(OBJECT(parent), TYPE_PCIE_ROOT_PORT)) {
+            return pci_bus_num(pci_get_bus(parent));
+        }
+        bus = pci_get_bus(parent);
+    }
+    return pci_bus_num(host_bus);
+}
+
+static void collect_device(PCIBus *bus, PCIDevice *pdev, void *opaque)
+{
+    CollectCtx *ctx = opaque;
+    bool has_mem_bar = false;
+    bool is_64bit, is_pref;
+    DeviceInfo dev;
+    PCIIORegion *r;
+    PCIBus *sec;
+    BarInfo bi;
+    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;
+        }
+    }
+
+    /*
+     * Every device with a memory BAR gets an entry, fixed or not — not
+     * just ones with pci-bars= set. This preserves the exact device
+     * sequence CheckDevice() needs for positional VID:DID matching (see
+     * collect_all_host_bridges()); only fixed devices carry BAR records
+     * (dev.bars stays empty, dev.is_fixed false, otherwise).
+     */
+    if (has_mem_bar) {
+        memset(&dev, 0, sizeof(dev));
+        dev.rp_bus    = find_root_port_bus(bus, ctx->root_bus);
+        dev.vendor_id = pci_get_word(pdev->config + PCI_VENDOR_ID);
+        dev.device_id = pci_get_word(pdev->config + PCI_DEVICE_ID);
+        dev.is_fixed  = (pdev->fixed_bar_addrs != NULL);
+        dev.bars      = g_array_new(false, true, sizeof(BarInfo));
+
+        if (dev.is_fixed) {
+            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);
+                is_pref  = !!(r->type & PCI_BASE_ADDRESS_MEM_PREFETCH);
+
+                memset(&bi, 0, sizeof(bi));
+                bi.bar   = i;
+                bi.addr  = (uint64_t)pdev->fixed_bar_addrs[i];
+                bi.size  = r->size;
+                bi.flags = (is_64bit ? QEMU_FIXED_BAR_F_MEM64 : 0) |
+                           (is_pref  ? QEMU_FIXED_BAR_F_PREF  : 0);
+                g_array_append_val(dev.bars, bi);
+            }
+        }
+        g_array_append_val(ctx->devices, dev);
+    }
+
+    if (!object_dynamic_cast(OBJECT(pdev), TYPE_PCI_BRIDGE)) {
+        return;
+    }
+    sec = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev));
+    if (sec) {
+        collect_bus(sec, opaque);
+    }
+}
+
+static gint cmp_host_bus_num(gconstpointer a, gconstpointer b)
+{
+    PCIHostState *ha = *(PCIHostState **)a;
+    PCIHostState *hb = *(PCIHostState **)b;
+    return (gint)pci_bus_num(ha->bus) - (gint)pci_bus_num(hb->bus);
+}
+
+/*
+ * EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL's CheckDevice interface
+ * (UEFI PI Spec 1.2 Vol 5) only passes VendorId/DeviceId/RevisionId/
+ * SubsystemVendorId/SubsystemDeviceId, no BDF — so in EDK2 we can't
+ * match a blob entry to a specific device directly using BDF. To work
+ * around that, we prepare the blob entries in the same order PciBusDxe
+ * discovers devices, so matching by VID:DID inherently works.
+ */
+static void collect_all_host_bridges(CollectCtx *ctx)
+{
+    PCIHostState *hb;
+    GPtrArray *sorted;
+    guint i;
+
+    sorted = g_ptr_array_new();
+    QLIST_FOREACH(hb, &pci_host_bridges, next) {
+        if (hb->bus) {
+            g_ptr_array_add(sorted, hb);
+        }
+    }
+    g_ptr_array_sort(sorted, cmp_host_bus_num);
+
+    for (i = 0; i < sorted->len; i++) {
+        hb = g_ptr_array_index(sorted, i);
+        ctx->root_bus = hb->bus;
+        collect_bus(hb->bus, ctx);
+    }
+
+    g_ptr_array_free(sorted, true);
+}
+
+static uint8_t *create_blob(CollectCtx *ctx, size_t *blob_size)
+{
+    QemuFixedBarsDevice *dout;
+    QemuFixedBarsBar *bout;
+    QemuFixedBarsHdr *hdr;
+    DeviceInfo *dev;
+    uint8_t *blob;
+    uint8_t *ptr;
+    BarInfo *bi;
+    size_t sz;
+    guint d, b;
+
+    sz = sizeof(QemuFixedBarsHdr);
+    for (d = 0; d < ctx->devices->len; d++) {
+        dev = &g_array_index(ctx->devices, DeviceInfo, d);
+        sz += sizeof(QemuFixedBarsDevice);
+        sz += dev->bars->len * sizeof(QemuFixedBarsBar);
+    }
+
+    blob = g_malloc0(sz);
+    hdr  = (QemuFixedBarsHdr *)blob;
+    hdr->version     = cpu_to_le32(QEMU_FIXED_BARS_VERSION);
+    hdr->num_devices = cpu_to_le32(ctx->devices->len);
+
+    ptr = blob + sizeof(*hdr);
+    for (d = 0; d < ctx->devices->len; d++) {
+        dev  = &g_array_index(ctx->devices, DeviceInfo, d);
+        dout = (QemuFixedBarsDevice *)ptr;
+
+        dout->vendor_id = cpu_to_le16(dev->vendor_id);
+        dout->device_id = cpu_to_le16(dev->device_id);
+        dout->dev_flags = dev->is_fixed ? QEMU_FIXED_BARS_DEV_F_FIXED : 0;
+        dout->rp_bus    = dev->rp_bus;
+        dout->num_bars  = dev->bars->len;
+        dout->reserved  = 0;
+        ptr += sizeof(QemuFixedBarsDevice);
+
+        for (b = 0; b < dev->bars->len; b++) {
+            bi   = &g_array_index(dev->bars, BarInfo, b);
+            bout = (QemuFixedBarsBar *)ptr;
+
+            bout->bar     = bi->bar;
+            bout->flags   = cpu_to_le32(bi->flags);
+            bout->address = cpu_to_le64(bi->addr);
+            bout->size    = cpu_to_le64(bi->size);
+            ptr += sizeof(QemuFixedBarsBar);
+        }
+    }
+
+    *blob_size = sz;
+    return blob;
+}
+
+/*
+ * fixed_bars_write_blob - validate fixed BAR addresses and write fw_cfg blob.
+ *
+ * @fw_cfg:       fw_cfg state to write the etc/fixed-bars blob into.
+ * @mmio32_base:  base of the machine's 32-bit PCIe MMIO aperture.
+ * @mmio32_size:  size of the machine's 32-bit PCIe MMIO aperture.
+ * @mmio64_base:  base of the machine's 64-bit PCIe MMIO aperture.
+ * @mmio64_size:  size of the machine's 64-bit PCIe MMIO aperture.
+ *
+ * Returns true if any fixed BAR device was found and the blob was
+ * written, false if there was nothing to do. Aborts if any BAR
+ * address fails validation.
+ */
+bool fixed_bars_write_blob(FWCfgState *fw_cfg,
+                           uint64_t mmio32_base,
+                           uint64_t mmio32_size,
+                           uint64_t mmio64_base,
+                           uint64_t mmio64_size)
+{
+    FixedBarsInfo info;
+    CollectCtx ctx;
+    uint8_t *blob;
+    size_t blob_size;
+    guint d;
+
+    if (!mmio32_size || !mmio64_size) {
+        error_report("pci-bars: zero-size PCIe MMIO aperture "
+                     "(32-bit=0x%"PRIx64", 64-bit=0x%"PRIx64")",
+                     mmio32_size, mmio64_size);
+        exit(1);
+    }
+    if (mmio32_size - 1 > UINT64_MAX - mmio32_base ||
+        mmio64_size - 1 > UINT64_MAX - mmio64_base) {
+        error_report("pci-bars: PCIe MMIO aperture base+size overflows "
+                     "(32-bit base=0x%"PRIx64" size=0x%"PRIx64", "
+                     "64-bit base=0x%"PRIx64" size=0x%"PRIx64")",
+                     mmio32_base, mmio32_size, mmio64_base, mmio64_size);
+        exit(1);
+    }
+
+    info.mmio32_base = mmio32_base;
+    info.mmio32_limit = mmio32_base + mmio32_size - 1;
+    info.mmio64_base = mmio64_base;
+    info.mmio64_limit = mmio64_base + mmio64_size - 1;
+
+    if (!fixed_bars_validate(&info)) {
+        return false;
+    }
+
+    ctx.devices  = g_array_new(false, true, sizeof(DeviceInfo));
+    ctx.root_bus = NULL;
+    collect_all_host_bridges(&ctx);
+
+    blob = create_blob(&ctx, &blob_size);
+    fw_cfg_add_file(fw_cfg, FW_CFG_FIXED_BARS, blob, blob_size);
+
+    for (d = 0; d < ctx.devices->len; d++) {
+        g_array_free(g_array_index(ctx.devices, DeviceInfo, d).bars, true);
+    }
+    g_array_free(ctx.devices, true);
+
+    return true;
+}
diff --git a/hw/pci/pci-fixed-bar.h b/hw/pci/pci-fixed-bar.h
new file mode 100644
index 0000000000..3afc22a733
--- /dev/null
+++ b/hw/pci/pci-fixed-bar.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved
+ * Fixed-BAR — QEMU side declarations.
+ *
+ * Written by Tushar Dave
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef HW_PCI_FIXED_BAR_H
+#define HW_PCI_FIXED_BAR_H
+
+#include "hw/nvram/fw_cfg.h"
+#include "hw/pci/pci_bus.h"
+
+#define FW_CFG_FIXED_BARS        "etc/fixed-bars"
+#define QEMU_FIXED_BARS_VERSION  1
+
+/*
+ * On-wire layout (little-endian, packed):
+ *
+ *   QemuFixedBarsHdr
+ *
+ *   For each device (num_devices total):
+ *     QemuFixedBarsDevice               -- device header
+ *     QemuFixedBarsBar[num_bars]        -- one record per BAR (FIXED only)
+ */
+
+typedef struct {
+    uint32_t version;     /* QEMU_FIXED_BARS_VERSION */
+    uint32_t num_devices;
+} QEMU_PACKED QemuFixedBarsHdr;  /* 8 bytes */
+
+typedef struct {
+    uint16_t vendor_id;
+    uint16_t device_id;
+    uint8_t  dev_flags;  /* QEMU_FIXED_BARS_DEV_F_* */
+    uint8_t  rp_bus;     /* primary bus of the root port this device is under */
+    uint8_t  num_bars;
+    uint8_t  reserved;
+} QEMU_PACKED QemuFixedBarsDevice;  /* 8 bytes */
+
+typedef struct {
+    uint8_t  bar;
+    uint8_t  reserved[3];
+    uint32_t flags;    /* QEMU_FIXED_BAR_F_* */
+    uint64_t address;
+    uint64_t size;
+} QEMU_PACKED QemuFixedBarsBar;  /* 24 bytes */
+
+#define QEMU_FIXED_BAR_F_MEM64       (1u << 0)
+#define QEMU_FIXED_BAR_F_PREF        (1u << 1)
+#define QEMU_FIXED_BARS_DEV_F_FIXED  (1u << 0)
+
+bool fixed_bars_write_blob(FWCfgState *fw_cfg,
+                           uint64_t mmio32_base,
+                           uint64_t mmio32_size,
+                           uint64_t mmio64_base,
+                           uint64_t mmio64_size);
+
+#endif /* HW_PCI_FIXED_BAR_H */
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 4/5] hw/arm/virt: export fixed BAR metadata via fw_cfg
  2026-08-27  0:40 [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement Tushar Dave
                   ` (2 preceding siblings ...)
  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 ` 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
  5 siblings, 0 replies; 20+ messages in thread
From: Tushar Dave @ 2026-08-27  0:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: alwilliamson, jgg, skolothumtho, qemu-arm, peter.maydell, mst,
	marcel.apfelbaum, devel

Export fixed BAR metadata from the virt machine through the
"etc/fixed-bars" fw_cfg file during machine initialization.

Fixed BAR placement requires firmware to preserve the PCI resource
configuration established by QEMU. ACPI provides this via the PCI
boot configuration preservation mechanism, _DSM function 5.

Reject fixed BAR configuration when ACPI is disabled, and request PCI
boot configuration preservation when ACPI is enabled.

Signed-off-by: Tushar Dave <tdave@nvidia.com>
---
 hw/arm/virt.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index e7a56e37f7..cb9cd2d25c 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -96,6 +96,7 @@
 #include "hw/cxl/cxl_host.h"
 #include "qemu/guest-random.h"
 #include "hw/watchdog/sbsa_gwdt.h"
+#include "hw/pci/pci-fixed-bar.h"
 
 static GlobalProperty arm_virt_compat_defaults[] = {
     { TYPE_VIRTIO_IOMMU_PCI, "aw-bits", "48" },
@@ -2440,6 +2441,21 @@ void virt_machine_done(Notifier *notifier, void *data)
                                        vms->memmap[VIRT_PLATFORM_BUS].size,
                                        vms->irqmap[VIRT_PLATFORM_BUS]);
     }
+    if (fixed_bars_write_blob(vms->fw_cfg,
+                              vms->memmap[VIRT_PCIE_MMIO].base,
+                              vms->memmap[VIRT_PCIE_MMIO].size,
+                              vms->memmap[VIRT_HIGH_PCIE_MMIO].base,
+                              vms->memmap[VIRT_HIGH_PCIE_MMIO].size)) {
+        if (!virt_is_acpi_enabled(vms)) {
+            error_report("pci-bars: fixed BAR placement requires ACPI to "
+                         "preserve the PCI boot configuration, otherwise "
+                         "these BAR assignments could be reassigned later "
+                         "with no protection");
+            exit(1);
+        }
+        vms->pci_preserve_config = true;
+    }
+
     if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, as, ms, cpu) < 0) {
         exit(1);
     }
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 5/5] hw/arm/virt: add highmem-mmio-base property
  2026-08-27  0:40 [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement Tushar Dave
                   ` (3 preceding siblings ...)
  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 ` Tushar Dave
  2026-08-27  7:18 ` [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement Gerd Hoffmann
  5 siblings, 0 replies; 20+ messages in thread
From: Tushar Dave @ 2026-08-27  0:40 UTC (permalink / raw)
  To: qemu-devel
  Cc: alwilliamson, jgg, skolothumtho, qemu-arm, peter.maydell, mst,
	marcel.apfelbaum, devel

Add a highmem-mmio-base property to override the base of the virt
machine's high PCIe MMIO window, alongside the existing
highmem-mmio-size:

  -machine virt,highmem-mmio-base=<addr>,highmem-mmio-size=<size>

Some PCI devices require MMIO resources to be placed within a
specific address range that cannot be satisfied by the default
placement. The configured base is validated to be non-zero, aligned
to highmem-mmio-size, non-overlapping with the existing high memory
layout, and within the configured physical address space.

Signed-off-by: Tushar Dave <tdave@nvidia.com>
---
 hw/arm/virt.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 61 insertions(+), 1 deletion(-)

diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index cb9cd2d25c..47653b8753 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2515,9 +2515,34 @@ static void virt_set_high_memmap(VirtMachineState *vms,
 
     for (i = VIRT_LOWMEMMAP_LAST; i < ARRAY_SIZE(extended_memmap); i++) {
         region_enabled = virt_get_high_memmap_enabled(vms, i);
-        region_base = ROUND_UP(base, extended_memmap[i].size);
         region_size = extended_memmap[i].size;
 
+        if (i == VIRT_HIGH_PCIE_MMIO && extended_memmap[i].base) {
+            region_base = extended_memmap[i].base;
+
+            if (region_base < base) {
+                error_report("highmem-mmio-base 0x%"PRIx64" overlaps prior "
+                             "high memory layout (must be >= 0x%"PRIx64")",
+                             region_base, base);
+                exit(1);
+            }
+            if (region_base % region_size != 0) {
+                error_report("highmem-mmio-base 0x%"PRIx64" must be "
+                             "aligned to highmem-mmio-size 0x%"PRIx64,
+                             region_base, region_size);
+                exit(1);
+            }
+            if (region_base + region_size > BIT_ULL(pa_bits)) {
+                error_report("highmem-mmio-base + highmem-mmio-size "
+                             "[0x%"PRIx64", 0x%"PRIx64") exceeds %d-bit PA "
+                             "space", region_base,
+                             region_base + region_size, pa_bits);
+                exit(1);
+            }
+        } else {
+            region_base = ROUND_UP(base, region_size);
+        }
+
         vms->memmap[i].base = region_base;
         vms->memmap[i].size = region_size;
 
@@ -3418,6 +3443,33 @@ static void virt_set_highmem_mmio_size(Object *obj, Visitor *v,
     extended_memmap[VIRT_HIGH_PCIE_MMIO].size = size;
 }
 
+static void virt_get_highmem_mmio_base(Object *obj, Visitor *v,
+                                       const char *name, void *opaque,
+                                       Error **errp)
+{
+    uint64_t base = extended_memmap[VIRT_HIGH_PCIE_MMIO].base;
+
+    visit_type_size(v, name, &base, errp);
+}
+
+static void virt_set_highmem_mmio_base(Object *obj, Visitor *v,
+                                       const char *name, void *opaque,
+                                       Error **errp)
+{
+    uint64_t base;
+
+    if (!visit_type_size(v, name, &base, errp)) {
+        return;
+    }
+
+    if (base == 0) {
+        error_setg(errp, "highmem-mmio-base cannot be 0");
+        return;
+    }
+
+    extended_memmap[VIRT_HIGH_PCIE_MMIO].base = base;
+}
+
 static char *virt_get_msi(Object *obj, Error **errp)
 {
     VirtMachineState *vms = VIRT_MACHINE(obj);
@@ -4285,6 +4337,14 @@ static void virt_machine_class_init(ObjectClass *oc, const void *data)
                                           "Set the high memory region size "
                                           "for PCI MMIO");
 
+    object_class_property_add(oc, "highmem-mmio-base", "size",
+                                   virt_get_highmem_mmio_base,
+                                   virt_set_highmem_mmio_base,
+                                   NULL, NULL);
+    object_class_property_set_description(oc, "highmem-mmio-base",
+                                          "Set the high memory region base "
+                                          "for PCI MMIO");
+
     object_class_property_add(oc, "virtio-mmio-transports", "uint8",
                                    virt_get_virtio_transports,
                                    virt_set_virtio_transports,
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement
  2026-08-27  0:40 [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement Tushar Dave
                   ` (4 preceding siblings ...)
  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 ` Gerd Hoffmann
  2026-08-27 13:47   ` Alex Williamson
  5 siblings, 1 reply; 20+ messages in thread
From: Gerd Hoffmann @ 2026-08-27  7:18 UTC (permalink / raw)
  To: Tushar Dave
  Cc: qemu-devel, alwilliamson, jgg, skolothumtho, qemu-arm,
	peter.maydell, mst, marcel.apfelbaum, devel

  Hi,

> Following the feedback, RFC v2 keeps PCI enumeration and resource
> assignment in firmware. QEMU only validates the user-provided fixed
> BAR configuration and provides the required metadata to firmware
> through the "etc/fixed-bars" fw_cfg file.

qemu already has vendor-specific pci capabilities.  They are used to
pass hints for the bridge window sizes of pci bridges (including pcie
root ports) with hotplug support.  See OvmfPkg/PciHotPlugInitDxe/ for
the firmware side support.

I'd strongly recommend to do the same for the fixed bars:  Add a pci
capability to pass that information.  All the logic you have today to
link the information in the fw_cfg file to the correct pci device is
simply not needed any more then.

> * fixed-bar=on on a PCIe root port marks its subordinate hierarchy for
>   fixed BAR placement. Every device with memory BARs in that hierarchy
>   must provide a complete pci-bars= configuration.

Why is this needed?

> * pci-bars=barN@<addr>[,barM@<addr>]... on a PCI endpoint specifies the
>   required address for each memory BAR. All memory BARs on the device
>   must have an explicitly assigned address.

fixed-bar-<nr>=<addr> ?

> On the firmware side, a new DXE driver, QemuFixedBarsDxe, installs
> EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL before PciBusDxe starts.
> When PciBusDxe calls CheckDevice() for a discovered PCI function, the
> driver returns ACPI address descriptors with _MIF|_MAF set for fixed
> BARs. Two small changes to PciBusDxe preserve these fixed addresses and
> program them into the BAR registers during BAR programming.

Expecting PciBusDxe respecting AddrRangeMin looks sensible to me ...

> After PciEnumerationComplete, QemuFixedBarsDxe walks each fixed
> root-port hierarchy and programs the bridge memory windows to cover
> the fixed BAR ranges assigned to endpoint devices.

... but changing things after-the-fact in platform code is a complete
non-starter.  PciBusDxe needs to do that, i.e. take care that the bridge
window assigned actually cover the fixed pci bars.

take care,
  Gerd



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement
  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-31 13:24     ` Gerd Hoffmann
  0 siblings, 2 replies; 20+ messages in thread
From: Alex Williamson @ 2026-08-27 13:47 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: alex, Tushar Dave, qemu-devel, jgg, skolothumtho, qemu-arm,
	peter.maydell, mst, marcel.apfelbaum, devel

On Thu, 27 Aug 2026 09:18:50 +0200
Gerd Hoffmann <kraxel@redhat.com> wrote:

>   Hi,
> 
> > Following the feedback, RFC v2 keeps PCI enumeration and resource
> > assignment in firmware. QEMU only validates the user-provided fixed
> > BAR configuration and provides the required metadata to firmware
> > through the "etc/fixed-bars" fw_cfg file.  
> 
> qemu already has vendor-specific pci capabilities.  They are used to
> pass hints for the bridge window sizes of pci bridges (including pcie
> root ports) with hotplug support.  See OvmfPkg/PciHotPlugInitDxe/ for
> the firmware side support.
> 
> I'd strongly recommend to do the same for the fixed bars:  Add a pci
> capability to pass that information.  All the logic you have today to
> link the information in the fw_cfg file to the correct pci device is
> simply not needed any more then.

Placement of a VMM defined capability into a vfio-pci device is not
such a trivial problem as it is for emulated devices.  Space may not be
readily available and the capability may mask non-architected registers.

Does this suggestion relate to fixing the gap between mapping fw_cfg
entries by vendor/device IDs or is there something fundamentally
undesirable about using fw_cfg here?
 
> > * fixed-bar=on on a PCIe root port marks its subordinate hierarchy for
> >   fixed BAR placement. Every device with memory BARs in that hierarchy
> >   must provide a complete pci-bars= configuration.  
> 
> Why is this needed?

AIUI, the problem space is greatly expanded if we mix user provided
fixed-bars with firmware assigned BARs and it's possible that there is
no solution that meets the requirements.  This option both simplifies
the problem space and allows the resource windows to be audited to
generate user actionable errors in QEMU.

> > * pci-bars=barN@<addr>[,barM@<addr>]... on a PCI endpoint specifies the
> >   required address for each memory BAR. All memory BARs on the device
> >   must have an explicitly assigned address.  
> 
> fixed-bar-<nr>=<addr> ?

Could be a reasonable alternative.  I'll let Tushar or others wrestle
with the deeper edk2 comments below ;)  Thanks,

Alex

> > On the firmware side, a new DXE driver, QemuFixedBarsDxe, installs
> > EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL before PciBusDxe starts.
> > When PciBusDxe calls CheckDevice() for a discovered PCI function, the
> > driver returns ACPI address descriptors with _MIF|_MAF set for fixed
> > BARs. Two small changes to PciBusDxe preserve these fixed addresses and
> > program them into the BAR registers during BAR programming.  
> 
> Expecting PciBusDxe respecting AddrRangeMin looks sensible to me ...
> 
> > After PciEnumerationComplete, QemuFixedBarsDxe walks each fixed
> > root-port hierarchy and programs the bridge memory windows to cover
> > the fixed BAR ranges assigned to endpoint devices.  
> 
> ... but changing things after-the-fact in platform code is a complete
> non-starter.  PciBusDxe needs to do that, i.e. take care that the bridge
> window assigned actually cover the fixed pci bars.
> 
> take care,
>   Gerd
> 
> 



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [edk2-devel] [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement
  2026-08-27 13:47   ` Alex Williamson
@ 2026-08-27 14:38     ` Ard Biesheuvel
  2026-08-28 15:49       ` Tushar Dave
  2026-08-31 13:24     ` Gerd Hoffmann
  1 sibling, 1 reply; 20+ messages in thread
From: Ard Biesheuvel @ 2026-08-27 14:38 UTC (permalink / raw)
  To: devel@edk2.groups.io, Alex Williamson, Gerd Hoffmann
  Cc: Tushar Dave, qemu-devel, Jason Gunthorpe, skolothumtho, qemu-arm,
	peter.maydell, Michael S. Tsirkin, marcel.apfelbaum


On Thu, 27 Aug 2026, at 15:47, Alex Williamson via groups.io wrote:
> On Thu, 27 Aug 2026 09:18:50 +0200
> Gerd Hoffmann <kraxel@redhat.com> wrote:
>
>>   Hi,
>> 
>> > Following the feedback, RFC v2 keeps PCI enumeration and resource
>> > assignment in firmware. QEMU only validates the user-provided fixed
>> > BAR configuration and provides the required metadata to firmware
>> > through the "etc/fixed-bars" fw_cfg file.  
>> 
>> qemu already has vendor-specific pci capabilities.  They are used to
>> pass hints for the bridge window sizes of pci bridges (including pcie
>> root ports) with hotplug support.  See OvmfPkg/PciHotPlugInitDxe/ for
>> the firmware side support.
>> 
>> I'd strongly recommend to do the same for the fixed bars:  Add a pci
>> capability to pass that information.  All the logic you have today to
>> link the information in the fw_cfg file to the correct pci device is
>> simply not needed any more then.
>
> Placement of a VMM defined capability into a vfio-pci device is not
> such a trivial problem as it is for emulated devices.  Space may not be
> readily available and the capability may mask non-architected registers.
>

Is this the reason we cannot rely on the Enhanced Allocation (EA)
capability here?

> Does this suggestion relate to fixing the gap between mapping fw_cfg
> entries by vendor/device IDs or is there something fundamentally
> undesirable about using fw_cfg here?
> 

I much prefer this approach over the previous one, as the PCI resource
allocation logic remains in the firmware where it belongs.

However, the OS may still re-assign/re-balance things in some cases, and
so using a non-standard mechanism here means that the OS needs to learn
that these devices are special.

...
>
>> > On the firmware side, a new DXE driver, QemuFixedBarsDxe, installs
>> > EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL before PciBusDxe starts.
>> > When PciBusDxe calls CheckDevice() for a discovered PCI function, the
>> > driver returns ACPI address descriptors with _MIF|_MAF set for fixed
>> > BARs. Two small changes to PciBusDxe preserve these fixed addresses and
>> > program them into the BAR registers during BAR programming.  
>> 
>> Expecting PciBusDxe respecting AddrRangeMin looks sensible to me ...
>> 
>> > After PciEnumerationComplete, QemuFixedBarsDxe walks each fixed
>> > root-port hierarchy and programs the bridge memory windows to cover
>> > the fixed BAR ranges assigned to endpoint devices.  
>> 
>> ... but changing things after-the-fact in platform code is a complete
>> non-starter.  PciBusDxe needs to do that, i.e. take care that the bridge
>> window assigned actually cover the fixed pci bars.
>> 

Agreed - if the bridge windows are not programmed correctly on the first
pass, there is something in the code that needs to be fixed. I don't think
papering over it like this is the right approach.



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [edk2-devel] [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement
  2026-08-27 14:38     ` [edk2-devel] " Ard Biesheuvel
@ 2026-08-28 15:49       ` Tushar Dave
  2026-08-31 13:42         ` Gerd Hoffmann
  0 siblings, 1 reply; 20+ messages in thread
From: Tushar Dave @ 2026-08-28 15:49 UTC (permalink / raw)
  To: Ard Biesheuvel, devel@edk2.groups.io, Alex Williamson,
	Gerd Hoffmann
  Cc: qemu-devel@nongnu.org, Jason Gunthorpe, Shameer Kolothum Thodi,
	qemu-arm@nongnu.org, peter.maydell@linaro.org, Michael S. Tsirkin,
	marcel.apfelbaum@gmail.com



On 8/27/2026 9:38 AM, Ard Biesheuvel wrote:
> 
> On Thu, 27 Aug 2026, at 15:47, Alex Williamson via groups.io wrote:
>> On Thu, 27 Aug 2026 09:18:50 +0200
>> Gerd Hoffmann <kraxel@redhat.com> wrote:
>>
>>>   Hi,
>>>
>>>> Following the feedback, RFC v2 keeps PCI enumeration and resource
>>>> assignment in firmware. QEMU only validates the user-provided fixed
>>>> BAR configuration and provides the required metadata to firmware
>>>> through the "etc/fixed-bars" fw_cfg file.  
>>>
>>> qemu already has vendor-specific pci capabilities.  They are used to
>>> pass hints for the bridge window sizes of pci bridges (including pcie
>>> root ports) with hotplug support.  See OvmfPkg/PciHotPlugInitDxe/ for
>>> the firmware side support.
>>>
>>> I'd strongly recommend to do the same for the fixed bars:  Add a pci
>>> capability to pass that information.  All the logic you have today to
>>> link the information in the fw_cfg file to the correct pci device is
>>> simply not needed any more then.
>>
>> Placement of a VMM defined capability into a vfio-pci device is not
>> such a trivial problem as it is for emulated devices.  Space may not be
>> readily available and the capability may mask non-architected registers.
>>
> 
> Is this the reason we cannot rely on the Enhanced Allocation (EA)
> capability here?

AFAICT, this was explored on RFC v1.
EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL was suggested as the
shorter path at the time, which is what this series uses.
> 
>> Does this suggestion relate to fixing the gap between mapping fw_cfg
>> entries by vendor/device IDs or is there something fundamentally
>> undesirable about using fw_cfg here?
>>
> 
> I much prefer this approach over the previous one, as the PCI resource
> allocation logic remains in the firmware where it belongs.
> 
> However, the OS may still re-assign/re-balance things in some cases, and
> so using a non-standard mechanism here means that the OS needs to learn
> that these devices are special.

This fixed-bar design already accounted for this using ACPI _DSM in
patch 4/5 — fixed BAR placement requires ACPI and emits the standard
_DSM function 5 (Preserve PCI Boot Configuration) for the hierarchy, so
the OS is told not to reassign these BARs.

> 
> ...
>>
>>>> On the firmware side, a new DXE driver, QemuFixedBarsDxe, installs
>>>> EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL before PciBusDxe starts.
>>>> When PciBusDxe calls CheckDevice() for a discovered PCI function, the
>>>> driver returns ACPI address descriptors with _MIF|_MAF set for fixed
>>>> BARs. Two small changes to PciBusDxe preserve these fixed addresses and
>>>> program them into the BAR registers during BAR programming.  
>>>
>>> Expecting PciBusDxe respecting AddrRangeMin looks sensible to me ...
>>>
>>>> After PciEnumerationComplete, QemuFixedBarsDxe walks each fixed
>>>> root-port hierarchy and programs the bridge memory windows to cover
>>>> the fixed BAR ranges assigned to endpoint devices.  
>>>
>>> ... but changing things after-the-fact in platform code is a complete
>>> non-starter.  PciBusDxe needs to do that, i.e. take care that the bridge
>>> window assigned actually cover the fixed pci bars.
>>>
> 
> Agreed - if the bridge windows are not programmed correctly on the first
> pass, there is something in the code that needs to be fixed. I don't think
> papering over it like this is the right approach.

That's a fair point. I did the window-sizing after PciBusDxe because I
didn't want to touch existing PciBusDxe code too much.

As per my understanding, PciBusDxe's enumeration splits into three phases:

Phase 1 (PciHostBridgeEnumerator) walks the whole tree and calls
CheckDevice() per device — by the time this phase finishes, every fixed
BAR address is already known and cached on the device
(PciBar[Bar].FixedBaseAddress).

Phase 2 (PciHostBridgeResourceAllocator) prepares an address for every
resource node in the tree — both individual BARs and bridge windows
alike — purely from size and alignment; PCI_RESOURCE_NODE has no address
field at all, so this is entirely blind to whether a fixed address was
already required.

Phase 3 (ProgramResource) then writes the actual PCI config-space
registers, and by default it just writes whatever address Phase 2
prepared, for both BARs and bridge windows. The one exception is
ProgramBar() — it specifically checks whether that particular BAR was
marked fixed back in Phase 1, and if so, overrides Phase 2's prepared
base address with the real fixed one. However, ProgramPpbApperture(),
which writes the bridge's own window registers, has no equivalent
override — it always writes whatever Phase 2 prepared, with no awareness
of a fixed BAR anywhere underneath it. And that needs fixing, and for
that I have to change the existing code.

I think the fix would be to extend Phase 2's own sizing step
(CalculateResourceAperture() in PciResourceSupport.c) to check for the
already-known fixed address on each child and, when present, size and
position the window as the exact union of those addresses instead of the
blind size-only sum. Does that match the direction you had in mind, or
is there a different integration point you'd suggest?


Thanks.
-Tushar





^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement
  2026-08-27 13:47   ` Alex Williamson
  2026-08-27 14:38     ` [edk2-devel] " Ard Biesheuvel
@ 2026-08-31 13:24     ` Gerd Hoffmann
  2026-09-01 22:27       ` Tushar Dave
  1 sibling, 1 reply; 20+ messages in thread
From: Gerd Hoffmann @ 2026-08-31 13:24 UTC (permalink / raw)
  To: Alex Williamson
  Cc: Tushar Dave, qemu-devel, jgg, skolothumtho, qemu-arm,
	peter.maydell, mst, marcel.apfelbaum, devel

On Thu, Aug 27, 2026 at 07:47:33AM -0600, Alex Williamson wrote:
> On Thu, 27 Aug 2026 09:18:50 +0200
> Gerd Hoffmann <kraxel@redhat.com> wrote:
> 
> > I'd strongly recommend to do the same for the fixed bars:  Add a pci
> > capability to pass that information.  All the logic you have today to
> > link the information in the fw_cfg file to the correct pci device is
> > simply not needed any more then.
> 
> Placement of a VMM defined capability into a vfio-pci device is not
> such a trivial problem as it is for emulated devices.  Space may not be
> readily available and the capability may mask non-architected registers.
> 
> Does this suggestion relate to fixing the gap between mapping fw_cfg
> entries by vendor/device IDs or is there something fundamentally
> undesirable about using fw_cfg here?

Well, fw_cfg is the fallback option if we don't have any better way.
Attaching the information directly to the device by placing it in a
pci capability is at very minimum worth exploring.  If this is not
working for vfio devices, ok, we have to accept that I guess.

And, yes, the logic to match entries in the fw_cfg file with the correct
device using vendor and device id looks somewhat fragile to me too.

Existing code in qemu+firmware (for example bootorder) uses the location
in the physical device tree to identify devices, like this:

/pci@i0cf8/pci-bridge@3/*@0/*@0/*@0,0
 ^^^^^^^^^                             pcie root bus
           ^^^^^^^^^^^^                pcie root port @ slot 3
                        ^^^            virtio-scsi-pci @ slot 0
                            ^^^        scsi controller bus #0
                                ^^^^^  scsi device target 0, lun 0

> > > * fixed-bar=on on a PCIe root port marks its subordinate hierarchy for
> > >   fixed BAR placement. Every device with memory BARs in that hierarchy
> > >   must provide a complete pci-bars= configuration.  
> > 
> > Why is this needed?
> 
> AIUI, the problem space is greatly expanded if we mix user provided
> fixed-bars with firmware assigned BARs and it's possible that there is
> no solution that meets the requirements.  This option both simplifies
> the problem space and allows the resource windows to be audited to
> generate user actionable errors in QEMU.

I can see that allowing fixed and non-fixed bars mix is much harder to
handle.  Do we need to ask the user to manually set that though?  I'd
prefer pci devices propagating automatically to the parent bus that they
have fixed bars and additional constrains apply.

Also: if the main use case for this is to map vfio devices with guest
physical address == host physical address, is there a need to specify
this manually at all?  Shouldn't we have a 'vfio-pci-fixed' device which
handles this automatically?

> > > * pci-bars=barN@<addr>[,barM@<addr>]... on a PCI endpoint specifies the
> > >   required address for each memory BAR. All memory BARs on the device
> > >   must have an explicitly assigned address.  
> > 
> > fixed-bar-<nr>=<addr> ?
> 
> Could be a reasonable alternative.

Parsing (and quoting) property strings with commas in the middle is a
PITA, also when using numerical properties you can use the 'size'
property type which accepts things like '16G'.

take care,
  Gerd



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [edk2-devel] [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement
  2026-08-28 15:49       ` Tushar Dave
@ 2026-08-31 13:42         ` Gerd Hoffmann
  2026-09-01 21:58           ` Tushar Dave
  0 siblings, 1 reply; 20+ messages in thread
From: Gerd Hoffmann @ 2026-08-31 13:42 UTC (permalink / raw)
  To: Tushar Dave
  Cc: Ard Biesheuvel, devel@edk2.groups.io, Alex Williamson,
	qemu-devel@nongnu.org, Jason Gunthorpe, Shameer Kolothum Thodi,
	qemu-arm@nongnu.org, peter.maydell@linaro.org, Michael S. Tsirkin,
	marcel.apfelbaum@gmail.com

  Hi,

> > Is this the reason we cannot rely on the Enhanced Allocation (EA)
> > capability here?
> 
> AFAICT, this was explored on RFC v1.

> EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL was suggested as the
> shorter path at the time, which is what this series uses.

Essentially we have *two* problems to solve here.  The first is how do
we get the fixed bar information from qemu to the firmware, and the
second is how we integrate that into edk2.

So we could have a driver which parses EA and passes along the
information found to PciDxe using
EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL.

Not sure how much of a win that would be compared to adding EA support
to PciDxe directly given that the PciDxe bridge window logic needs
enhancements to properly handle fixed bars (as discussed below).

> > Agreed - if the bridge windows are not programmed correctly on the first
> > pass, there is something in the code that needs to be fixed. I don't think
> > papering over it like this is the right approach.
> 
> That's a fair point. I did the window-sizing after PciBusDxe because I
> didn't want to touch existing PciBusDxe code too much.
> 
> As per my understanding, PciBusDxe's enumeration splits into three phases:
> 
> Phase 1 (PciHostBridgeEnumerator) walks the whole tree and calls
> CheckDevice() per device — by the time this phase finishes, every fixed
> BAR address is already known and cached on the device
> (PciBar[Bar].FixedBaseAddress).
> 
> Phase 2 (PciHostBridgeResourceAllocator) prepares an address for every
> resource node in the tree — both individual BARs and bridge windows
> alike — purely from size and alignment; PCI_RESOURCE_NODE has no address
> field at all, so this is entirely blind to whether a fixed address was
> already required.
> 
> Phase 3 (ProgramResource) then writes the actual PCI config-space
> registers, and by default it just writes whatever address Phase 2
> prepared, for both BARs and bridge windows. The one exception is
> ProgramBar() — it specifically checks whether that particular BAR was
> marked fixed back in Phase 1, and if so, overrides Phase 2's prepared
> base address with the real fixed one. However, ProgramPpbApperture(),
> which writes the bridge's own window registers, has no equivalent
> override — it always writes whatever Phase 2 prepared, with no awareness
> of a fixed BAR anywhere underneath it. And that needs fixing, and for
> that I have to change the existing code.
> 
> I think the fix would be to extend Phase 2's own sizing step
> (CalculateResourceAperture() in PciResourceSupport.c) to check for the
> already-known fixed address on each child and, when present, size and
> position the window as the exact union of those addresses instead of the
> blind size-only sum. Does that match the direction you had in mind, or
> is there a different integration point you'd suggest?

Sounds about right, when propagating resource requirements up from
devices to bridges looking only at the size is not enough if we want
properly support pci bars at fixed locations.

take care,
  Gerd



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [edk2-devel] [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement
  2026-08-31 13:42         ` Gerd Hoffmann
@ 2026-09-01 21:58           ` Tushar Dave
  0 siblings, 0 replies; 20+ messages in thread
From: Tushar Dave @ 2026-09-01 21:58 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: Ard Biesheuvel, devel@edk2.groups.io, Alex Williamson,
	qemu-devel@nongnu.org, Jason Gunthorpe, Shameer Kolothum Thodi,
	qemu-arm@nongnu.org, peter.maydell@linaro.org, Michael S. Tsirkin,
	marcel.apfelbaum@gmail.com



On 8/31/2026 8:42 AM, Gerd Hoffmann wrote:
>   Hi,
> 
>>> Is this the reason we cannot rely on the Enhanced Allocation (EA)
>>> capability here?
>>
>> AFAICT, this was explored on RFC v1.
> 
>> EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL was suggested as the
>> shorter path at the time, which is what this series uses.
> 
> Essentially we have *two* problems to solve here.  The first is how do
> we get the fixed bar information from qemu to the firmware, and the
> second is how we integrate that into edk2.
> 
> So we could have a driver which parses EA and passes along the
> information found to PciDxe using
> EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL.
> 
> Not sure how much of a win that would be compared to adding EA support
> to PciDxe directly given that the PciDxe bridge window logic needs
> enhancements to properly handle fixed bars (as discussed below).

Agreed, EA doesn't buy us much here; the bridge-window fix is needed
either way.

> 
>>> Agreed - if the bridge windows are not programmed correctly on the first
>>> pass, there is something in the code that needs to be fixed. I don't think
>>> papering over it like this is the right approach.
>>
>> That's a fair point. I did the window-sizing after PciBusDxe because I
>> didn't want to touch existing PciBusDxe code too much.
>>
>> As per my understanding, PciBusDxe's enumeration splits into three phases:
>>
>> Phase 1 (PciHostBridgeEnumerator) walks the whole tree and calls
>> CheckDevice() per device — by the time this phase finishes, every fixed
>> BAR address is already known and cached on the device
>> (PciBar[Bar].FixedBaseAddress).
>>
>> Phase 2 (PciHostBridgeResourceAllocator) prepares an address for every
>> resource node in the tree — both individual BARs and bridge windows
>> alike — purely from size and alignment; PCI_RESOURCE_NODE has no address
>> field at all, so this is entirely blind to whether a fixed address was
>> already required.
>>
>> Phase 3 (ProgramResource) then writes the actual PCI config-space
>> registers, and by default it just writes whatever address Phase 2
>> prepared, for both BARs and bridge windows. The one exception is
>> ProgramBar() — it specifically checks whether that particular BAR was
>> marked fixed back in Phase 1, and if so, overrides Phase 2's prepared
>> base address with the real fixed one. However, ProgramPpbApperture(),
>> which writes the bridge's own window registers, has no equivalent
>> override — it always writes whatever Phase 2 prepared, with no awareness
>> of a fixed BAR anywhere underneath it. And that needs fixing, and for
>> that I have to change the existing code.
>>
>> I think the fix would be to extend Phase 2's own sizing step
>> (CalculateResourceAperture() in PciResourceSupport.c) to check for the
>> already-known fixed address on each child and, when present, size and
>> position the window as the exact union of those addresses instead of the
>> blind size-only sum. Does that match the direction you had in mind, or
>> is there a different integration point you'd suggest?
> 
> Sounds about right, when propagating resource requirements up from
> devices to bridges looking only at the size is not enough if we want
> properly support pci bars at fixed locations.

Thanks for confirming the direction. I'll implement this and post it as
part of the next round.

> 
> take care,
>   Gerd


Thanks.
-Tushar



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement
  2026-08-31 13:24     ` Gerd Hoffmann
@ 2026-09-01 22:27       ` Tushar Dave
  2026-09-02  6:15         ` Gerd Hoffmann
  0 siblings, 1 reply; 20+ messages in thread
From: Tushar Dave @ 2026-09-01 22:27 UTC (permalink / raw)
  To: Gerd Hoffmann, Alex Williamson
  Cc: qemu-devel, jgg, skolothumtho, qemu-arm, peter.maydell, mst,
	marcel.apfelbaum, devel



On 8/31/2026 8:24 AM, Gerd Hoffmann wrote:
> On Thu, Aug 27, 2026 at 07:47:33AM -0600, Alex Williamson wrote:
>> On Thu, 27 Aug 2026 09:18:50 +0200
>> Gerd Hoffmann <kraxel@redhat.com> wrote:
>>
>>> I'd strongly recommend to do the same for the fixed bars:  Add a pci
>>> capability to pass that information.  All the logic you have today to
>>> link the information in the fw_cfg file to the correct pci device is
>>> simply not needed any more then.
>>
>> Placement of a VMM defined capability into a vfio-pci device is not
>> such a trivial problem as it is for emulated devices.  Space may not be
>> readily available and the capability may mask non-architected registers.
>>
>> Does this suggestion relate to fixing the gap between mapping fw_cfg
>> entries by vendor/device IDs or is there something fundamentally
>> undesirable about using fw_cfg here?
> 
> Well, fw_cfg is the fallback option if we don't have any better way.
> Attaching the information directly to the device by placing it in a
> pci capability is at very minimum worth exploring.  If this is not
> working for vfio devices, ok, we have to accept that I guess.
> 
> And, yes, the logic to match entries in the fw_cfg file with the correct
> device using vendor and device id looks somewhat fragile to me too.
> 
> Existing code in qemu+firmware (for example bootorder) uses the location
> in the physical device tree to identify devices, like this:
> 
> /pci@i0cf8/pci-bridge@3/*@0/*@0/*@0,0
>  ^^^^^^^^^                             pcie root bus
>            ^^^^^^^^^^^^                pcie root port @ slot 3
>                         ^^^            virtio-scsi-pci @ slot 0
>                             ^^^        scsi controller bus #0
>                                 ^^^^^  scsi device target 0, lun 0

Good point but the problem is CheckDevice()'s own signature, which is
fixed by UEFI PI spec (only passes
VendorId/DeviceId/RevisionId/SubsystemVendorId/SubsystemDeviceId). Even
though the path exists internally, the standard protocol interface
doesn't pass it to the callback. Therefore, we prepare the blob entries
in the same order PciBusDxe discovers devices, so matching by VID:DID
inherently works.

> 
>>>> * fixed-bar=on on a PCIe root port marks its subordinate hierarchy for
>>>>   fixed BAR placement. Every device with memory BARs in that hierarchy
>>>>   must provide a complete pci-bars= configuration.  
>>>
>>> Why is this needed?
>>
>> AIUI, the problem space is greatly expanded if we mix user provided
>> fixed-bars with firmware assigned BARs and it's possible that there is
>> no solution that meets the requirements.  This option both simplifies
>> the problem space and allows the resource windows to be audited to
>> generate user actionable errors in QEMU.
> 
> I can see that allowing fixed and non-fixed bars mix is much harder to
> handle.  Do we need to ask the user to manually set that though?  I'd
> prefer pci devices propagating automatically to the parent bus that they
> have fixed bars and additional constrains apply.

I looked at this again, and technically nothing actually needs the flag
to exist. The real reason I kept it is closer to a usability one; it's
meant to be a visible signal in the launch script itself, so anyone
reading or writing the qemu command line sees up front that every device
under that root port is expected to have pci-bars= configured, rather
than that requirement only surfacing as a runtime error if something's
missing.

I would be okay to drop it but that was the reasoning. Let me know.

> 
> Also: if the main use case for this is to map vfio devices with guest
> physical address == host physical address, is there a need to specify
> this manually at all?  Shouldn't we have a 'vfio-pci-fixed' device which
> handles this automatically?

VFIO GPA == HPA is the primary motivation, but I don't think fixed-bar
should be tied to VFIO or automatically derive guest addresses from the
host. For the VFIO use case, the admin can choose to specify the host
BAR addresses as the fixed-bar configuration to get GPA == HPA, but the
mechanism itself doesn't assume or enforce that -- the desired guest
layout isn't always just a copy of the host's, so having fixed-bar
auto-derive it on its own would be incorrect in some cases, not just
less general. The mechanism remains a generic way to explicitly specify
PCI BAR addresses.

> 
>>>> * pci-bars=barN@<addr>[,barM@<addr>]... on a PCI endpoint specifies the
>>>>   required address for each memory BAR. All memory BARs on the device
>>>>   must have an explicitly assigned address.  
>>>
>>> fixed-bar-<nr>=<addr> ?
>>
>> Could be a reasonable alternative.
> 
> Parsing (and quoting) property strings with commas in the middle is a
> PITA, also when using numerical properties you can use the 'size'
> property type which accepts things like '16G'.

Fair point.

-device some-device,pci-bars=bar0@0x1000000000,bar1@0x2000000000

would become:

-device some-device,fixed-bar-0=0x1000000000,fixed-bar-1=0x2000000000

and, with the 'size' property type, the same addresses could also be
expressed as:

-device some-device,fixed-bar-0=64G,fixed-bar-1=128G

> 
> take care,
>   Gerd

Thanks.
-Tushar



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement
  2026-09-01 22:27       ` Tushar Dave
@ 2026-09-02  6:15         ` Gerd Hoffmann
  2026-09-02 15:24           ` Alex Williamson
  2026-09-02 16:30           ` Tushar Dave
  0 siblings, 2 replies; 20+ messages in thread
From: Gerd Hoffmann @ 2026-09-02  6:15 UTC (permalink / raw)
  To: Tushar Dave
  Cc: Alex Williamson, qemu-devel, jgg, skolothumtho, qemu-arm,
	peter.maydell, mst, marcel.apfelbaum, devel

  Hi,

> > And, yes, the logic to match entries in the fw_cfg file with the correct
> > device using vendor and device id looks somewhat fragile to me too.
> > 
> > Existing code in qemu+firmware (for example bootorder) uses the location
> > in the physical device tree to identify devices, like this:
> > 
> > /pci@i0cf8/pci-bridge@3/*@0/*@0/*@0,0
> >  ^^^^^^^^^                             pcie root bus
> >            ^^^^^^^^^^^^                pcie root port @ slot 3
> >                         ^^^            virtio-scsi-pci @ slot 0
> >                             ^^^        scsi controller bus #0
> >                                 ^^^^^  scsi device target 0, lun 0
> 
> Good point but the problem is CheckDevice()'s own signature, which is
> fixed by UEFI PI spec (only passes
> VendorId/DeviceId/RevisionId/SubsystemVendorId/SubsystemDeviceId). Even
> though the path exists internally, the standard protocol interface
> doesn't pass it to the callback.

Hmm, yes.  Seems to be designed to apply quirks to device classes, not
individual devices.

Also note that OVMF already has an incompatible pci device driver and
there can be only one instance, so the code must be merged into the
existing driver instead of adding a second.

> Therefore, we prepare the blob entries
> in the same order PciBusDxe discovers devices, so matching by VID:DID
> inherently works.

Question is whenever we want have that edk2 limitation and the knowledge
about edk2 internals (pci scan order) encoded in the qemu <-> firmware
protocol.  I think it makes sense to (additionally) pass the complete
device path even if the current edk2 implementation doesn't use it, so
we have the option to improve things later on without having to change
the qemu <-> firmware protocolS for that.

> > I can see that allowing fixed and non-fixed bars mix is much harder to
> > handle.  Do we need to ask the user to manually set that though?  I'd
> > prefer pci devices propagating automatically to the parent bus that they
> > have fixed bars and additional constrains apply.
> 
> I looked at this again, and technically nothing actually needs the flag
> to exist. The real reason I kept it is closer to a usability one; it's
> meant to be a visible signal in the launch script itself, so anyone
> reading or writing the qemu command line sees up front that every device
> under that root port is expected to have pci-bars= configured, rather
> than that requirement only surfacing as a runtime error if something's
> missing.

I'm not sure how much of a usability win that actually is, if you forget
to set the flag you still get a runtime error.

In general I like things which can be done automatically actually happen
automatically as this simplifies things for the user in most cases.

> > Also: if the main use case for this is to map vfio devices with guest
> > physical address == host physical address, is there a need to specify
> > this manually at all?  Shouldn't we have a 'vfio-pci-fixed' device which
> > handles this automatically?
> 
> VFIO GPA == HPA is the primary motivation, but I don't think fixed-bar
> should be tied to VFIO or automatically derive guest addresses from the
> host.

Why not?  It is a great usability improvement IMHO.

> For the VFIO use case, the admin can choose to specify the host
> BAR addresses as the fixed-bar configuration to get GPA == HPA, but the
> mechanism itself doesn't assume or enforce that -- the desired guest
> layout isn't always just a copy of the host's, so having fixed-bar
> auto-derive it on its own would be incorrect in some cases, not just
> less general.

You still can have fixed-bar-<nr>=<addr> properties to override the
auto-discovered address for some or all pci bars.

> The mechanism remains a generic way to explicitly specify
> PCI BAR addresses.

Yes, the code which creates the fw_cfg files is generic and it makes
sense to have that in the core pci code, so it can be used for every pci
device.

Nevertheless I'd tend to only expose the properties for devices where an
actual use case exists.  Which is obviously vfio-pci(-fixed).  Also
pci-testdev for development / testing / CI.  I can't see much beyond
that though.

take care,
  Gerd



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement
  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
  1 sibling, 1 reply; 20+ messages in thread
From: Alex Williamson @ 2026-09-02 15:24 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: Tushar Dave, qemu-devel, jgg, skolothumtho, qemu-arm,
	peter.maydell, mst, marcel.apfelbaum, devel, alex

On Wed, 2 Sep 2026 08:15:30 +0200
Gerd Hoffmann <kraxel@redhat.com> wrote:

>   Hi,
> 
> > > And, yes, the logic to match entries in the fw_cfg file with the correct
> > > device using vendor and device id looks somewhat fragile to me too.
> > > 
> > > Existing code in qemu+firmware (for example bootorder) uses the location
> > > in the physical device tree to identify devices, like this:
> > > 
> > > /pci@i0cf8/pci-bridge@3/*@0/*@0/*@0,0
> > >  ^^^^^^^^^                             pcie root bus
> > >            ^^^^^^^^^^^^                pcie root port @ slot 3
> > >                         ^^^            virtio-scsi-pci @ slot 0
> > >                             ^^^        scsi controller bus #0
> > >                                 ^^^^^  scsi device target 0, lun 0  

Yeah, I wish such path-based device identification where available here.

> > 
> > Good point but the problem is CheckDevice()'s own signature, which is
> > fixed by UEFI PI spec (only passes
> > VendorId/DeviceId/RevisionId/SubsystemVendorId/SubsystemDeviceId). Even
> > though the path exists internally, the standard protocol interface
> > doesn't pass it to the callback.  
> 
> Hmm, yes.  Seems to be designed to apply quirks to device classes, not
> individual devices.
> 
> Also note that OVMF already has an incompatible pci device driver and
> there can be only one instance, so the code must be merged into the
> existing driver instead of adding a second.
> 
> > Therefore, we prepare the blob entries
> > in the same order PciBusDxe discovers devices, so matching by VID:DID
> > inherently works.  
> 
> Question is whenever we want have that edk2 limitation and the knowledge
> about edk2 internals (pci scan order) encoded in the qemu <-> firmware
> protocol.  I think it makes sense to (additionally) pass the complete
> device path even if the current edk2 implementation doesn't use it, so
> we have the option to improve things later on without having to change
> the qemu <-> firmware protocolS for that.
> 
> > > I can see that allowing fixed and non-fixed bars mix is much harder to
> > > handle.  Do we need to ask the user to manually set that though?  I'd
> > > prefer pci devices propagating automatically to the parent bus that they
> > > have fixed bars and additional constrains apply.  
> > 
> > I looked at this again, and technically nothing actually needs the flag
> > to exist. The real reason I kept it is closer to a usability one; it's
> > meant to be a visible signal in the launch script itself, so anyone
> > reading or writing the qemu command line sees up front that every device
> > under that root port is expected to have pci-bars= configured, rather
> > than that requirement only surfacing as a runtime error if something's
> > missing.  
> 
> I'm not sure how much of a usability win that actually is, if you forget
> to set the flag you still get a runtime error.
> 
> In general I like things which can be done automatically actually happen
> automatically as this simplifies things for the user in most cases.
> 
> > > Also: if the main use case for this is to map vfio devices with guest
> > > physical address == host physical address, is there a need to specify
> > > this manually at all?  Shouldn't we have a 'vfio-pci-fixed' device which
> > > handles this automatically?  
> > 
> > VFIO GPA == HPA is the primary motivation, but I don't think fixed-bar
> > should be tied to VFIO or automatically derive guest addresses from the
> > host.  
> 
> Why not?  It is a great usability improvement IMHO.

We thought about whether to make a vfio-pci shortcut to allow the HPA to
be pushed to the fixed BAR address, but decided that it's also easy for
a userspace script to to collect the physical BAR addresses and
construct the QEMU device options, while maintaining compatibility with
emulated devices and therefore enabling more comprehensive testing.  I
don't think we want to be limited by the physical devices available when
we're testing this.

> > For the VFIO use case, the admin can choose to specify the host
> > BAR addresses as the fixed-bar configuration to get GPA == HPA, but the
> > mechanism itself doesn't assume or enforce that -- the desired guest
> > layout isn't always just a copy of the host's, so having fixed-bar
> > auto-derive it on its own would be incorrect in some cases, not just
> > less general.  
> 
> You still can have fixed-bar-<nr>=<addr> properties to override the
> auto-discovered address for some or all pci bars.

If QEMU is willing to accept both a generic PCI mechanism to specify
this, AND a vfio-pci shortcut, sure, we can create the shortcut.  As
above though, it's also something the caller can construct relatively
easily (maybe not by hand, but with a trivial script) and increases the
test surface for QEMU.
 
> > The mechanism remains a generic way to explicitly specify
> > PCI BAR addresses.  
> 
> Yes, the code which creates the fw_cfg files is generic and it makes
> sense to have that in the core pci code, so it can be used for every pci
> device.
> 
> Nevertheless I'd tend to only expose the properties for devices where an
> actual use case exists.  Which is obviously vfio-pci(-fixed).  Also
> pci-testdev for development / testing / CI.  I can't see much beyond
> that though.

I always imagined the properties would live on the core PCI device and
at best vfio-pci would have a shortcut to prefill those properties
based on physical BAR address.  pci-testdev is pretty limited and we
can't fully test arbitrary device functionality with it.  We'd also
lose the ability to diverge from the host programming if we need to
debug a layout generated on another system.

IMO, the artificial restriction isn't worth it, especially in the
proposed environment where we enforce and validate fixed BAR
configurations for an entire PCI sub-tree.  I think that already
eliminates the most common usage failures we'd see otherwise.
Thanks,

Alex


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement
  2026-09-02  6:15         ` Gerd Hoffmann
  2026-09-02 15:24           ` Alex Williamson
@ 2026-09-02 16:30           ` Tushar Dave
  2026-09-03  9:47             ` Gerd Hoffmann
  1 sibling, 1 reply; 20+ messages in thread
From: Tushar Dave @ 2026-09-02 16:30 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: Alex Williamson, qemu-devel, jgg, skolothumtho, qemu-arm,
	peter.maydell, mst, marcel.apfelbaum, devel



On 9/2/2026 1:15 AM, Gerd Hoffmann wrote:
>   Hi,
> 
>>> And, yes, the logic to match entries in the fw_cfg file with the correct
>>> device using vendor and device id looks somewhat fragile to me too.
>>>
>>> Existing code in qemu+firmware (for example bootorder) uses the location
>>> in the physical device tree to identify devices, like this:
>>>
>>> /pci@i0cf8/pci-bridge@3/*@0/*@0/*@0,0
>>>  ^^^^^^^^^                             pcie root bus
>>>            ^^^^^^^^^^^^                pcie root port @ slot 3
>>>                         ^^^            virtio-scsi-pci @ slot 0
>>>                             ^^^        scsi controller bus #0
>>>                                 ^^^^^  scsi device target 0, lun 0
>>
>> Good point but the problem is CheckDevice()'s own signature, which is
>> fixed by UEFI PI spec (only passes
>> VendorId/DeviceId/RevisionId/SubsystemVendorId/SubsystemDeviceId). Even
>> though the path exists internally, the standard protocol interface
>> doesn't pass it to the callback.
> 
> Hmm, yes.  Seems to be designed to apply quirks to device classes, not
> individual devices.
> 
> Also note that OVMF already has an incompatible pci device driver and
> there can be only one instance, so the code must be merged into the
> existing driver instead of adding a second.

I checked OvmfPkg/IncompatiblePciDeviceSupportDxe -- its CheckDevice()
is unconditional, it returns the same 64-bit-MMIO-preference descriptor
for every device regardless of VendorId/DeviceId. Merging Fixed BAR
design in would make it a simple dispatch: if the device has an entry in
the fw_cfg blob we export, return our descriptor; otherwise fall through
to the existing behavior unchanged. Does that match what you had in
mind, or is there a different integration point you'd prefer?

> 
>> Therefore, we prepare the blob entries
>> in the same order PciBusDxe discovers devices, so matching by VID:DID
>> inherently works.
> 
> Question is whenever we want have that edk2 limitation and the knowledge
> about edk2 internals (pci scan order) encoded in the qemu <-> firmware
> protocol.  I think it makes sense to (additionally) pass the complete
> device path even if the current edk2 implementation doesn't use it, so
> we have the option to improve things later on without having to change
> the qemu <-> firmware protocolS for that.

I see your point. Sure thing, I'll add it.

> 
>>> I can see that allowing fixed and non-fixed bars mix is much harder to
>>> handle.  Do we need to ask the user to manually set that though?  I'd
>>> prefer pci devices propagating automatically to the parent bus that they
>>> have fixed bars and additional constrains apply.
>>
>> I looked at this again, and technically nothing actually needs the flag
>> to exist. The real reason I kept it is closer to a usability one; it's
>> meant to be a visible signal in the launch script itself, so anyone
>> reading or writing the qemu command line sees up front that every device
>> under that root port is expected to have pci-bars= configured, rather
>> than that requirement only surfacing as a runtime error if something's
>> missing.
> 
> I'm not sure how much of a usability win that actually is, if you forget
> to set the flag you still get a runtime error.

Fair point. I will drop 'fixed-bar=on' from RP property.

Thanks.
-Tushar
> 
> In general I like things which can be done automatically actually happen
> automatically as this simplifies things for the user in most cases.
> 
>>> Also: if the main use case for this is to map vfio devices with guest
>>> physical address == host physical address, is there a need to specify
>>> this manually at all?  Shouldn't we have a 'vfio-pci-fixed' device which
>>> handles this automatically?
>>
>> VFIO GPA == HPA is the primary motivation, but I don't think fixed-bar
>> should be tied to VFIO or automatically derive guest addresses from the
>> host.
> 
> Why not?  It is a great usability improvement IMHO.
> 
>> For the VFIO use case, the admin can choose to specify the host
>> BAR addresses as the fixed-bar configuration to get GPA == HPA, but the
>> mechanism itself doesn't assume or enforce that -- the desired guest
>> layout isn't always just a copy of the host's, so having fixed-bar
>> auto-derive it on its own would be incorrect in some cases, not just
>> less general.
> 
> You still can have fixed-bar-<nr>=<addr> properties to override the
> auto-discovered address for some or all pci bars.
> 
>> The mechanism remains a generic way to explicitly specify
>> PCI BAR addresses.
> 
> Yes, the code which creates the fw_cfg files is generic and it makes
> sense to have that in the core pci code, so it can be used for every pci
> device.
> 
> Nevertheless I'd tend to only expose the properties for devices where an
> actual use case exists.  Which is obviously vfio-pci(-fixed).  Also
> pci-testdev for development / testing / CI.  I can't see much beyond
> that though.
> 
> take care,
>   Gerd


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement
  2026-09-02 15:24           ` Alex Williamson
@ 2026-09-03  9:34             ` Gerd Hoffmann
  0 siblings, 0 replies; 20+ messages in thread
From: Gerd Hoffmann @ 2026-09-03  9:34 UTC (permalink / raw)
  To: Alex Williamson
  Cc: Tushar Dave, qemu-devel, jgg, skolothumtho, qemu-arm,
	peter.maydell, mst, marcel.apfelbaum, devel

  Hi,

> > You still can have fixed-bar-<nr>=<addr> properties to override the
> > auto-discovered address for some or all pci bars.
> 
> If QEMU is willing to accept both a generic PCI mechanism to specify
> this, AND a vfio-pci shortcut, sure, we can create the shortcut.

I think this makes sense, but at the end of the day I'm not the pci
maintainer, so this is not my call.

> As above though, it's also something the caller can construct
> relatively easily (maybe not by hand, but with a trivial script)

Then everybody who wants / needs this reinvents such a script.
Do we really want that?

> > Nevertheless I'd tend to only expose the properties for devices where an
> > actual use case exists.  Which is obviously vfio-pci(-fixed).  Also
> > pci-testdev for development / testing / CI.  I can't see much beyond
> > that though.
> 
> I always imagined the properties would live on the core PCI device and
> at best vfio-pci would have a shortcut to prefill those properties
> based on physical BAR address.  pci-testdev is pretty limited and we
> can't fully test arbitrary device functionality with it.  We'd also
> lose the ability to diverge from the host programming if we need to
> debug a layout generated on another system.
> 
> IMO, the artificial restriction isn't worth it, especially in the
> proposed environment where we enforce and validate fixed BAR
> configurations for an entire PCI sub-tree.  I think that already
> eliminates the most common usage failures we'd see otherwise.

Fair enough.

take care,
  Gerd



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement
  2026-09-02 16:30           ` Tushar Dave
@ 2026-09-03  9:47             ` Gerd Hoffmann
  2026-09-04 13:46               ` Tushar Dave
  0 siblings, 1 reply; 20+ messages in thread
From: Gerd Hoffmann @ 2026-09-03  9:47 UTC (permalink / raw)
  To: Tushar Dave
  Cc: Alex Williamson, qemu-devel, jgg, skolothumtho, qemu-arm,
	peter.maydell, mst, marcel.apfelbaum, devel

  Hi,

> > Also note that OVMF already has an incompatible pci device driver and
> > there can be only one instance, so the code must be merged into the
> > existing driver instead of adding a second.
> 
> I checked OvmfPkg/IncompatiblePciDeviceSupportDxe -- its CheckDevice()
> is unconditional, it returns the same 64-bit-MMIO-preference descriptor
> for every device regardless of VendorId/DeviceId. Merging Fixed BAR
> design in would make it a simple dispatch: if the device has an entry in
> the fw_cfg blob we export, return our descriptor; otherwise fall through
> to the existing behavior unchanged. Does that match what you had in
> mind, or is there a different integration point you'd prefer?

For the most part yes.

I'd suggest to keep the fixed-bars code in a separate source file,
then just add a small dispatch hook to the existing CheckDevice
function.  In case there is a fixed-bars entry use that instead of the
hardcoded template.

The option rom tweak for confidential VMs should be applied to the
fixed-bars entries too, so don't return early.

take care,
  Gerd



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 0/5] hw/pci, hw/arm/virt: fixed PCI BAR placement
  2026-09-03  9:47             ` Gerd Hoffmann
@ 2026-09-04 13:46               ` Tushar Dave
  0 siblings, 0 replies; 20+ messages in thread
From: Tushar Dave @ 2026-09-04 13:46 UTC (permalink / raw)
  To: Gerd Hoffmann
  Cc: Alex Williamson, qemu-devel, jgg, skolothumtho, qemu-arm,
	peter.maydell, mst, marcel.apfelbaum, devel



On 9/3/2026 4:47 AM, Gerd Hoffmann wrote:
>   Hi,
> 
>>> Also note that OVMF already has an incompatible pci device driver and
>>> there can be only one instance, so the code must be merged into the
>>> existing driver instead of adding a second.
>>
>> I checked OvmfPkg/IncompatiblePciDeviceSupportDxe -- its CheckDevice()
>> is unconditional, it returns the same 64-bit-MMIO-preference descriptor
>> for every device regardless of VendorId/DeviceId. Merging Fixed BAR
>> design in would make it a simple dispatch: if the device has an entry in
>> the fw_cfg blob we export, return our descriptor; otherwise fall through
>> to the existing behavior unchanged. Does that match what you had in
>> mind, or is there a different integration point you'd prefer?
> 
> For the most part yes.
> 
> I'd suggest to keep the fixed-bars code in a separate source file,
> then just add a small dispatch hook to the existing CheckDevice
> function.  In case there is a fixed-bars entry use that instead of the
> hardcoded template.
> 
> The option rom tweak for confidential VMs should be applied to the
> fixed-bars entries too, so don't return early.

Okay, thanks. I will incorporate all the changes and send the non-RFC
patch series.

Thanks.
-Tushar
> 
> take care,
>   Gerd



^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2026-09-04 13:46 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [RFC PATCH v2 2/5] pci: add validation for fixed BAR configuration Tushar Dave
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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox