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]] -=-=-=-=-=-=-=-=-=-=-=-
