Currently, Xen vPCI refuses BAR writes if the BAR is mapped in p2m. If
firmware initializes a 32-bit BAR to a bad address, Linux may try to
write a new address to the 32-bit BAR without disabling memory decoding.
Since Xen refuses such writes, the BAR (and thus PCI device) will be
non-functional.
Allow the hardware domain to issue 32-bit BAR writes with memory
decoding enabled. This increases the compatibility of PVH dom0 with more
hardware.
Note that Linux aims at disabling memory decoding before writing 64-bit
BARs. Continue to refuse 64-bit BAR writes in Xen while those BARs are
mapped for now to avoid mapping half-updated BARs in p2m.
Increase number of unmap/map slots to 2.
Resolves: https://gitlab.com/xen-project/xen/-/issues/197
Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com>
---
v2->v3:
* minor tweaks for fixed number of map/unmap slots
v1->v2:
* rework on top of queued BAR map/unmap operation machinery
RFC->v1:
* keep memory decoding enabled in hardware
* allow write while memory decoding is enabled for 32-bit BARs only
* rework BAR mapping machinery to support unmap-then-map operation
---
xen/drivers/vpci/header.c | 33 ++++++++++++++++++++++++---------
xen/include/xen/vpci.h | 2 +-
2 files changed, 25 insertions(+), 10 deletions(-)
diff --git a/xen/drivers/vpci/header.c b/xen/drivers/vpci/header.c
index 9856840c3c87..2df992468383 100644
--- a/xen/drivers/vpci/header.c
+++ b/xen/drivers/vpci/header.c
@@ -605,6 +605,8 @@ static void cf_check bar_write(
{
struct vpci_bar *bar = data;
bool hi = false;
+ uint16_t cmd = 0;
+ unsigned int map_slot = 0;
ASSERT(is_hardware_domain(pdev->domain));
@@ -618,19 +620,29 @@ static void cf_check bar_write(
val &= PCI_BASE_ADDRESS_MEM_MASK;
/*
- * Xen only cares whether the BAR is mapped into the p2m, so allow BAR
- * writes as long as the BAR is not mapped into the p2m.
+ * Allow 64-bit BAR writes only when the BAR is not mapped in p2m. Always
+ * allow 32-bit BAR writes.
*/
if ( bar->enabled )
{
- /* If the value written is the current one avoid printing a warning. */
- if ( val != (uint32_t)(bar->addr >> (hi ? 32 : 0)) )
- gprintk(XENLOG_WARNING,
- "%pp: ignored BAR %zu write while mapped\n",
- &pdev->sbdf, bar - pdev->vpci->header.bars + hi);
- return;
- }
+ if ( bar->type == VPCI_BAR_MEM32 )
+ {
+ if ( val == bar->addr )
+ return;
+ cmd = pci_conf_read16(pdev->sbdf, PCI_COMMAND);
+ modify_bars(pdev, cmd, false, false, map_slot++);
+ }
+ else
+ {
+ /* If the value written is the same avoid printing a warning. */
+ if ( val != (uint32_t)(bar->addr >> (hi ? 32 : 0)) )
+ gprintk(XENLOG_WARNING,
+ "%pp: ignored BAR %zu write while mapped\n",
+ &pdev->sbdf, bar - pdev->vpci->header.bars + hi);
+ return;
+ }
+ }
/*
* Update the cached address, so that when memory decoding is enabled
@@ -650,6 +662,9 @@ static void cf_check bar_write(
}
pci_conf_write32(pdev->sbdf, reg, val);
+
+ if ( bar->enabled )
+ modify_bars(pdev, cmd, false, true, map_slot++);
}
static void cf_check guest_mem_bar_write(const struct pci_dev *pdev,
diff --git a/xen/include/xen/vpci.h b/xen/include/xen/vpci.h
index a6f8cb67dcac..a458b722cea6 100644
--- a/xen/include/xen/vpci.h
+++ b/xen/include/xen/vpci.h
@@ -168,7 +168,7 @@ struct vpci_vcpu {
bool rom_only : 1;
bool map : 1;
bool pending : 1;
- } task[1];
+ } task[2];
};
void vpci_dump_msi(void);
--
2.53.0