Introduce a per-device fixed-bars property that allows users to provide
fixed PCI BAR addresses for PCI endpoint devices.

The fixed-bars property cannot be supported on hot-plugged PCI devices.
PCI BARs for the hot-plugged device are programmed by the guest at the
time the device appears.

Property format:
- Comma-separated list of BAR entries, each as:
  barN@<addr>[,barM@<addr>]*

- Example:
  -device vfio-pci,...,fixed-bars=bar2@0x6b8000000000

- Multiple BARs:
  -device vfio-pci,host=...,id=dev0
  -set dev0.fixed-bars=bar0@0x400000000000,bar4@0x410000000000

Signed-off-by: Tushar Dave <[email protected]>
---
 hw/pci/pci.c                | 108 ++++++++++++++++++++++++++++++++++++
 include/hw/pci/pci_device.h |  10 ++++
 2 files changed, 118 insertions(+)

diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 2c3657d00d..054fc2c0fa 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"
 
@@ -81,6 +82,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("fixed-bars", PCIDevice, fixed_bars),
     DEFINE_PROP_BIT("multifunction", PCIDevice, cap_present,
                     QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false),
     DEFINE_PROP_BIT("x-pcie-lnksta-dllla", PCIDevice, cap_present,
@@ -218,6 +220,103 @@ static void pci_bus_unrealize(BusState *qbus)
     vmstate_unregister(NULL, &vmstate_pcibus, bus);
 }
 
+#define FIXED_BARS_ERR "fixed-bars: 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, FIXED_BARS_ERR "invalid BAR '%s', expected 
bar0..bar5", tok);
+        return -1;
+    }
+    return v - OFF_AUTO_PCIBAR_BAR0;
+}
+
+/*
+ * Parse fixed-bars=barN@<addr>[,barM@<addr>]*
+ * BAR type, size, and alignment validation is deferred to the allocator,
+ * which has the full device context needed to perform those checks.
+ * On error, sets *@errp.
+ */
+static void pci_parse_fixed_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;
+    int index;
+    int i, ret;
+
+    if (!pci_dev->fixed_bars || !*pci_dev->fixed_bars) {
+        return;
+    }
+    if (DEVICE(pci_dev)->hotplugged) {
+        error_setg(&local_err,
+                   "fixed-bars is not supported on hot-plugged PCI devices");
+        goto out;
+    }
+
+    entries = g_strsplit(pci_dev->fixed_bars, ",", -1);
+    for (e = entries; e && *e; e++) {
+        const char *entry = g_strstrip(*e);
+        if (*entry == '\0') {
+            error_setg(&local_err, FIXED_BARS_ERR "empty field in list");
+            goto out;
+        }
+
+        parts = g_strsplit(entry, "@", 2);
+        if (!parts[0] || !parts[1]) {
+            error_setg(&local_err, FIXED_BARS_ERR "not '%s'", entry);
+            goto out;
+        }
+
+        index = pci_parse_bar_token(parts[0], &local_err);
+        if (index < 0) {
+            goto out;
+        }
+
+        ret = qemu_strtou64(parts[1], &endp, 0, &bar_addr);
+        if (ret) {
+            error_setg(&local_err, FIXED_BARS_ERR "unparseable address in 
'%s'",
+                       entry);
+            goto out;
+        }
+        if (*endp != '\0') {
+            error_setg(&local_err, FIXED_BARS_ERR "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, FIXED_BARS_ERR "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)) {
@@ -1473,6 +1572,8 @@ static void pci_qdev_unrealize(DeviceState *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);
     }
@@ -2369,6 +2470,13 @@ static void pci_qdev_realize(DeviceState *qdev, Error 
**errp)
         is_default_rom = true;
     }
 
+    pci_parse_fixed_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..3e46876985 100644
--- a/include/hw/pci/pci_device.h
+++ b/include/hw/pci/pci_device.h
@@ -179,6 +179,16 @@ struct PCIDevice {
     char *failover_pair_id;
     uint32_t acpi_index;
 
+    /*
+     * When fixed-bars property is in use, fixed_bar_addrs is non-NULL
+     * and has PCI_NUM_REGIONS - 1 elements (bar0..bar5); each slot is
+     * either PCI_BAR_UNMAPPED (no fixed address for that BAR) or the
+     * fixed address for that BAR.  NULL if the property is unused/empty
+     * or the map is not yet allocated.
+     */
+    char *fixed_bars;
+    pcibus_t *fixed_bar_addrs;
+
     /*
      * Indirect DMA region bounce buffer size as configured for the device. 
This
      * is a configuration parameter that is reflected into bus_master_as when
-- 
2.34.1



-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#121917): https://edk2.groups.io/g/devel/message/121917
Mute This Topic: https://groups.io/mt/119221704/21656
Group Owner: [email protected]
Unsubscribe: https://edk2.groups.io/g/devel/unsub [[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-


Reply via email to