[PATCH v4 4/4] vpci: allow 32-bit BAR writes with memory decoding enabled

Stewart Hildebrand posted 4 patches 4 months, 1 week ago
[PATCH v4 4/4] vpci: allow 32-bit BAR writes with memory decoding enabled
Posted by Stewart Hildebrand 4 months, 1 week ago
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.

Resolves: https://gitlab.com/xen-project/xen/-/issues/197
Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com>
---
v3->v4:
* rebase on dynamically allocated map queue

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 | 32 +++++++++++++++++++++++---------
 1 file changed, 23 insertions(+), 9 deletions(-)

diff --git a/xen/drivers/vpci/header.c b/xen/drivers/vpci/header.c
index 20fe380552f4..dc4f585b4e40 100644
--- a/xen/drivers/vpci/header.c
+++ b/xen/drivers/vpci/header.c
@@ -670,6 +670,7 @@ static void cf_check bar_write(
 {
     struct vpci_bar *bar = data;
     bool hi = false;
+    uint16_t cmd = 0;
 
     ASSERT(is_hardware_domain(pdev->domain));
 
@@ -683,19 +684,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);
+        }
+        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
@@ -715,6 +726,9 @@ static void cf_check bar_write(
     }
 
     pci_conf_write32(pdev->sbdf, reg, val);
+
+    if ( bar->enabled )
+        modify_bars(pdev, cmd, false, true);
 }
 
 static void cf_check guest_mem_bar_write(const struct pci_dev *pdev,
-- 
2.53.0
Re: [PATCH v4 4/4] vpci: allow 32-bit BAR writes with memory decoding enabled
Posted by Roger Pau Monné 3 months, 3 weeks ago
On Mon, Apr 06, 2026 at 03:11:58PM -0400, Stewart Hildebrand wrote:
> 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.

While Linux does the disabling for 64bit BARs, I wonder if at some we
will need to handle 64bit BAR writes anyway for other OSes, specially
with domU support.

> Resolves: https://gitlab.com/xen-project/xen/-/issues/197
> Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com>
> ---
> v3->v4:
> * rebase on dynamically allocated map queue
> 
> 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 | 32 +++++++++++++++++++++++---------
>  1 file changed, 23 insertions(+), 9 deletions(-)
> 
> diff --git a/xen/drivers/vpci/header.c b/xen/drivers/vpci/header.c
> index 20fe380552f4..dc4f585b4e40 100644
> --- a/xen/drivers/vpci/header.c
> +++ b/xen/drivers/vpci/header.c
> @@ -670,6 +670,7 @@ static void cf_check bar_write(
>  {
>      struct vpci_bar *bar = data;
>      bool hi = false;
> +    uint16_t cmd = 0;
>  
>      ASSERT(is_hardware_domain(pdev->domain));
>  
> @@ -683,19 +684,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);
> +        }
> +        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
> @@ -715,6 +726,9 @@ static void cf_check bar_write(
>      }
>  
>      pci_conf_write32(pdev->sbdf, reg, val);

I don't think it matters a lot, but here we are changing the position
of the BAR in the host memory map while the mappings are still active.

> +
> +    if ( bar->enabled )
> +        modify_bars(pdev, cmd, false, true);
>  }
>  
>  static void cf_check guest_mem_bar_write(const struct pci_dev *pdev,

As it's in context here - we possibly want to do something similar
with guest writes now that we can?

While we might not have a user-case for domUs ATM, I think it's vest
if we also fix that use case at the same time that dom0 is fixed
(unless there's something that prevents it).

Thanks, Roger.
Re: [PATCH v4 4/4] vpci: allow 32-bit BAR writes with memory decoding enabled
Posted by Jan Beulich 3 months, 2 weeks ago
On 24.04.2026 10:50, Roger Pau Monné wrote:
> On Mon, Apr 06, 2026 at 03:11:58PM -0400, Stewart Hildebrand wrote:
>> --- a/xen/drivers/vpci/header.c
>> +++ b/xen/drivers/vpci/header.c
>> @@ -670,6 +670,7 @@ static void cf_check bar_write(
>>  {
>>      struct vpci_bar *bar = data;
>>      bool hi = false;
>> +    uint16_t cmd = 0;
>>  
>>      ASSERT(is_hardware_domain(pdev->domain));
>>  
>> @@ -683,19 +684,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);
>> +        }
>> +        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
>> @@ -715,6 +726,9 @@ static void cf_check bar_write(
>>      }
>>  
>>      pci_conf_write32(pdev->sbdf, reg, val);
> 
> I don't think it matters a lot, but here we are changing the position
> of the BAR in the host memory map while the mappings are still active.

It would matter if the original address space could be re-used for another
purpose while those mappings are still there?

Jan

Re: [PATCH v4 4/4] vpci: allow 32-bit BAR writes with memory decoding enabled
Posted by Jan Beulich 3 months, 3 weeks ago
On 06.04.2026 21:11, Stewart Hildebrand wrote:
> --- a/xen/drivers/vpci/header.c
> +++ b/xen/drivers/vpci/header.c
> @@ -670,6 +670,7 @@ static void cf_check bar_write(
>  {
>      struct vpci_bar *bar = data;
>      bool hi = false;
> +    uint16_t cmd = 0;
>  
>      ASSERT(is_hardware_domain(pdev->domain));
>  
> @@ -683,19 +684,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);
> +        }
> +        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
> @@ -715,6 +726,9 @@ static void cf_check bar_write(
>      }
>  
>      pci_conf_write32(pdev->sbdf, reg, val);
> +
> +    if ( bar->enabled )
> +        modify_bars(pdev, cmd, false, true);
>  }
>  
>  static void cf_check guest_mem_bar_write(const struct pci_dev *pdev,

While this all looks plausible, isn't something similar needed in rom_write()
then as well?

Jan