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 <[email protected]>
---
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