Add fixed-bar-N properties to PCIDevice to allow the guest address
of an individual PCI BAR to be explicitly specified. The properties
are available for PCI BARs 0 through 5 and accept QEMU size
values, for example fixed-bar-0=0x40000000 or fixed-bar-0=64G.

e.g.
-device some-device,fixed-bar-0=<addr>[,fixed-bar-1=<addr>]...

During realize, validate that each specified BAR exists and is a
memory BAR, and reject fixed BAR configuration for hot-plugged
devices.

The fixed BAR properties are only supported on machine types that
implement fixed BAR placement. Add the `pci_fixed_bar_supported`
MachineClass flag to advertise this support, and reject fixed BAR
properties on machine types that do not set it.

Signed-off-by: Tushar Dave <[email protected]>
---
 hw/pci/pci.c                | 71 +++++++++++++++++++++++++++++++++++++
 include/hw/core/boards.h    |  3 ++
 include/hw/pci/pci_device.h |  7 ++++
 3 files changed, 81 insertions(+)

diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 2d2fe02c68..e9ae0d5648 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -89,6 +89,18 @@ 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_SIZE("fixed-bar-0", PCIDevice, fixed_bar_addr[0],
+                     PCI_BAR_UNMAPPED),
+    DEFINE_PROP_SIZE("fixed-bar-1", PCIDevice, fixed_bar_addr[1],
+                     PCI_BAR_UNMAPPED),
+    DEFINE_PROP_SIZE("fixed-bar-2", PCIDevice, fixed_bar_addr[2],
+                     PCI_BAR_UNMAPPED),
+    DEFINE_PROP_SIZE("fixed-bar-3", PCIDevice, fixed_bar_addr[3],
+                     PCI_BAR_UNMAPPED),
+    DEFINE_PROP_SIZE("fixed-bar-4", PCIDevice, fixed_bar_addr[4],
+                     PCI_BAR_UNMAPPED),
+    DEFINE_PROP_SIZE("fixed-bar-5", PCIDevice, fixed_bar_addr[5],
+                     PCI_BAR_UNMAPPED),
     DEFINE_PROP_BIT("multifunction", PCIDevice, cap_present,
                     QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false),
     DEFINE_PROP_BIT("x-pcie-lnksta-dllla", PCIDevice, cap_present,
@@ -228,6 +240,58 @@ static void pci_bus_unrealize(BusState *qbus)
     vmstate_unregister(NULL, &vmstate_pcibus, bus);
 }
 
+static bool pci_dev_has_fixed_bar(PCIDevice *pci_dev)
+{
+    int i;
+
+    for (i = 0; i < PCI_NUM_REGIONS - 1; i++) {
+        if (pci_dev->fixed_bar_addr[i] != PCI_BAR_UNMAPPED) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static void pci_check_fixed_bars(PCIDevice *pci_dev, Error **errp)
+{
+    PCIIORegion *r;
+    int i;
+
+    if (!pci_dev_has_fixed_bar(pci_dev)) {
+        return;
+    }
+
+    if (!MACHINE_GET_CLASS(qdev_get_machine())->pci_fixed_bar_supported) {
+        error_setg(errp, "fixed BAR placement is not supported by this "
+                   "machine type");
+        return;
+    }
+
+    if (DEVICE(pci_dev)->hotplugged) {
+        error_setg(errp, "fixed BAR placement is not supported on "
+                   "hot-plugged devices");
+        return;
+    }
+
+    for (i = 0; i < PCI_NUM_REGIONS - 1; i++) {
+        if (pci_dev->fixed_bar_addr[i] == PCI_BAR_UNMAPPED) {
+            continue;
+        }
+
+        r = &pci_dev->io_regions[i];
+        if (!r->size) {
+            error_setg(errp, "fixed-bar-%d: bar%d does not exist on %s",
+                       i, i, pci_dev->name);
+            return;
+        }
+        if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
+            error_setg(errp, "fixed-bar-%d: bar%d on %s is an I/O BAR, "
+                       "not a memory BAR", i, i, pci_dev->name);
+            return;
+        }
+    }
+}
+
 static int pcibus_num(PCIBus *bus)
 {
     if (pci_bus_is_root(bus)) {
@@ -2399,6 +2463,13 @@ static void pci_qdev_realize(DeviceState *qdev, Error 
**errp)
         is_default_rom = true;
     }
 
+    pci_check_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/core/boards.h b/include/hw/core/boards.h
index d0e4ad9820..8092634eb3 100644
--- a/include/hw/core/boards.h
+++ b/include/hw/core/boards.h
@@ -242,6 +242,8 @@ typedef struct {
  *    is not needed.
  * @numa_mem_supported:
  *    true if '--numa node.mem' option is supported and false otherwise
+ * @pci_fixed_bar_supported:
+ *    true if the machine supports fixed PCI BAR placement and false otherwise
  * @hotplug_allowed:
  *    If the hook is provided, then it'll be called for each device
  *    hotplug to check whether the device hotplug is allowed.  Return
@@ -317,6 +319,7 @@ struct MachineClass {
     bool ignore_boot_device_suffixes;
     bool nvdimm_supported;
     bool numa_mem_supported;
+    bool pci_fixed_bar_supported;
     bool auto_enable_numa;
     bool cpu_cluster_has_numa_boundary;
     SMPCompatProps smp_props;
diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h
index 5cac6e1688..db4a003ae3 100644
--- a/include/hw/pci/pci_device.h
+++ b/include/hw/pci/pci_device.h
@@ -187,6 +187,13 @@ struct PCIDevice {
     uint32_t max_bounce_buffer_size;
 
     char *sriov_pf;
+
+    /*
+     * fixed_bar_addr[N] holds the guest address configured via the
+     * fixed-bar-N property for memory BAR N. PCI_BAR_UNMAPPED means
+     * the BAR's address is not fixed.
+     */
+    uint64_t fixed_bar_addr[PCI_NUM_REGIONS - 1];
 };
 
 static inline int pci_intx(PCIDevice *pci_dev)
-- 
2.34.1


Reply via email to