vpci: Hide extended capability when it fails to initialize

When vpci fails to initialize an extended capability of device, it
just returns an error and vPCI gets disabled for the whole device.

So, add function to hide extended capability when initialization
fails. And remove the failed extended capability handler from vpci
extended capability list.

Signed-off-by: Jiqian Chen <Jiqian.Chen@amd.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
This commit is contained in:
Jiqian Chen authored and Jan Beulich committed 2025-07-30 13:44:35 +02:00
1 parent 8241316f49
commit 0ba439b942
2 files changed
+92 -1

No files matched your search

+88
View File
@@ -165,6 +165,92 @@ static int vpci_capability_hide(const struct pci_dev *pdev, unsigned int cap)
return 0;
}
static struct vpci_register *vpci_get_previous_ext_cap_register(
const struct vpci *vpci, unsigned int offset)
{
unsigned int pos = PCI_CFG_SPACE_SIZE;
struct vpci_register *r;
if ( offset <= PCI_CFG_SPACE_SIZE )
{
ASSERT_UNREACHABLE();
return NULL;
}
for ( r = vpci_get_register(vpci, pos, 4); r;
r = pos > PCI_CFG_SPACE_SIZE ? vpci_get_register(vpci, pos, 4)
: NULL )
{
uint32_t header = (uint32_t)(uintptr_t)r->private;
ASSERT(header == (uintptr_t)r->private);
pos = PCI_EXT_CAP_NEXT(header);
if ( pos == offset )
break;
}
return r;
}
static int vpci_ext_capability_hide(
const struct pci_dev *pdev, unsigned int cap)
{
const unsigned int offset = pci_find_ext_capability(pdev->sbdf, cap);
struct vpci_register *r, *prev_r;
struct vpci *vpci = pdev->vpci;
uint32_t header, pre_header;
if ( offset < PCI_CFG_SPACE_SIZE )
{
ASSERT_UNREACHABLE();
return 0;
}
spin_lock(&vpci->lock);
r = vpci_get_register(vpci, offset, 4);
if ( !r )
{
spin_unlock(&vpci->lock);
return -ENODEV;
}
header = (uint32_t)(uintptr_t)r->private;
if ( offset == PCI_CFG_SPACE_SIZE )
{
if ( PCI_EXT_CAP_NEXT(header) <= PCI_CFG_SPACE_SIZE )
r->private = (void *)0;
else
/*
* The first extended capability (0x100) can not be removed from
* the linked list, so instead mask its capability ID to return 0
* hopefully forcing OSes to skip it.
*/
r->private = (void *)(uintptr_t)(header & ~PCI_EXT_CAP_ID(header));
spin_unlock(&vpci->lock);
return 0;
}
prev_r = vpci_get_previous_ext_cap_register(vpci, offset);
if ( !prev_r )
{
spin_unlock(&vpci->lock);
return -ENODEV;
}
pre_header = (uint32_t)(uintptr_t)prev_r->private;
pre_header &= ~PCI_EXT_CAP_NEXT_MASK;
pre_header |= header & PCI_EXT_CAP_NEXT_MASK;
prev_r->private = (void *)(uintptr_t)pre_header;
list_del(&r->node);
spin_unlock(&vpci->lock);
xfree(r);
return 0;
}
static int vpci_init_capabilities(struct pci_dev *pdev)
{
for ( unsigned int i = 0; i < NUM_VPCI_INIT; i++ )
@@ -206,6 +292,8 @@ static int vpci_init_capabilities(struct pci_dev *pdev)
if ( !is_ext )
rc = vpci_capability_hide(pdev, cap);
else
rc = vpci_ext_capability_hide(pdev, cap);
if ( rc )
{
printk(XENLOG_ERR "%pd %pp: hide %s cap %u fail rc=%d\n",
+4 -1
View File
@@ -448,7 +448,10 @@
/* Extended Capabilities (PCI-X 2.0 and Express) */
#define PCI_EXT_CAP_ID(header) ((header) & 0x0000ffff)
#define PCI_EXT_CAP_VER(header) (((header) >> 16) & 0xf)
#define PCI_EXT_CAP_NEXT(header) (((header) >> 20) & 0xffc)
#define PCI_EXT_CAP_NEXT_MASK 0xfff00000U
/* Bottom two bits of next capability position are reserved. */
#define PCI_EXT_CAP_NEXT(header) \
(MASK_EXTR(header, PCI_EXT_CAP_NEXT_MASK) & 0xffcU)
#define PCI_EXT_CAP_ID_ERR 1
#define PCI_EXT_CAP_ID_VC 2
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.