vpci/msix: improve handling of bogus MSI-X capabilities

I've had the luck to come across a PCI card that exposes a MSI-X capability
where the BIR of the vector and PBA tables points at a BAR that has 0 size.

This doesn't play nice with the code in vpci_make_msix_hole(), as it would
still use the address of such empty BAR (0) and attempt to carve a hole in
the p2m.  This leads to errors like the one below being reported by Xen:

d0v0 0000:22:00.0: existing mapping (mfn: 181c4300 type: 0) at 0 clobbers MSIX MMIO area

And the device left unable to enable memory decoding due to the failure
reported by vpci_make_msix_hole().

Introduce checking in init_msix() to ensure the BARs containing the MSI-X
tables are usable.  This requires checking that the BIR points to a
non-empty BAR, and the offset and size of the MSI-X tables can fit in the
target BAR.

This fixes booting PVH dom0 on Supermicro AS -2126HS-TN servers with AMD
EPYC 9965 processors.  The broken device is:

22:00.0 SATA controller: Advanced Micro Devices, Inc. [AMD] FCH SATA Controller [AHCI mode] (rev 93)

There are multiple of those integrated controllers in the system, all
broken in the same way.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Released-Acked-By: Oleksii Kurochko <oleksii.kurochko@gmail.com>
Reviewed-by: Stewart Hildebrand <stewart.hildebrand@amd.com>
This commit is contained in:
Roger Pau Monne committed 2025-10-10 11:02:39 +02:00
1 parent 8daabe4052
commit 53859596c0
2 files changed
+49 -5

No files matched your search

+46 -5
View File
@@ -18,6 +18,7 @@
*/
#include <xen/io.h>
#include <xen/lib.h>
#include <xen/sched.h>
#include <xen/vpci.h>
@@ -675,6 +676,51 @@ static int cf_check init_msix(struct pci_dev *pdev)
if ( !msix )
return -ENOMEM;
msix->tables[VPCI_MSIX_TABLE] =
pci_conf_read32(pdev->sbdf, msix_table_offset_reg(msix_offset));
msix->tables[VPCI_MSIX_PBA] =
pci_conf_read32(pdev->sbdf, msix_pba_offset_reg(msix_offset));
/* Check that the referenced BAR(s) regions are valid. */
for ( i = 0; i < ARRAY_SIZE(msix->tables); i++ )
{
const char *name = (i == VPCI_MSIX_TABLE) ? "vector" : "PBA";
const struct vpci_bar *bars = pdev->vpci->header.bars;
unsigned int bir = msix->tables[i] & PCI_MSIX_BIRMASK;
unsigned int type;
unsigned int offset = msix->tables[i] & ~PCI_MSIX_BIRMASK;
unsigned int size =
(i == VPCI_MSIX_TABLE) ? max_entries * PCI_MSIX_ENTRY_SIZE
: ROUNDUP(DIV_ROUND_UP(max_entries, 8), 8);
if ( bir >= ARRAY_SIZE(pdev->vpci->header.bars) )
{
printk(XENLOG_ERR DEV_BUG_PREFIX
"%pp: MSI-X %s table with out of range BIR %u\n",
&pdev->sbdf, name, bir);
invalid:
xfree(msix);
return -ENODEV;
}
type = bars[bir].type;
if ( type != VPCI_BAR_MEM32 && type != VPCI_BAR_MEM64_LO )
{
printk(XENLOG_ERR DEV_BUG_PREFIX
"%pp: MSI-X %s table at invalid BAR%u with type %u\n",
&pdev->sbdf, name, bir, type);
goto invalid;
}
if ( (uint64_t)offset + size > bars[bir].size )
{
printk(XENLOG_ERR DEV_BUG_PREFIX
"%pp: MSI-X %s table offset %#x size %#x outside of BAR%u size %#lx\n",
&pdev->sbdf, name, offset, size, bir, bars[bir].size);
goto invalid;
}
}
rc = vpci_add_register(pdev->vpci, control_read, control_write,
msix_control_reg(msix_offset), 2, msix);
if ( rc )
@@ -686,11 +732,6 @@ static int cf_check init_msix(struct pci_dev *pdev)
msix->max_entries = max_entries;
msix->pdev = pdev;
msix->tables[VPCI_MSIX_TABLE] =
pci_conf_read32(pdev->sbdf, msix_table_offset_reg(msix_offset));
msix->tables[VPCI_MSIX_PBA] =
pci_conf_read32(pdev->sbdf, msix_pba_offset_reg(msix_offset));
for ( i = 0; i < max_entries; i++)
{
msix->entries[i].masked = true;
+3
View File
@@ -60,6 +60,9 @@ static inline void
debugtrace_printk(const char *fmt, ...) {}
#endif
/* Common log prefixes for platform related issues. */
#define DEV_BUG_PREFIX "DEVICE BUG: "
extern void printk(const char *fmt, ...)
__attribute__ ((format (printf, 1, 2), cold));
void vprintk(const char *fmt, va_list args)
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.