* [PATCH v4 1/5] vpci: Export functions required for SR-IOV support
2026-10-15 14:08 [PATCH v4 0/5] Implement SR-IOV support for PVH Mykyta Poturai
@ 2026-10-15 14:08 ` Mykyta Poturai
2026-10-15 14:08 ` [PATCH v4 2/5] vpci: use a local variable for the BAR rangeset Mykyta Poturai
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Mykyta Poturai @ 2026-10-15 14:08 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Stewart Hildebrand, Roger Pau Monné, Mykyta Poturai,
Teddy Astie
From: Stewart Hildebrand <stewart.hildebrand@amd.com>
Rename and export vpci_modify_bars, vpci_guest_mem_bar_{read,write}
Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com>
Signed-off-by: Mykyta Poturai <mykyta_poturai@epam.com>
Reviewed-by: Teddy Astie <teddy.astie@vates.tech>
---
v3->v4:
* squash "vpci: rename and export vpci_guest_mem_bar_{read,write}" with
"vpci: rename and export vpci_modify_bars"
v2->v3:
* move declaration to private.h
v1->v2
* Collect RBs
---
| 36 ++++++++++++++++++++----------------
xen/drivers/vpci/private.h | 9 +++++++++
2 files changed, 29 insertions(+), 16 deletions(-)
--git a/xen/drivers/vpci/header.c b/xen/drivers/vpci/header.c
index 7e5f4b8a00..a4e575be4e 100644
--- a/xen/drivers/vpci/header.c
+++ b/xen/drivers/vpci/header.c
@@ -305,7 +305,7 @@ static void defer_map(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
raise_softirq(SCHEDULE_SOFTIRQ);
}
-static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
+int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
{
struct vpci_header *header = &pdev->vpci->header;
struct pci_dev *tmp;
@@ -546,7 +546,7 @@ static void cf_check cmd_write(
* memory decoding bit has not been changed, so leave everything as-is,
* hoping the guest will realize and try again.
*/
- modify_bars(pdev, cmd, false);
+ vpci_modify_bars(pdev, cmd, false);
else
pci_conf_write16(pdev->sbdf, reg, cmd);
}
@@ -611,9 +611,9 @@ static void cf_check bar_write(
pci_conf_write32(pdev->sbdf, reg, val);
}
-static void cf_check guest_mem_bar_write(const struct pci_dev *pdev,
- unsigned int reg, uint32_t val,
- void *data)
+void cf_check vpci_guest_mem_bar_write(const struct pci_dev *pdev,
+ unsigned int reg, uint32_t val,
+ void *data)
{
struct vpci_bar *bar = data;
bool hi = false;
@@ -653,8 +653,8 @@ static void cf_check guest_mem_bar_write(const struct pci_dev *pdev,
bar->guest_addr = guest_addr;
}
-static uint32_t cf_check guest_mem_bar_read(const struct pci_dev *pdev,
- unsigned int reg, void *data)
+uint32_t cf_check vpci_guest_mem_bar_read(const struct pci_dev *pdev,
+ unsigned int reg, void *data)
{
const struct vpci_bar *bar = data;
uint32_t reg_val;
@@ -714,13 +714,15 @@ static void cf_check rom_write(
* Pass PCI_COMMAND_MEMORY or 0 to signal a map/unmap request, note that
* this fabricated command is never going to be written to the register.
*/
- else if ( modify_bars(pdev, new_enabled ? PCI_COMMAND_MEMORY : 0, true) )
+ else if ( vpci_modify_bars(pdev,
+ new_enabled ? PCI_COMMAND_MEMORY : 0,
+ true) )
/*
* No memory has been added or removed from the p2m (because the actual
* p2m changes are deferred in defer_map) and the ROM enable bit has
* not been changed, so leave everything as-is, hoping the guest will
* realize and try again. It's important to not update rom->addr in the
- * unmap case if modify_bars has failed, or future attempts would
+ * unmap case if vpci_modify_bars has failed, or future attempts would
* attempt to unmap the wrong address.
*/
return;
@@ -800,8 +802,8 @@ int vpci_init_header(struct pci_dev *pdev)
/*
* For DomUs, clear PCI_COMMAND_{MASTER,MEMORY,IO} and other
* DomU-controllable bits in PCI_COMMAND. Devices assigned to DomUs will
- * start with memory decoding disabled, and modify_bars() will not be called
- * at the end of this function.
+ * start with memory decoding disabled, and vpci_modify_bars() will not be
+ * called at the end of this function.
*/
if ( !is_hwdom )
cmd &= ~(PCI_COMMAND_VGA_PALETTE | PCI_COMMAND_INVALIDATE |
@@ -824,8 +826,9 @@ int vpci_init_header(struct pci_dev *pdev)
bars[i].type = VPCI_BAR_MEM64_HI;
rc = vpci_add_register(pdev->vpci,
is_hwdom ? vpci_hw_read32
- : guest_mem_bar_read,
- is_hwdom ? bar_write : guest_mem_bar_write,
+ : vpci_guest_mem_bar_read,
+ is_hwdom ? bar_write
+ : vpci_guest_mem_bar_write,
reg, 4, &bars[i]);
if ( rc )
goto fail;
@@ -883,8 +886,9 @@ int vpci_init_header(struct pci_dev *pdev)
bars[i].prefetchable = val & PCI_BASE_ADDRESS_MEM_PREFETCH;
rc = vpci_add_register(pdev->vpci,
- is_hwdom ? vpci_hw_read32 : guest_mem_bar_read,
- is_hwdom ? bar_write : guest_mem_bar_write,
+ is_hwdom ? vpci_hw_read32
+ : vpci_guest_mem_bar_read,
+ is_hwdom ? bar_write : vpci_guest_mem_bar_write,
reg, 4, &bars[i]);
if ( rc )
goto fail;
@@ -926,7 +930,7 @@ int vpci_init_header(struct pci_dev *pdev)
goto fail;
}
- return (cmd & PCI_COMMAND_MEMORY) ? modify_bars(pdev, cmd, false) : 0;
+ return (cmd & PCI_COMMAND_MEMORY) ? vpci_modify_bars(pdev, cmd, false) : 0;
fail:
pci_conf_write16(pdev->sbdf, PCI_COMMAND, cmd);
diff --git a/xen/drivers/vpci/private.h b/xen/drivers/vpci/private.h
index 2907f6b40f..f012fd160d 100644
--- a/xen/drivers/vpci/private.h
+++ b/xen/drivers/vpci/private.h
@@ -138,6 +138,15 @@ static inline size_t vmsix_table_size(const struct vpci *vpci, unsigned int nr)
8), 8);
}
+/* Map/unmap the BARs of a vPCI device. */
+int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only);
+
+void cf_check vpci_guest_mem_bar_write(const struct pci_dev *pdev,
+ unsigned int reg, uint32_t val,
+ void *data);
+
+uint32_t cf_check vpci_guest_mem_bar_read(const struct pci_dev *pdev,
+ unsigned int reg, void *data);
#endif /* __XEN__ */
#endif /* VPCI_PRIVATE_H */
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH v4 2/5] vpci: use a local variable for the BAR rangeset
2026-10-15 14:08 [PATCH v4 0/5] Implement SR-IOV support for PVH Mykyta Poturai
2026-10-15 14:08 ` [PATCH v4 1/5] vpci: Export functions required for SR-IOV support Mykyta Poturai
@ 2026-10-15 14:08 ` Mykyta Poturai
2026-10-15 14:08 ` [PATCH v4 4/5] vpci: add SR-IOV support for PVH Dom0 Mykyta Poturai
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Mykyta Poturai @ 2026-10-15 14:08 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Mykyta Poturai, Roger Pau Monné, Stewart Hildebrand,
Mykyta Poturai
From: Mykyta Poturai <Mykyta_Poturai@epam.com>
In the BAR (un)mapping paths latch the rangeset of the BAR being
processed into a local variable, instead of dereferencing struct
vpci_bar on every use. Preparatory work for moving the rangesets out
of struct vpci_bar.
No functional change.
Signed-off-by: Mykyta Poturai <mykyta_poturai@epam.com>
Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com>
---
v3->v4:
* new patch, split out of "vpci: Use pervcpu ranges for BAR mapping" so
that the non-functional churn is separate from the functional change
---
| 41 +++++++++++++++++++++------------------
1 file changed, 22 insertions(+), 19 deletions(-)
--git a/xen/drivers/vpci/header.c b/xen/drivers/vpci/header.c
index a4e575be4e..23412bcafa 100644
--- a/xen/drivers/vpci/header.c
+++ b/xen/drivers/vpci/header.c
@@ -196,6 +196,7 @@ bool vpci_process_pending(struct vcpu *v)
for ( i = 0; i < ARRAY_SIZE(header->bars); i++ )
{
struct vpci_bar *bar = &header->bars[i];
+ struct rangeset *mem = bar->mem;
struct map_data data = {
.d = v->domain,
.map = v->vpci.cmd & PCI_COMMAND_MEMORY,
@@ -203,10 +204,10 @@ bool vpci_process_pending(struct vcpu *v)
};
int rc;
- if ( rangeset_is_empty(bar->mem) )
+ if ( rangeset_is_empty(mem) )
continue;
- rc = rangeset_consume_ranges(bar->mem, map_range, &data);
+ rc = rangeset_consume_ranges(mem, map_range, &data);
if ( rc == -ERESTART )
{
@@ -218,8 +219,7 @@ bool vpci_process_pending(struct vcpu *v)
{
spin_lock(&pdev->vpci->lock);
/* Disable memory decoding unconditionally on failure. */
- modify_decoding(pdev, v->vpci.cmd & ~PCI_COMMAND_MEMORY,
- false);
+ modify_decoding(pdev, v->vpci.cmd & ~PCI_COMMAND_MEMORY, false);
spin_unlock(&pdev->vpci->lock);
/* Clean all the rangesets */
@@ -260,13 +260,14 @@ static int __init apply_map(struct domain *d, const struct pci_dev *pdev,
for ( i = 0; i < ARRAY_SIZE(header->bars); i++ )
{
struct vpci_bar *bar = &header->bars[i];
+ struct rangeset *mem = bar->mem;
struct map_data data = { .d = d, .map = true, .bar = bar };
- if ( rangeset_is_empty(bar->mem) )
+ if ( rangeset_is_empty(mem) )
continue;
- while ( (rc = rangeset_consume_ranges(bar->mem, map_range,
- &data)) == -ERESTART )
+ while ( (rc = rangeset_consume_ranges(mem, map_range, &data)) ==
+ -ERESTART )
{
/*
* It's safe to drop and reacquire the lock in this context
@@ -331,12 +332,13 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
for ( i = 0; i < ARRAY_SIZE(header->bars); i++ )
{
struct vpci_bar *bar = &header->bars[i];
+ struct rangeset *mem = bar->mem;
unsigned long start = PFN_DOWN(bar->addr);
unsigned long end = PFN_DOWN(bar->addr + bar->size - 1);
unsigned long start_guest = PFN_DOWN(bar->guest_addr);
unsigned long end_guest = PFN_DOWN(bar->guest_addr + bar->size - 1);
- if ( !bar->mem )
+ if ( !mem )
continue;
if ( !MAPPABLE_BAR(bar) ||
@@ -354,7 +356,7 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
continue;
}
- ASSERT(rangeset_is_empty(bar->mem));
+ ASSERT(rangeset_is_empty(mem));
/*
* Make sure that the guest set address has the same page offset
@@ -369,7 +371,7 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
return -EINVAL;
}
- rc = rangeset_add_range(bar->mem, start_guest, end_guest);
+ rc = rangeset_add_range(mem, start_guest, end_guest);
if ( rc )
{
printk(XENLOG_G_WARNING "Failed to add [%lx, %lx]: %d\n",
@@ -380,12 +382,12 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
/* Check for overlap with the already setup BAR ranges. */
for ( j = 0; j < i; j++ )
{
- struct vpci_bar *prev_bar = &header->bars[j];
+ struct rangeset *prev_mem = header->bars[j].mem;
- if ( rangeset_is_empty(prev_bar->mem) )
+ if ( rangeset_is_empty(prev_mem) )
continue;
- rc = rangeset_remove_range(prev_bar->mem, start_guest, end_guest);
+ rc = rangeset_remove_range(prev_mem, start_guest, end_guest);
if ( rc )
{
gprintk(XENLOG_WARNING,
@@ -395,7 +397,7 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
}
}
- rc = pci_sanitize_bar_memory(bar->mem);
+ rc = pci_sanitize_bar_memory(mem);
if ( rc )
{
gprintk(XENLOG_WARNING,
@@ -414,12 +416,12 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
for ( j = 0; j < ARRAY_SIZE(header->bars); j++ )
{
- const struct vpci_bar *bar = &header->bars[j];
+ struct rangeset *mem = header->bars[j].mem;
- if ( rangeset_is_empty(bar->mem) )
+ if ( rangeset_is_empty(mem) )
continue;
- rc = rangeset_remove_range(bar->mem, start, end);
+ rc = rangeset_remove_range(mem, start, end);
if ( rc )
{
gprintk(XENLOG_WARNING,
@@ -469,8 +471,9 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
for ( j = 0; j < ARRAY_SIZE(header->bars); j++)
{
const struct vpci_bar *bar = &header->bars[j];
+ struct rangeset *mem = bar->mem;
- if ( !rangeset_overlaps_range(bar->mem, start, end) ||
+ if ( !rangeset_overlaps_range(mem, start, end) ||
/*
* If only the ROM enable bit is toggled check against
* other BARs in the same device for overlaps, but not
@@ -481,7 +484,7 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
bar->type == VPCI_BAR_ROM) )
continue;
- rc = rangeset_remove_range(bar->mem, start, end);
+ rc = rangeset_remove_range(mem, start, end);
if ( rc )
{
gprintk(XENLOG_WARNING,
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH v4 4/5] vpci: add SR-IOV support for PVH Dom0
2026-10-15 14:08 [PATCH v4 0/5] Implement SR-IOV support for PVH Mykyta Poturai
2026-10-15 14:08 ` [PATCH v4 1/5] vpci: Export functions required for SR-IOV support Mykyta Poturai
2026-10-15 14:08 ` [PATCH v4 2/5] vpci: use a local variable for the BAR rangeset Mykyta Poturai
@ 2026-10-15 14:08 ` Mykyta Poturai
2026-10-15 14:08 ` [PATCH v4 5/5] vpci: add SR-IOV support for DomUs Mykyta Poturai
2026-10-15 14:08 ` [PATCH v4 3/5] vpci: allow queueing of mapping operations Mykyta Poturai
4 siblings, 0 replies; 6+ messages in thread
From: Mykyta Poturai @ 2026-10-15 14:08 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Stewart Hildebrand, Oleksii Kurochko, Community Manager,
Andrew Cooper, Anthony PERARD, Michal Orzel, Jan Beulich,
Julien Grall, Roger Pau Monné, Stefano Stabellini,
Roger Pau Monné, Daniel P. Smith, Roger Pau Monné,
Mykyta Poturai
From: Stewart Hildebrand <stewart.hildebrand@amd.com>
This code is expected to only be used by privileged domains,
unprivileged domains should not get access to the SR-IOV capability.
Implement a write handler for the SR-IOV control register, so Xen can
discover the VFs a PF enables and map their BARs. VF BARs live in the
PF's capability and are re-sized on every VF Enable, as the System Page
Size register can change them. They decode only while both VF Enable
and VF MSE are set.
Xen adds the VFs itself: on VF Enable their routing IDs are recorded
and config space accesses to them terminated for 100ms, as the spec
permits; the first access past that adds the VF. Clearing VF Enable
removes them again.
Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com>
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Signed-off-by: Mykyta Poturai <mykyta_poturai@epam.com>
---
v3->v4:
* add VFs in Xen instead of requiring the hardware domain to report
them
* drop the emulated command register read for VFs; PCI_COMMAND_MEMORY
is read-only 0 on a VF and must not control mapping
* require VF Enable in addition to MSE before mapping
* cache the SR-IOV capability position at init
* address review comments on vf_init_bars(), init_sriov(),
cleanup_sriov() and size_vf_bars()
* change SOB order
v2->v3:
* rework adding VFs with multiple mapping operations support
* switch back to no longer adding VFs withing Xen
* use private.h
* style fixes
* fixup cleanup_sriov
* emulate command register read for VFs
v1->v2:
* switch to VF discovery by Xen
* fix sequential vpci_modify_bars calls
* move documentation changes to a separate commit
---
CHANGELOG.md | 1 +
SUPPORT.md | 2 -
tools/tests/vpci/emul.h | 4 +
xen/drivers/passthrough/pci.c | 44 +++
xen/drivers/pci/physdev.c | 16 +-
xen/drivers/vpci/Makefile | 1 +
| 10 +-
xen/drivers/vpci/private.h | 15 +
xen/drivers/vpci/sriov.c | 507 ++++++++++++++++++++++++++++++++++
xen/drivers/vpci/vpci.c | 14 +
xen/include/xen/pci.h | 3 +
xen/include/xen/vpci.h | 24 +-
12 files changed, 629 insertions(+), 12 deletions(-)
create mode 100644 xen/drivers/vpci/sriov.c
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a2dd01037b..69f3175014 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
afterwards.
### Added
+ - PCI SR-IOV is supported for PVH dom0.
### Removed
- On x86:
diff --git a/SUPPORT.md b/SUPPORT.md
index eb07332462..62c9fd54e8 100644
--- a/SUPPORT.md
+++ b/SUPPORT.md
@@ -170,8 +170,6 @@ unexpected behavior or issues on some hardware.
At least the following features are missing on a PVH dom0:
- * PCI SR-IOV.
-
* Native NMI forwarding (nmi=dom0 command line option).
* MCE handling.
diff --git a/tools/tests/vpci/emul.h b/tools/tests/vpci/emul.h
index 38211afbb8..01770dc851 100644
--- a/tools/tests/vpci/emul.h
+++ b/tools/tests/vpci/emul.h
@@ -101,6 +101,10 @@ typedef union {
#define pci_get_pdev(...) (&test_pdev)
#define pci_get_ro_map(...) NULL
+#define pci_vf_setup_pending(...) false
+#define vpci_sriov_add_vf(...) false
+#define vpci_sriov_drop_vfs(...)
+
#define test_bit(...) false
/* Dummy native helpers. Writes are ignored, reads return 1's. */
diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c
index 578472d068..63d207b5d6 100644
--- a/xen/drivers/passthrough/pci.c
+++ b/xen/drivers/passthrough/pci.c
@@ -32,6 +32,7 @@
#include <xen/softirq.h>
#include <xen/tasklet.h>
#include <xen/vpci.h>
+#include <xen/xvmalloc.h>
#include <xen/msi.h>
#include <xsm/xsm.h>
#include "ats.h"
@@ -40,6 +41,8 @@ struct pci_seg {
struct list_head alldevs_list;
u16 nr;
unsigned long *ro_map;
+ /* BDFs of VFs which have been enabled but which Xen has not added yet. */
+ unsigned long *vf_setup_map;
/* bus2bridge_lock protects bus2bridge array */
spinlock_t bus2bridge_lock;
#define MAX_BUSES 256
@@ -139,6 +142,47 @@ const unsigned long *pci_get_ro_map(u16 seg)
return pseg ? pseg->ro_map : NULL;
}
+/*
+ * Marking is serialised by the hardware domain's pci_lock, which the SR-IOV
+ * control register write handler holds; unmarking only clears a bit.
+ */
+int pci_vf_setup_begin(pci_sbdf_t sbdf)
+{
+ struct pci_seg *pseg = get_pseg(sbdf.seg);
+
+ if ( !pseg )
+ return -ENODEV;
+
+ if ( !pseg->vf_setup_map )
+ {
+ unsigned int nr = PCI_BDF(-1, -1, -1) + 1;
+
+ pseg->vf_setup_map = xvzalloc_array(unsigned long, BITS_TO_LONGS(nr));
+ if ( !pseg->vf_setup_map )
+ return -ENOMEM;
+ }
+
+ set_bit(sbdf.bdf, pseg->vf_setup_map);
+
+ return 0;
+}
+
+void pci_vf_setup_end(pci_sbdf_t sbdf)
+{
+ const struct pci_seg *pseg = get_pseg(sbdf.seg);
+
+ if ( pseg && pseg->vf_setup_map )
+ clear_bit(sbdf.bdf, pseg->vf_setup_map);
+}
+
+bool pci_vf_setup_pending(pci_sbdf_t sbdf)
+{
+ const struct pci_seg *pseg = get_pseg(sbdf.seg);
+
+ return pseg && pseg->vf_setup_map &&
+ test_bit(sbdf.bdf, pseg->vf_setup_map);
+}
+
static struct phantom_dev {
u16 seg;
u8 bus, slot, stride;
diff --git a/xen/drivers/pci/physdev.c b/xen/drivers/pci/physdev.c
index 5695c300f7..7c0277a7e4 100644
--- a/xen/drivers/pci/physdev.c
+++ b/xen/drivers/pci/physdev.c
@@ -26,15 +26,15 @@ ret_t pci_physdev_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
if ( copy_from_guest(&add, arg, 1) != 0 )
break;
- pdev_info.is_extfn = (add.flags & XEN_PCI_DEV_EXTFN);
+ /*
+ * Virtual functions are always discovered by the hypervisor, so
+ * they don't need to be added here.
+ */
if ( add.flags & XEN_PCI_DEV_VIRTFN )
- {
- pdev_info.is_virtfn = true;
- pdev_info.physfn.bus = add.physfn.bus;
- pdev_info.physfn.devfn = add.physfn.devfn;
- }
- else
- pdev_info.is_virtfn = false;
+ return 0;
+
+ pdev_info.is_extfn = (add.flags & XEN_PCI_DEV_EXTFN);
+ pdev_info.is_virtfn = false;
#ifdef CONFIG_NUMA
if ( add.flags & XEN_PCI_DEV_PXM )
diff --git a/xen/drivers/vpci/Makefile b/xen/drivers/vpci/Makefile
index 9793a4f9b0..3ad9a31f2d 100644
--- a/xen/drivers/vpci/Makefile
+++ b/xen/drivers/vpci/Makefile
@@ -1,3 +1,4 @@
obj-y += cap.o
+obj-y += sriov.o
obj-y += vpci.o header.o rebar.o
obj-$(CONFIG_HAS_PCI_MSI) += msi.o msix.o
--git a/xen/drivers/vpci/header.c b/xen/drivers/vpci/header.c
index 01881e5eb2..a07e547c64 100644
--- a/xen/drivers/vpci/header.c
+++ b/xen/drivers/vpci/header.c
@@ -629,8 +629,13 @@ static void cf_check cmd_write(
* Let Dom0 play with all the bits directly except for the memory
* decoding one. Bits that are not allowed for DomU are already
* handled above and by the rsvdp_mask.
+ *
+ * PCI_COMMAND_MEMORY is hardwired to 0 for VFs, so for the hardware domain
+ * it must not control the mapping: VF memory space is enabled by MSE in the
+ * PF's SR-IOV control register.
*/
- if ( header->bars_mapped != !!(cmd & PCI_COMMAND_MEMORY) )
+ if ( !(pdev->info.is_virtfn && is_hardware_domain(pdev->domain)) &&
+ header->bars_mapped != !!(cmd & PCI_COMMAND_MEMORY) )
/*
* Ignore the error. No memory has been added or removed from the p2m
* (because the actual p2m changes are deferred in defer_map) and the
@@ -891,6 +896,9 @@ int vpci_init_header(struct pci_dev *pdev)
header->guest_cmd = cmd;
+ if ( pdev->info.is_virtfn )
+ return vpci_vf_init_header(pdev);
+
/* Disable memory decoding before sizing. */
if ( !is_hwdom || (cmd & PCI_COMMAND_MEMORY) )
pci_conf_write16(pdev->sbdf, PCI_COMMAND, cmd & ~PCI_COMMAND_MEMORY);
diff --git a/xen/drivers/vpci/private.h b/xen/drivers/vpci/private.h
index f012fd160d..1faf87a628 100644
--- a/xen/drivers/vpci/private.h
+++ b/xen/drivers/vpci/private.h
@@ -45,6 +45,7 @@ typedef struct {
REGISTER_VPCI_CAPABILITY(PCI_EXT_CAP_ID_##name, name, finit, fclean, true)
int __must_check vpci_init_header(struct pci_dev *pdev);
+int __must_check vpci_vf_init_header(struct pci_dev *pdev);
int vpci_init_capabilities(struct pci_dev *pdev, bool ext_only);
void vpci_cleanup_capabilities(struct pci_dev *pdev, bool ext_only);
@@ -141,6 +142,20 @@ static inline size_t vmsix_table_size(const struct vpci *vpci, unsigned int nr)
/* Map/unmap the BARs of a vPCI device. */
int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only);
+/*
+ * Handle a config space access to a VF which is enabled in hardware but not
+ * known to Xen yet. Returns false while the VF need not respond, in which
+ * case the access has to be terminated. Must be called with no PCI lock
+ * held, as adding a device acquires them.
+ */
+bool vpci_sriov_add_vf(pci_sbdf_t sbdf);
+
+/*
+ * Remove the VFs of the PF whose VF Enable the config space write in progress
+ * has cleared. Same locking requirement as above.
+ */
+void vpci_sriov_drop_vfs(void);
+
void cf_check vpci_guest_mem_bar_write(const struct pci_dev *pdev,
unsigned int reg, uint32_t val,
void *data);
diff --git a/xen/drivers/vpci/sriov.c b/xen/drivers/vpci/sriov.c
new file mode 100644
index 0000000000..4e09bdfbfd
--- /dev/null
+++ b/xen/drivers/vpci/sriov.c
@@ -0,0 +1,507 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Handlers for accesses to the SR-IOV capability structure.
+ *
+ * Copyright (C) 2026 Citrix Systems R&D
+ */
+
+#include "private.h"
+
+#include <xen/sched.h>
+#include <xen/softirq.h>
+#include <xen/time.h>
+#include <xen/vpci.h>
+#include <xen/xvmalloc.h>
+
+#include <xsm/xsm.h>
+
+static int vf_init_bars(struct pci_dev *vf_pdev)
+{
+ int vf_idx;
+ unsigned int i;
+ const struct pci_dev *pf_pdev = vf_pdev->pf_pdev;
+ struct vpci_bar *bars = vf_pdev->vpci->header.bars;
+ struct vpci_bar *physfn_vf_bars = pf_pdev->vpci->sriov->vf_bars;
+ struct vpci_sriov *sriov = pf_pdev->vpci->sriov;
+
+ vf_idx = vf_pdev->sbdf.sbdf - (pf_pdev->sbdf.sbdf + sriov->offset);
+ if ( vf_idx < 0 )
+ return -EINVAL;
+
+ if ( sriov->num_vfs > 1 && !sriov->stride )
+ return -EINVAL;
+
+ if ( sriov->stride )
+ {
+ if ( vf_idx % sriov->stride )
+ return -EINVAL;
+ vf_idx /= sriov->stride;
+ }
+
+ if ( vf_idx >= sriov->num_vfs )
+ return -EINVAL;
+
+ /*
+ * Set up BARs for this VF out of PF's VF BARs taking into account
+ * the index of the VF.
+ */
+ for ( i = 0; i < PCI_SRIOV_NUM_BARS; i++ )
+ {
+ struct vpci_bar *pf_bar = &physfn_vf_bars[i];
+ struct vpci_bar *bar = &bars[i];
+
+ /*
+ * The upper half of a 64-bit BAR holds no address or size of its own;
+ * the handlers reach the lower half through its type alone.
+ */
+ if ( pf_bar->type == VPCI_BAR_MEM64_HI )
+ {
+ bar->type = VPCI_BAR_MEM64_HI;
+ continue;
+ }
+
+ if ( pf_bar->type != VPCI_BAR_MEM32 &&
+ pf_bar->type != VPCI_BAR_MEM64_LO )
+ continue;
+
+ bar->addr = pf_bar->addr + vf_idx * pf_bar->size;
+ bar->guest_addr = is_hardware_domain(vf_pdev->domain) ? bar->addr : 0;
+ bar->size = pf_bar->size;
+ bar->type = pf_bar->type;
+ bar->prefetchable = pf_bar->prefetchable;
+ }
+
+ return 0;
+}
+
+int vpci_vf_init_header(struct pci_dev *vf_pdev)
+{
+ const struct pci_dev *pf_pdev;
+ int rc = 0;
+ uint16_t ctrl;
+
+ ASSERT(rw_is_write_locked(&vf_pdev->domain->pci_lock));
+ ASSERT(vf_pdev->info.is_virtfn);
+
+ pf_pdev = vf_pdev->pf_pdev;
+ ASSERT(pf_pdev);
+
+ rc = vf_init_bars(vf_pdev);
+ if ( rc )
+ return rc;
+
+ ctrl = pci_conf_read16(pf_pdev->sbdf,
+ pf_pdev->vpci->sriov->pos + PCI_SRIOV_CTRL);
+
+ if ( (pf_pdev->domain == vf_pdev->domain) && (ctrl & PCI_SRIOV_CTRL_MSE) )
+ {
+ rc = vpci_modify_bars(vf_pdev, PCI_COMMAND_MEMORY, false);
+ if ( rc )
+ return rc;
+ }
+
+ return rc;
+}
+
+static int map_vfs(const struct pci_dev *pf_pdev, uint16_t cmd)
+{
+ struct pci_dev *vf_pdev;
+ int rc;
+
+ ASSERT(rw_is_write_locked(&pf_pdev->domain->pci_lock));
+
+ list_for_each_entry(vf_pdev, &pf_pdev->vf_list, vf_list)
+ {
+ /* Don't try to change mapping if it is already in the correct state. */
+ if ( vf_pdev->vpci->header.bars_mapped == !!cmd )
+ continue;
+
+ rc = vpci_modify_bars(vf_pdev, cmd, false);
+ if ( rc )
+ {
+ gprintk(XENLOG_ERR, "failed to %s VF %pp: %d\n",
+ (cmd & PCI_COMMAND_MEMORY) ? "map" : "unmap",
+ &vf_pdev->sbdf, rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static void size_vf_bars(const struct pci_dev *pf_pdev, unsigned int sriov_pos,
+ uint64_t *vf_rlen)
+{
+ struct vpci_bar *bars = pf_pdev->vpci->sriov->vf_bars;
+ unsigned int i;
+ int rc = 0;
+
+ ASSERT(rw_is_write_locked(&pf_pdev->domain->pci_lock));
+ ASSERT(!pf_pdev->info.is_virtfn);
+ ASSERT(pf_pdev->vpci->sriov);
+
+ /* Set the BARs addresses and size. */
+ for ( i = 0; i < PCI_SRIOV_NUM_BARS; i += rc )
+ {
+ unsigned int idx = sriov_pos + PCI_SRIOV_BAR + i * 4;
+ uint32_t bar;
+ uint64_t addr, size;
+
+ bar = pci_conf_read32(pf_pdev->sbdf, idx);
+
+ rc = pci_size_mem_bar(pf_pdev->sbdf, idx, &addr, &size,
+ PCI_BAR_VF |
+ ((i == PCI_SRIOV_NUM_BARS - 1) ? PCI_BAR_LAST
+ : 0));
+
+ /*
+ * Update vf_rlen on the PF. According to the spec the size of
+ * the BARs can change if the system page size register is
+ * modified, so always update rlen when enabling VFs.
+ */
+ vf_rlen[i] = size;
+
+ if ( !size )
+ {
+ bars[i].type = VPCI_BAR_EMPTY;
+ continue;
+ }
+
+ bars[i].addr = addr;
+ bars[i].guest_addr = addr;
+ bars[i].size = size;
+ bars[i].prefetchable = bar & PCI_BASE_ADDRESS_MEM_PREFETCH;
+
+ switch ( rc )
+ {
+ case 1:
+ bars[i].type = VPCI_BAR_MEM32;
+ break;
+
+ case 2:
+ bars[i].type = VPCI_BAR_MEM64_LO;
+ bars[i + 1].type = VPCI_BAR_MEM64_HI;
+ break;
+
+ default:
+ ASSERT_UNREACHABLE();
+ rc = 1;
+ }
+ }
+}
+
+/*
+ * Accesses to a marked BDF are terminated until 100ms after VF Enable; the
+ * first one past that adds the VF, which control_write() cannot do itself.
+ */
+static pci_sbdf_t vf_sbdf(const struct pci_dev *pf_pdev, unsigned int idx)
+{
+ const struct vpci_sriov *sriov = pf_pdev->vpci->sriov;
+ pci_sbdf_t sbdf = pf_pdev->sbdf;
+
+ sbdf.bdf += sriov->offset + sriov->stride * idx;
+
+ return sbdf;
+}
+
+static void mark_vfs(const struct pci_dev *pf_pdev)
+{
+ struct vpci_sriov *sriov = pf_pdev->vpci->sriov;
+ unsigned int i;
+
+ /* VF Offset and VF Stride are only valid while VF Enable is set. */
+ sriov->offset = pci_conf_read16(pf_pdev->sbdf,
+ sriov->pos + PCI_SRIOV_VF_OFFSET);
+ sriov->stride = pci_conf_read16(pf_pdev->sbdf,
+ sriov->pos + PCI_SRIOV_VF_STRIDE);
+ sriov->vfs_ready = NOW() + MILLISECS(100);
+
+ for ( i = 0; i < sriov->num_vfs; i++ )
+ {
+ pci_sbdf_t sbdf = vf_sbdf(pf_pdev, i);
+ int rc = pci_vf_setup_begin(sbdf);
+
+ if ( rc )
+ gprintk(XENLOG_WARNING, "%pp: cannot set up VF %pp: %d\n",
+ &pf_pdev->sbdf, &sbdf, rc);
+ }
+}
+
+static void unmark_vfs(const struct pci_dev *pf_pdev)
+{
+ struct vpci_sriov *sriov = pf_pdev->vpci->sriov;
+ unsigned int i;
+
+ if ( !sriov->vfs_ready )
+ return;
+
+ for ( i = 0; i < sriov->num_vfs; i++ )
+ pci_vf_setup_end(vf_sbdf(pf_pdev, i));
+
+ sriov->vfs_ready = 0;
+}
+
+/* Find the PF which has enabled @sbdf, if that VF is ready to be added. */
+static const struct pci_dev *ready_pf(const struct domain *d, pci_sbdf_t sbdf)
+{
+ struct pci_dev *pdev;
+
+ ASSERT(rw_is_locked(&d->pci_lock));
+
+ for_each_pdev ( d, pdev )
+ {
+ const struct vpci_sriov *sriov = pdev->vpci ? pdev->vpci->sriov : NULL;
+ unsigned int i;
+
+ if ( !sriov || !sriov->vfs_ready || NOW() < sriov->vfs_ready )
+ continue;
+
+ for ( i = 0; i < sriov->num_vfs; i++ )
+ if ( vf_sbdf(pdev, i).sbdf == sbdf.sbdf )
+ return pdev;
+ }
+
+ return NULL;
+}
+
+bool vpci_sriov_add_vf(pci_sbdf_t sbdf)
+{
+ struct domain *d = current->domain;
+ struct pci_dev_info info = { .is_virtfn = true };
+ const struct pci_dev *pf;
+ pci_sbdf_t pf_sbdf;
+ nodeid_t node;
+ int rc;
+
+ read_lock(&d->pci_lock);
+ pf = ready_pf(d, sbdf);
+ if ( !pf )
+ {
+ read_unlock(&d->pci_lock);
+ gprintk(XENLOG_DEBUG, "%pp: config space access during VF setup\n",
+ &sbdf);
+ return false;
+ }
+
+ pf_sbdf = pf->sbdf;
+ info.physfn.bus = pf_sbdf.bus;
+ info.physfn.devfn = pf_sbdf.devfn;
+ node = pf->node;
+ read_unlock(&d->pci_lock);
+
+ rc = pci_add_device(sbdf.seg, sbdf.bus, sbdf.devfn, &info, node);
+ if ( rc )
+ gprintk(XENLOG_ERR, "%pp: cannot add VF %pp: %d\n", &pf_sbdf, &sbdf,
+ rc);
+
+ /*
+ * Stop terminating accesses either way: a VF which Xen failed to add is
+ * left for the hardware domain to register.
+ */
+ pci_vf_setup_end(sbdf);
+
+ return true;
+}
+
+void vpci_sriov_drop_vfs(void)
+{
+ struct vcpu *curr = current;
+ pci_sbdf_t pf_sbdf = curr->vpci.drop_vfs_pf;
+ struct pci_dev *pf, *vf, *tmp;
+
+ if ( likely(pf_sbdf.sbdf == ~0U) )
+ return;
+
+ curr->vpci.drop_vfs_pf.sbdf = ~0U;
+
+ /*
+ * The unmapping queued by control_write() still references the VF pdevs,
+ * so it has to complete before they are freed.
+ */
+ while ( vpci_process_pending(curr) )
+ process_pending_softirqs();
+
+ pcidevs_lock();
+ pf = pci_get_pdev(NULL, pf_sbdf);
+ if ( pf )
+ {
+ list_for_each_entry_safe(vf, tmp, &pf->vf_list, vf_list)
+ {
+ pci_sbdf_t sbdf = vf->sbdf;
+ int rc = pci_remove_device(sbdf.seg, sbdf.bus, sbdf.devfn);
+
+ if ( rc )
+ gprintk(XENLOG_ERR, "%pp: cannot remove VF %pp: %d\n",
+ &pf_sbdf, &sbdf, rc);
+ }
+ }
+ pcidevs_unlock();
+}
+
+static void cf_check control_write(const struct pci_dev *pdev, unsigned int reg,
+ uint32_t val, void *data)
+{
+ unsigned int sriov_pos = reg - PCI_SRIOV_CTRL;
+ struct vpci_sriov *sriov = pdev->vpci->sriov;
+ uint16_t control = pci_conf_read16(pdev->sbdf, reg);
+ bool enabled = control & PCI_SRIOV_CTRL_VFE;
+ bool new_enabled = val & PCI_SRIOV_CTRL_VFE;
+ /* VF BARs decode only while both VF Enable and VF MSE are set. */
+ bool decoding = enabled && (control & PCI_SRIOV_CTRL_MSE);
+ bool new_decoding = new_enabled && (val & PCI_SRIOV_CTRL_MSE);
+ int rc;
+
+ ASSERT(!pdev->info.is_virtfn);
+
+ if ( new_enabled == enabled && new_decoding == decoding )
+ {
+ pci_conf_write16(pdev->sbdf, reg, val);
+ return;
+ }
+
+ /* Unmap while the VF pdevs are still around for vpci_modify_bars(). */
+ if ( decoding && !new_decoding )
+ map_vfs(pdev, 0);
+
+ if ( enabled && !new_enabled )
+ {
+ unmark_vfs(pdev);
+
+ /* The VFs cease to exist below, so Xen has to remove them. */
+ if ( !list_empty(&pdev->vf_list) )
+ current->vpci.drop_vfs_pf = pdev->sbdf;
+ }
+
+ if ( !enabled && new_enabled )
+ {
+ size_vf_bars(pdev, sriov_pos, data);
+
+ /*
+ * Only update the number of active VFs when enabling, when
+ * disabling use the cached value in order to always remove the same
+ * number of VFs that were active.
+ */
+ sriov->num_vfs = pci_conf_read16(pdev->sbdf,
+ sriov_pos + PCI_SRIOV_NUM_VF);
+ mark_vfs(pdev);
+ }
+
+ if ( !decoding && new_decoding )
+ {
+ rc = map_vfs(pdev, PCI_COMMAND_MEMORY);
+
+ if ( rc )
+ map_vfs(pdev, 0);
+ }
+
+ pci_conf_write16(pdev->sbdf, reg, val);
+}
+
+static int cf_check init_sriov(struct pci_dev *pdev)
+{
+ unsigned int pos;
+
+ if ( pdev->info.is_virtfn )
+ return -EINVAL;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
+
+ ASSERT(pos);
+
+ if ( xsm_resource_setup_pci(XSM_PRIV, pdev->sbdf.bdf) )
+ {
+ printk(XENLOG_ERR
+ "%pp: SR-IOV configuration unsupported for unpriv %pd\n",
+ &pdev->sbdf, pdev->domain);
+ return -EACCES;
+ }
+
+ pdev->vpci->sriov = xvzalloc(struct vpci_sriov);
+ if ( !pdev->vpci->sriov )
+ return -ENOMEM;
+
+ pdev->vpci->sriov->pos = pos;
+
+ /*
+ * We need to modify vf_rlen in control_write but we can't do it cleanly
+ * from pdev because write callback only accepts const pdev. Moving vf_rlen
+ * inside of struct vpci_sriov is also not possible because it is used
+ * before vpci init. So pass it here as additional data to not require
+ * dropping const in control_write.
+ */
+ return vpci_add_register(pdev->vpci, vpci_hw_read16, control_write,
+ pos + PCI_SRIOV_CTRL, 2, &pdev->physfn.vf_rlen);
+}
+
+static int cf_check cleanup_sriov(const struct pci_dev *pdev, bool hide)
+{
+ unsigned int pos;
+ int rc;
+
+ if ( !pdev->vpci->sriov )
+ return 0;
+
+ ASSERT(!pdev->info.is_virtfn);
+
+ unmark_vfs(pdev);
+
+ if ( !list_empty(&pdev->vf_list) )
+ {
+ struct pci_dev *vf_pdev, *temp;
+
+ printk(XENLOG_WARNING
+ "Attempting to remove SR-IOV PF %pp with VFs still present, VFs will be forcefully removed first\n",
+ &pdev->sbdf);
+
+ map_vfs(pdev, 0);
+ list_for_each_entry_safe_reverse(vf_pdev, temp, &pdev->vf_list, vf_list)
+ {
+ pci_remove_device(vf_pdev->sbdf.seg, vf_pdev->sbdf.bus,
+ vf_pdev->sbdf.devfn);
+ }
+ }
+
+ pos = pdev->vpci->sriov->pos;
+ if ( !hide )
+ {
+ XVFREE(pdev->vpci->sriov);
+ return 0;
+ }
+
+ rc = vpci_remove_registers(pdev->vpci, pos + PCI_SRIOV_CTRL, 2);
+ if ( rc )
+ {
+ printk(XENLOG_ERR "%pd %pp: fail to remove SRIOV handlers rc=%d\n",
+ pdev->domain, &pdev->sbdf, rc);
+ ASSERT_UNREACHABLE();
+ return rc;
+ }
+ XVFREE(pdev->vpci->sriov);
+
+ /*
+ * Unprivileged domains have a deny by default register access policy, no
+ * need to add any further handlers for them.
+ */
+ if ( !is_hardware_domain(pdev->domain) )
+ return 0;
+
+ rc = vpci_add_register(pdev->vpci, vpci_hw_read16, NULL,
+ pos + PCI_SRIOV_CTRL, 2, NULL);
+ if ( rc )
+ printk(XENLOG_ERR "%pd %pp: fail to add SRIOV ctrl handler rc=%d\n",
+ pdev->domain, &pdev->sbdf, rc);
+
+ return rc;
+}
+
+REGISTER_VPCI_EXTCAP(SRIOV, init_sriov, cleanup_sriov);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/drivers/vpci/vpci.c b/xen/drivers/vpci/vpci.c
index 9a422f9ea0..767fe5c6c9 100644
--- a/xen/drivers/vpci/vpci.c
+++ b/xen/drivers/vpci/vpci.c
@@ -27,6 +27,7 @@
void vpci_vcpu_init(struct vcpu *v)
{
INIT_LIST_HEAD(&v->vpci.task_queue);
+ v->vpci.drop_vfs_pf.sbdf = ~0U;
}
#ifdef CONFIG_HAS_VPCI_GUEST_SUPPORT
@@ -486,6 +487,11 @@ uint32_t vpci_read(pci_sbdf_t sbdf, unsigned int reg, unsigned int size)
return data;
}
+ /* Ahead of any lock being taken, as adding a device acquires them. */
+ if ( unlikely(is_hardware_domain(d) && pci_vf_setup_pending(sbdf)) &&
+ !vpci_sriov_add_vf(sbdf) )
+ return data & (0xffffffffU >> (32 - 8 * size));
+
/*
* Find the PCI dev matching the address, which for hwdom also requires
* consulting DomXEN. Passthrough everything that's not trapped.
@@ -607,6 +613,11 @@ void vpci_write(pci_sbdf_t sbdf, unsigned int reg, unsigned int size,
return;
}
+ /* Ahead of any lock being taken, as adding a device acquires them. */
+ if ( unlikely(is_hardware_domain(d) && pci_vf_setup_pending(sbdf)) &&
+ !vpci_sriov_add_vf(sbdf) )
+ return;
+
/*
* Find the PCI dev matching the address, which for hwdom also requires
* consulting DomXEN. Passthrough everything that's not trapped.
@@ -680,6 +691,9 @@ void vpci_write(pci_sbdf_t sbdf, unsigned int reg, unsigned int size,
/* Tailing gap, write the remaining. */
vpci_write_hw(sbdf, reg + data_offset, size - data_offset,
data >> (data_offset * 8));
+
+ /* Only once every lock has been released, as removal acquires them. */
+ vpci_sriov_drop_vfs();
}
/* Helper function to check an access size and alignment on vpci space. */
diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h
index b7a35371a7..b902c5e888 100644
--- a/xen/include/xen/pci.h
+++ b/xen/include/xen/pci.h
@@ -229,6 +229,9 @@ void setup_hwdom_pci_devices(struct domain *d,
int pci_release_devices(struct domain *d);
int pci_add_segment(u16 seg);
const unsigned long *pci_get_ro_map(u16 seg);
+int pci_vf_setup_begin(pci_sbdf_t sbdf);
+void pci_vf_setup_end(pci_sbdf_t sbdf);
+bool pci_vf_setup_pending(pci_sbdf_t sbdf);
int pci_add_device(u16 seg, u8 bus, u8 devfn,
const struct pci_dev_info *info, nodeid_t node);
int pci_remove_device(u16 seg, u8 bus, u8 devfn);
diff --git a/xen/include/xen/vpci.h b/xen/include/xen/vpci.h
index 26aa120583..eead9487d0 100644
--- a/xen/include/xen/vpci.h
+++ b/xen/include/xen/vpci.h
@@ -4,6 +4,7 @@
#ifdef CONFIG_HAS_VPCI
#include <xen/pci.h>
+#include <xen/time.h>
#include <xen/types.h>
#include <xen/list.h>
@@ -84,7 +85,6 @@ struct vpci {
* upon to know whether BARs are mapped into the guest p2m.
*/
bool bars_mapped : 1;
- /* FIXME: currently there's no support for SR-IOV. */
} header;
/* MSI data. */
@@ -145,6 +145,22 @@ struct vpci {
unsigned int nbars:3;
} rebar;
+ struct vpci_sriov {
+ /* PF only */
+ struct vpci_bar vf_bars[PCI_SRIOV_NUM_BARS];
+ /*
+ * When the VFs which are enabled but not added to Xen yet are
+ * guaranteed to respond to config space accesses. Zero if there are
+ * none.
+ */
+ s_time_t vfs_ready;
+ /* Routing ID offset of the first VF, and stride between VFs. */
+ uint16_t offset;
+ uint16_t stride;
+ uint16_t num_vfs;
+ unsigned int pos;
+ } *sriov;
+
#ifdef CONFIG_HAS_VPCI_GUEST_SUPPORT
/* Guest SBDF of the device. */
#define INVALID_GUEST_SBDF ((pci_sbdf_t){ .sbdf = ~0U })
@@ -169,6 +185,12 @@ struct vpci_map_task {
struct vpci_vcpu {
struct list_head task_queue;
+ /*
+ * PF whose VFs have been disabled by the config space write in progress,
+ * and which Xen has to remove once the PCI locks have been dropped. All
+ * ones when there is none.
+ */
+ pci_sbdf_t drop_vfs_pf;
};
void vpci_dump_msi(void);
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH v4 5/5] vpci: add SR-IOV support for DomUs
2026-10-15 14:08 [PATCH v4 0/5] Implement SR-IOV support for PVH Mykyta Poturai
` (2 preceding siblings ...)
2026-10-15 14:08 ` [PATCH v4 4/5] vpci: add SR-IOV support for PVH Dom0 Mykyta Poturai
@ 2026-10-15 14:08 ` Mykyta Poturai
2026-10-15 14:08 ` [PATCH v4 3/5] vpci: allow queueing of mapping operations Mykyta Poturai
4 siblings, 0 replies; 6+ messages in thread
From: Mykyta Poturai @ 2026-10-15 14:08 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Stewart Hildebrand, Roger Pau Monné, Mykyta Poturai
From: Stewart Hildebrand <stewart.hildebrand@amd.com>
Emulate guest BAR register values based on PF BAR values for VFs.
This allows creating a guest view of the normal BAR registers and emulates
the size and properties as it is done during PCI device enumeration by
the guest.
Expose VID/DID and class/revision to the guest.
Note, that VFs ROM BAR is read-only and is all zeros, but VF may provide
access to the PFs ROM via emulation and is not implemented.
Note this only removes one of the gaps on the way to vPCI DomU support;
VFs are not usable in DomUs until the rest of it is upstream. The DomU
testing behind this patch used out-of-tree vPCI DomU patches.
Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com>
Signed-off-by: Mykyta Poturai <mykyta_poturai@epam.com>
---
v3->v4:
* emulate VID/DID again
* add a note to commit message
v2->v3:
* don't emulate VID/DID registers
* replace ifdef with IS_ENABLED
v1->v2:
* remove VF register handlers covered by init_header
* set guest addr unconditionally
---
xen/drivers/vpci/sriov.c | 65 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 63 insertions(+), 2 deletions(-)
diff --git a/xen/drivers/vpci/sriov.c b/xen/drivers/vpci/sriov.c
index 4e09bdfbfd..024a099822 100644
--- a/xen/drivers/vpci/sriov.c
+++ b/xen/drivers/vpci/sriov.c
@@ -79,19 +79,80 @@ int vpci_vf_init_header(struct pci_dev *vf_pdev)
const struct pci_dev *pf_pdev;
int rc = 0;
uint16_t ctrl;
+ unsigned int sriov_pos;
ASSERT(rw_is_write_locked(&vf_pdev->domain->pci_lock));
ASSERT(vf_pdev->info.is_virtfn);
pf_pdev = vf_pdev->pf_pdev;
ASSERT(pf_pdev);
+ sriov_pos = pf_pdev->vpci->sriov->pos;
rc = vf_init_bars(vf_pdev);
if ( rc )
return rc;
- ctrl = pci_conf_read16(pf_pdev->sbdf,
- pf_pdev->vpci->sriov->pos + PCI_SRIOV_CTRL);
+ ctrl = pci_conf_read16(pf_pdev->sbdf, sriov_pos + PCI_SRIOV_CTRL);
+
+ if ( IS_ENABLED(CONFIG_HAS_VPCI_GUEST_SUPPORT) &&
+ pf_pdev->domain != vf_pdev->domain )
+ {
+ struct vpci_bar *bars = vf_pdev->vpci->header.bars;
+ /*
+ * A VF's Vendor and Device ID registers read as 0xffff. The guest has
+ * no PF to synthesise them from, so take the vendor from the PF and
+ * the device from its SR-IOV capability.
+ */
+ uint16_t vid = pci_conf_read16(pf_pdev->sbdf, PCI_VENDOR_ID);
+ uint16_t did = pci_conf_read16(pf_pdev->sbdf,
+ sriov_pos + PCI_SRIOV_VF_DID);
+
+ rc = vpci_add_register(vf_pdev->vpci, vpci_read_val, NULL,
+ PCI_VENDOR_ID, 2, (void *)(uintptr_t)vid);
+ if ( rc )
+ return rc;
+
+ rc = vpci_add_register(vf_pdev->vpci, vpci_read_val, NULL,
+ PCI_DEVICE_ID, 2, (void *)(uintptr_t)did);
+ if ( rc )
+ return rc;
+
+ /* Hardcode multi-function device bit to 0 */
+ rc = vpci_add_register(vf_pdev->vpci, vpci_read_val, NULL,
+ PCI_HEADER_TYPE, 1,
+ (void *)PCI_HEADER_TYPE_NORMAL);
+ if ( rc )
+ return rc;
+
+ rc = vpci_add_register(vf_pdev->vpci, vpci_hw_read32, NULL,
+ PCI_CLASS_REVISION, 4, NULL);
+ if ( rc )
+ return rc;
+
+ for ( unsigned int i = 0; i < PCI_SRIOV_NUM_BARS; i++ )
+ {
+ switch ( pf_pdev->vpci->sriov->vf_bars[i].type )
+ {
+ case VPCI_BAR_MEM32:
+ case VPCI_BAR_MEM64_LO:
+ case VPCI_BAR_MEM64_HI:
+ rc = vpci_add_register(vf_pdev->vpci, vpci_guest_mem_bar_read,
+ vpci_guest_mem_bar_write,
+ PCI_BASE_ADDRESS_0 + i * 4, 4, &bars[i]);
+ if ( rc )
+ return rc;
+ break;
+
+ default:
+ rc = vpci_add_register(vf_pdev->vpci, vpci_read_val, NULL,
+ PCI_BASE_ADDRESS_0 + i * 4, 4,
+ (void *)0);
+ if ( rc )
+ return rc;
+ break;
+ }
+ }
+ }
if ( (pf_pdev->domain == vf_pdev->domain) && (ctrl & PCI_SRIOV_CTRL_MSE) )
{
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH v4 3/5] vpci: allow queueing of mapping operations
2026-10-15 14:08 [PATCH v4 0/5] Implement SR-IOV support for PVH Mykyta Poturai
` (3 preceding siblings ...)
2026-10-15 14:08 ` [PATCH v4 5/5] vpci: add SR-IOV support for DomUs Mykyta Poturai
@ 2026-10-15 14:08 ` Mykyta Poturai
4 siblings, 0 replies; 6+ messages in thread
From: Mykyta Poturai @ 2026-10-15 14:08 UTC (permalink / raw)
To: xen-devel@lists.xenproject.org
Cc: Stewart Hildebrand, Andrew Cooper, Anthony PERARD, Michal Orzel,
Jan Beulich, Julien Grall, Roger Pau Monné,
Stefano Stabellini, Mykyta Poturai
From: Stewart Hildebrand <stewart.hildebrand@amd.com>
Introduce vPCI BAR mapping task queue. Store information needed to
map/unmap BARs in struct vpci_map_task. Allow queueing of BAR map/unmap
operations in a list, thus making it possible to perform multiple p2m
operations associated with single PCI config space register access.
The rangesets describing the ranges to be (un)mapped move from struct
vpci_bar into the task. They are only live for the duration of the
operation, and a single per-device set cannot describe more than one
queued operation. Allocate them along with the task and free them once
the task has been processed.
This is preparatory work for further changes that need to perform
multiple unmap/map operations before returning to guest.
At the moment, only a single operation will be queued. However, when
multiple operations are queued, there is a check in modify_bars() to
skip BARs already in the requested state that will no longer be
accurate. Remove this check in preparation of upcoming changes.
Amends: 622bdd962822 ("vpci/header: handle p2m range sets per BAR")
Message-ID: <20260406191203.97662-3-stewart.hildebrand@amd.com>
Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com>
Signed-off-by: Mykyta Poturai <mykyta_poturai@epam.com>
---
v3->v4:
* drop "vpci: Use pervcpu ranges for BAR mapping"; the rangesets are
now allocated per task here rather than per vcpu, so that patch only
added code this one removed again
* non-functional churn split out into "vpci: use a local variable for
the BAR rangeset"
v2->v3:
* new patch in this series, borrowed from [1]
[1]: https://patchew.org/Xen/20260406191203.97662-1-stewart.hildebrand@amd.com/20260406191203.97662-3-stewart.hildebrand@amd.com/
---
xen/common/domain.c | 2 +
| 232 ++++++++++++++++++++++++--------------
xen/drivers/vpci/vpci.c | 10 +-
xen/include/xen/vpci.h | 21 +++-
4 files changed, 173 insertions(+), 92 deletions(-)
diff --git a/xen/common/domain.c b/xen/common/domain.c
index 6b52713518..8b54635b33 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -508,6 +508,8 @@ struct vcpu *vcpu_create(struct domain *d, unsigned int vcpu_id)
if ( arch_vcpu_create(v) != 0 )
goto fail_sched;
+ vpci_vcpu_init(v);
+
d->vcpu[vcpu_id] = v;
if ( vcpu_id != 0 )
{
--git a/xen/drivers/vpci/header.c b/xen/drivers/vpci/header.c
index 23412bcafa..01881e5eb2 100644
--- a/xen/drivers/vpci/header.c
+++ b/xen/drivers/vpci/header.c
@@ -23,6 +23,7 @@
#include <xen/lib.h>
#include <xen/sched.h>
#include <xen/softirq.h>
+#include <xen/xvmalloc.h>
#include <xsm/xsm.h>
@@ -35,7 +36,7 @@
struct map_data {
struct domain *d;
- const struct vpci_bar *bar;
+ const struct vpci_bar_map *bar;
bool map;
};
@@ -174,32 +175,23 @@ static void modify_decoding(const struct pci_dev *pdev, uint16_t cmd,
ASSERT_UNREACHABLE();
}
-bool vpci_process_pending(struct vcpu *v)
+static int vpci_process_map_task(struct vpci_map_task *task, struct vcpu *v)
{
- const struct pci_dev *pdev = v->vpci.pdev;
- struct vpci_header *header = NULL;
unsigned int i;
+ const struct pci_dev *pdev = task->pdev;
- if ( !pdev )
- return false;
-
- read_lock(&v->domain->pci_lock);
+ ASSERT(rw_is_locked(&pdev->domain->pci_lock));
if ( !pdev->vpci || (v->domain != pdev->domain) )
- {
- v->vpci.pdev = NULL;
- read_unlock(&v->domain->pci_lock);
- return false;
- }
+ return -EINVAL;
- header = &pdev->vpci->header;
- for ( i = 0; i < ARRAY_SIZE(header->bars); i++ )
+ for ( i = 0; i < ARRAY_SIZE(task->bars); i++ )
{
- struct vpci_bar *bar = &header->bars[i];
+ struct vpci_bar_map *bar = &task->bars[i];
struct rangeset *mem = bar->mem;
struct map_data data = {
- .d = v->domain,
- .map = v->vpci.cmd & PCI_COMMAND_MEMORY,
+ .d = pdev->domain,
+ .map = task->cmd & PCI_COMMAND_MEMORY,
.bar = bar,
};
int rc;
@@ -210,38 +202,88 @@ bool vpci_process_pending(struct vcpu *v)
rc = rangeset_consume_ranges(mem, map_range, &data);
if ( rc == -ERESTART )
- {
- read_unlock(&v->domain->pci_lock);
- return true;
- }
+ return rc;
if ( rc )
{
spin_lock(&pdev->vpci->lock);
/* Disable memory decoding unconditionally on failure. */
- modify_decoding(pdev, v->vpci.cmd & ~PCI_COMMAND_MEMORY, false);
+ modify_decoding(pdev, task->cmd & ~PCI_COMMAND_MEMORY, false);
spin_unlock(&pdev->vpci->lock);
- /* Clean all the rangesets */
- for ( i = 0; i < ARRAY_SIZE(header->bars); i++ )
- if ( !rangeset_is_empty(header->bars[i].mem) )
- rangeset_purge(header->bars[i].mem);
+ if ( !is_hardware_domain(pdev->domain) )
+ domain_crash(pdev->domain);
- v->vpci.pdev = NULL;
+ return rc;
+ }
+ }
+
+ spin_lock(&pdev->vpci->lock);
+ modify_decoding(pdev, task->cmd, task->rom_only);
+ spin_unlock(&pdev->vpci->lock);
+
+ return 0;
+}
+static void destroy_map_task(struct vpci_map_task *task)
+{
+ unsigned int i;
+
+ if ( !task )
+ {
+ ASSERT_UNREACHABLE();
+ return;
+ }
+
+ for ( i = 0; i < ARRAY_SIZE(task->bars); i++ )
+ rangeset_destroy(task->bars[i].mem);
+
+ xvfree(task);
+}
+
+static void clear_map_queue(struct vcpu *v)
+{
+ struct vpci_map_task *task;
+
+ while ( (task = list_first_entry_or_null(&v->vpci.task_queue,
+ struct vpci_map_task,
+ next)) != NULL )
+ {
+ list_del(&task->next);
+ destroy_map_task(task);
+ }
+}
+
+bool vpci_process_pending(struct vcpu *v)
+{
+ struct vpci_map_task *task;
+
+ if ( list_empty(&v->vpci.task_queue) )
+ return false;
+
+ read_lock(&v->domain->pci_lock);
+
+ while ( (task = list_first_entry_or_null(&v->vpci.task_queue,
+ struct vpci_map_task,
+ next)) != NULL )
+ {
+ int rc = vpci_process_map_task(task, v);
+
+ if ( rc == -ERESTART )
+ {
read_unlock(&v->domain->pci_lock);
+ return true;
+ }
- if ( !is_hardware_domain(v->domain) )
- domain_crash(v->domain);
+ list_del(&task->next);
+ destroy_map_task(task);
- return false;
+ if ( rc )
+ {
+ clear_map_queue(v);
+ break;
}
}
- v->vpci.pdev = NULL;
-
- spin_lock(&pdev->vpci->lock);
- modify_decoding(pdev, v->vpci.cmd, v->vpci.rom_only);
- spin_unlock(&pdev->vpci->lock);
read_unlock(&v->domain->pci_lock);
@@ -249,17 +291,16 @@ bool vpci_process_pending(struct vcpu *v)
}
static int __init apply_map(struct domain *d, const struct pci_dev *pdev,
- uint16_t cmd)
+ struct vpci_map_task *task)
{
- struct vpci_header *header = &pdev->vpci->header;
int rc = 0;
unsigned int i;
ASSERT(rw_is_write_locked(&d->pci_lock));
- for ( i = 0; i < ARRAY_SIZE(header->bars); i++ )
+ for ( i = 0; i < ARRAY_SIZE(task->bars); i++ )
{
- struct vpci_bar *bar = &header->bars[i];
+ struct vpci_bar_map *bar = &task->bars[i];
struct rangeset *mem = bar->mem;
struct map_data data = { .d = d, .map = true, .bar = bar };
@@ -280,12 +321,48 @@ static int __init apply_map(struct domain *d, const struct pci_dev *pdev,
}
}
if ( !rc )
- modify_decoding(pdev, cmd, false);
+ modify_decoding(pdev, task->cmd, false);
return rc;
}
-static void defer_map(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
+static struct vpci_map_task *alloc_map_task(const struct pci_dev *pdev,
+ uint16_t cmd, bool rom_only)
+{
+ struct vpci_map_task *task;
+ unsigned int i;
+
+ task = xvzalloc(struct vpci_map_task);
+
+ if ( !task )
+ return NULL;
+
+ for ( i = 0; i < ARRAY_SIZE(task->bars); i++ )
+ {
+ if ( !MAPPABLE_BAR(&pdev->vpci->header.bars[i]) )
+ continue;
+
+ task->bars[i].mem = rangeset_new(pdev->domain, NULL,
+ RANGESETF_no_print);
+
+ if ( !task->bars[i].mem )
+ {
+ destroy_map_task(task);
+ return NULL;
+ }
+
+ task->bars[i].addr = pdev->vpci->header.bars[i].addr;
+ task->bars[i].guest_addr = pdev->vpci->header.bars[i].guest_addr;
+ }
+
+ task->cmd = cmd;
+ task->rom_only = rom_only;
+ task->pdev = pdev;
+
+ return task;
+}
+
+static void defer_map(struct vpci_map_task *task)
{
struct vcpu *curr = current;
@@ -295,9 +372,8 @@ static void defer_map(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
* is mapped. This can lead to parallel mapping operations being
* started for the same device if the domain is not well-behaved.
*/
- curr->vpci.pdev = pdev;
- curr->vpci.cmd = cmd;
- curr->vpci.rom_only = rom_only;
+ list_add_tail(&task->next, &curr->vpci.task_queue);
+
/*
* Raise a scheduler softirq in order to prevent the guest from resuming
* execution with pending mapping operations, to trigger the invocation
@@ -312,11 +388,17 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
struct pci_dev *tmp;
const struct domain *d;
const struct vpci_msix *msix = pdev->vpci->msix;
+ struct vpci_map_task *task;
unsigned int i, j;
int rc;
ASSERT(rw_is_write_locked(&pdev->domain->pci_lock));
+ task = alloc_map_task(pdev, cmd, rom_only);
+
+ if ( !task )
+ return -ENOMEM;
+
/*
* Create a rangeset per BAR that represents the current device memory
* region and compare it against all the currently active BAR memory
@@ -332,7 +414,7 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
for ( i = 0; i < ARRAY_SIZE(header->bars); i++ )
{
struct vpci_bar *bar = &header->bars[i];
- struct rangeset *mem = bar->mem;
+ struct rangeset *mem = task->bars[i].mem;
unsigned long start = PFN_DOWN(bar->addr);
unsigned long end = PFN_DOWN(bar->addr + bar->size - 1);
unsigned long start_guest = PFN_DOWN(bar->guest_addr);
@@ -343,9 +425,7 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
if ( !MAPPABLE_BAR(bar) ||
(rom_only ? bar->type != VPCI_BAR_ROM
- : (bar->type == VPCI_BAR_ROM && !header->rom_enabled)) ||
- /* Skip BARs already in the requested state. */
- bar->enabled == !!(cmd & PCI_COMMAND_MEMORY) )
+ : (bar->type == VPCI_BAR_ROM && !header->rom_enabled)) )
continue;
if ( !pci_check_bar(pdev, _mfn(start), _mfn(end)) )
@@ -368,7 +448,8 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
gprintk(XENLOG_G_WARNING,
"%pp: can't map BAR%u - offset mismatch: %#lx vs %#lx\n",
&pdev->sbdf, i, bar->guest_addr, bar->addr);
- return -EINVAL;
+ rc = -EINVAL;
+ goto fail;
}
rc = rangeset_add_range(mem, start_guest, end_guest);
@@ -376,13 +457,13 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
{
printk(XENLOG_G_WARNING "Failed to add [%lx, %lx]: %d\n",
start_guest, end_guest, rc);
- return rc;
+ goto fail;
}
/* Check for overlap with the already setup BAR ranges. */
for ( j = 0; j < i; j++ )
{
- struct rangeset *prev_mem = header->bars[j].mem;
+ struct rangeset *prev_mem = task->bars[j].mem;
if ( rangeset_is_empty(prev_mem) )
continue;
@@ -393,7 +474,7 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
gprintk(XENLOG_WARNING,
"%pp: failed to remove overlapping range [%lx, %lx]: %d\n",
&pdev->sbdf, start_guest, end_guest, rc);
- return rc;
+ goto fail;
}
}
@@ -403,7 +484,7 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
gprintk(XENLOG_WARNING,
"%pp: failed to sanitize BAR#%u memory: %d\n",
&pdev->sbdf, i, rc);
- return rc;
+ goto fail;
}
}
@@ -414,9 +495,9 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
unsigned long end = PFN_DOWN(vmsix_table_addr(pdev->vpci, i) +
vmsix_table_size(pdev->vpci, i) - 1);
- for ( j = 0; j < ARRAY_SIZE(header->bars); j++ )
+ for ( j = 0; j < ARRAY_SIZE(task->bars); j++ )
{
- struct rangeset *mem = header->bars[j].mem;
+ struct rangeset *mem = task->bars[j].mem;
if ( rangeset_is_empty(mem) )
continue;
@@ -427,7 +508,7 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
gprintk(XENLOG_WARNING,
"%pp: failed to remove MSIX table [%lx, %lx]: %d\n",
&pdev->sbdf, start, end, rc);
- return rc;
+ goto fail;
}
}
}
@@ -471,7 +552,7 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
for ( j = 0; j < ARRAY_SIZE(header->bars); j++)
{
const struct vpci_bar *bar = &header->bars[j];
- struct rangeset *mem = bar->mem;
+ struct rangeset *mem = task->bars[j].mem;
if ( !rangeset_overlaps_range(mem, start, end) ||
/*
@@ -490,7 +571,7 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
gprintk(XENLOG_WARNING,
"%pp: failed to remove [%lx, %lx]: %d\n",
&pdev->sbdf, start, end, rc);
- return rc;
+ goto fail;
}
}
}
@@ -513,12 +594,19 @@ int vpci_modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
* will always be to establish mappings and process all the BARs.
*/
ASSERT((cmd & PCI_COMMAND_MEMORY) && !rom_only);
- return apply_map(pdev->domain, pdev, cmd);
+ rc = apply_map(pdev->domain, pdev, task);
+ destroy_map_task(task);
+ return rc;
}
- defer_map(pdev, cmd, rom_only);
+ defer_map(task);
return 0;
+
+ fail:
+ destroy_map_task(task);
+
+ return rc;
}
static void cf_check cmd_write(
@@ -737,18 +825,6 @@ static void cf_check rom_write(
}
}
-static int bar_add_rangeset(const struct pci_dev *pdev, struct vpci_bar *bar,
- unsigned int i)
-{
- char str[32];
-
- snprintf(str, sizeof(str), "%pp:BAR%u", &pdev->sbdf, i);
-
- bar->mem = rangeset_new(pdev->domain, str, RANGESETF_no_print);
-
- return !bar->mem ? -ENOMEM : 0;
-}
-
int vpci_init_header(struct pci_dev *pdev)
{
uint16_t cmd;
@@ -859,10 +935,6 @@ int vpci_init_header(struct pci_dev *pdev)
else
bars[i].type = VPCI_BAR_MEM32;
- rc = bar_add_rangeset(pdev, &bars[i], i);
- if ( rc )
- goto fail;
-
rc = pci_size_mem_bar(pdev->sbdf, reg, &addr, &size,
(i == num_bars - 1) ? PCI_BAR_LAST : 0);
if ( rc < 0 )
@@ -916,12 +988,6 @@ int vpci_init_header(struct pci_dev *pdev)
4, rom);
if ( rc )
rom->type = VPCI_BAR_EMPTY;
- else
- {
- rc = bar_add_rangeset(pdev, rom, num_bars);
- if ( rc )
- goto fail;
- }
}
else if ( !is_hwdom )
{
diff --git a/xen/drivers/vpci/vpci.c b/xen/drivers/vpci/vpci.c
index 9e2c27e3a3..9a422f9ea0 100644
--- a/xen/drivers/vpci/vpci.c
+++ b/xen/drivers/vpci/vpci.c
@@ -24,6 +24,11 @@
#ifdef __XEN__
+void vpci_vcpu_init(struct vcpu *v)
+{
+ INIT_LIST_HEAD(&v->vpci.task_queue);
+}
+
#ifdef CONFIG_HAS_VPCI_GUEST_SUPPORT
static int assign_virtual_sbdf(struct pci_dev *pdev)
{
@@ -89,8 +94,6 @@ struct vpci_register *vpci_get_register(const struct vpci *vpci,
void vpci_deassign_device(struct pci_dev *pdev)
{
- unsigned int i;
-
ASSERT(rw_is_write_locked(&pdev->domain->pci_lock));
if ( !has_vpci(pdev->domain) || !pdev->vpci )
@@ -116,9 +119,6 @@ void vpci_deassign_device(struct pci_dev *pdev)
}
spin_unlock(&pdev->vpci->lock);
- for ( i = 0; i < ARRAY_SIZE(pdev->vpci->header.bars); i++ )
- rangeset_destroy(pdev->vpci->header.bars[i].mem);
-
xfree(pdev->vpci);
pdev->vpci = NULL;
}
diff --git a/xen/include/xen/vpci.h b/xen/include/xen/vpci.h
index 877aa391d1..26aa120583 100644
--- a/xen/include/xen/vpci.h
+++ b/xen/include/xen/vpci.h
@@ -19,6 +19,8 @@
*/
#define VPCI_MAX_VIRT_DEV (PCI_SLOT(~0) + 1)
+void vpci_vcpu_init(struct vcpu *v);
+
/* Assign vPCI to device by adding handlers. */
int __must_check vpci_assign_device(struct pci_dev *pdev);
@@ -54,7 +56,6 @@ struct vpci {
uint64_t guest_addr;
uint64_t size;
uint64_t resizable_sizes;
- struct rangeset *mem;
enum {
VPCI_BAR_EMPTY,
VPCI_BAR_IO,
@@ -152,14 +153,24 @@ struct vpci {
#endif
};
-struct vpci_vcpu {
+#ifdef __XEN__
+struct vpci_map_task {
/* Per-vcpu structure to store state while {un}mapping of PCI BARs. */
- const struct pci_dev *pdev;
+ struct list_head next;
+ struct vpci_bar_map {
+ uint64_t addr;
+ uint64_t guest_addr;
+ struct rangeset *mem;
+ } bars[ARRAY_SIZE(((struct vpci_header *)NULL)->bars)];
uint16_t cmd;
bool rom_only : 1;
+ const struct pci_dev *pdev;
+};
+
+struct vpci_vcpu {
+ struct list_head task_queue;
};
-#ifdef __XEN__
void vpci_dump_msi(void);
/* Arch-specific vPCI MSI helpers. */
@@ -204,6 +215,8 @@ bool vpci_ecam_read(pci_sbdf_t sbdf, unsigned int reg, unsigned int len,
#else /* !CONFIG_HAS_VPCI */
struct vpci_vcpu {};
+static inline void vpci_vcpu_init(struct vcpu *v) { }
+
static inline int vpci_reinit_ext_capabilities(struct pci_dev *pdev)
{
return 0;
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread