diff --git a/xen/arch/x86/physdev.c b/xen/arch/x86/physdev.c index 8496011b59..5bbcf3b530 100644 --- a/xen/arch/x86/physdev.c +++ b/xen/arch/x86/physdev.c @@ -8,6 +8,8 @@ #include #include #include +#include + #include #include #include @@ -169,8 +171,19 @@ int cf_check physdev_check_pci_extcfg(struct pci_dev *pdev, void *arg) ASSERT(pdev->seg == info->segment); if ( pdev->bus >= info->start_bus && pdev->bus <= info->end_bus ) + { pci_check_extcfg(pdev); + /* + * The re-init failing doesn't mean the device becomes entirely non- + * functional. In case of failure, a message was already logged. + * Hence don't otherwise act upon failure. + * + * FIXME: Re-visit when DomU support is added to vPCI. + */ + vpci_reinit_ext_capabilities(pdev); + } + return 0; } #endif /* COMPAT */ diff --git a/xen/drivers/vpci/cap.c b/xen/drivers/vpci/cap.c index e26a2f95da..3c3e6ce1d3 100644 --- a/xen/drivers/vpci/cap.c +++ b/xen/drivers/vpci/cap.c @@ -285,13 +285,16 @@ static int vpci_init_ext_capability_list(const struct pci_dev *pdev) return 0; } -int vpci_init_capabilities(struct pci_dev *pdev) +int vpci_init_capabilities(struct pci_dev *pdev, bool ext_only) { - int rc; + int rc, accum_rc = 0; - rc = vpci_init_capability_list(pdev); - if ( rc ) - return rc; + if ( !ext_only ) + { + rc = vpci_init_capability_list(pdev); + if ( rc ) + return rc; + } rc = vpci_init_ext_capability_list(pdev); if ( rc ) @@ -305,7 +308,7 @@ int vpci_init_capabilities(struct pci_dev *pdev) unsigned int pos = 0; if ( !is_ext ) - pos = pci_find_cap_offset(pdev->sbdf, cap); + pos = !ext_only ? pci_find_cap_offset(pdev->sbdf, cap) : 0; else if ( is_hardware_domain(pdev->domain) ) pos = pci_find_ext_capability(pdev, cap); @@ -341,30 +344,40 @@ int vpci_init_capabilities(struct pci_dev *pdev) { printk(XENLOG_ERR "%pd %pp: hide %s cap %u fail rc=%d\n", pdev->domain, &pdev->sbdf, type, cap, rc); - return rc; + + /* Best effort for the re-init case. */ + if ( !ext_only ) + return rc; + + if ( !accum_rc ) + accum_rc = rc; } } } - return 0; + return accum_rc; } -void vpci_cleanup_capabilities(struct pci_dev *pdev) +void vpci_cleanup_capabilities(struct pci_dev *pdev, bool ext_only) { for ( unsigned int i = 0; i < NUM_VPCI_INIT; i++ ) { const vpci_capability_t *capability = &__start_vpci_array[i]; const unsigned int cap = capability->id; - unsigned int pos = 0; if ( !capability->cleanup ) continue; - if ( !capability->is_ext ) - pos = pci_find_cap_offset(pdev->sbdf, cap); - else if ( is_hardware_domain(pdev->domain) ) - pos = pci_find_ext_capability(pdev, cap); - if ( pos ) + /* + * Cannot call pci_find_ext_capability() here, as extended config space + * may (no longer) be accessible. As a result, extended capability + * ->cleanup() handlers need to cope with being called despite ->init() + * never having been called. Which in turn allows calling them even + * for DomU-s, no matter that vpci_init_capabilities() excludes them + * there for now. + */ + if ( capability->is_ext + || (!ext_only && pci_find_cap_offset(pdev->sbdf, cap)) ) { int rc = capability->cleanup(pdev, false); @@ -376,6 +389,27 @@ void vpci_cleanup_capabilities(struct pci_dev *pdev) } } +int vpci_reinit_ext_capabilities(struct pci_dev *pdev) +{ + if ( !pdev->vpci ) + return 0; + + /* + * FIXME: DomU support is missing. For already running domains we may + * need to pause them around the entire re-evaluation of extended config + * space accessibility. + */ + ASSERT(pdev->domain == hardware_domain); + + if ( vpci_remove_registers(pdev->vpci, PCI_CFG_SPACE_SIZE, + PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) ) + ASSERT_UNREACHABLE(); + + vpci_cleanup_capabilities(pdev, true); + + return vpci_init_capabilities(pdev, true); +} + /* * Local variables: * mode: C diff --git a/xen/drivers/vpci/private.h b/xen/drivers/vpci/private.h index fdeeca7aa8..2907f6b40f 100644 --- a/xen/drivers/vpci/private.h +++ b/xen/drivers/vpci/private.h @@ -46,8 +46,8 @@ typedef struct { int __must_check vpci_init_header(struct pci_dev *pdev); -int vpci_init_capabilities(struct pci_dev *pdev); -void vpci_cleanup_capabilities(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); /* Add/remove a register handler. */ int __must_check vpci_add_register_mask(struct vpci *vpci, diff --git a/xen/drivers/vpci/vpci.c b/xen/drivers/vpci/vpci.c index 683d5fc36c..0ac9ec8b04 100644 --- a/xen/drivers/vpci/vpci.c +++ b/xen/drivers/vpci/vpci.c @@ -102,7 +102,7 @@ void vpci_deassign_device(struct pci_dev *pdev) &pdev->domain->vpci_dev_assigned_map); #endif - vpci_cleanup_capabilities(pdev); + vpci_cleanup_capabilities(pdev, false); spin_lock(&pdev->vpci->lock); while ( !list_empty(&pdev->vpci->handlers) ) @@ -159,7 +159,7 @@ int vpci_assign_device(struct pci_dev *pdev) if ( rc ) goto out; - rc = vpci_init_capabilities(pdev); + rc = vpci_init_capabilities(pdev, false); out: if ( rc ) diff --git a/xen/include/xen/vpci.h b/xen/include/xen/vpci.h index 83ac41edf8..877aa391d1 100644 --- a/xen/include/xen/vpci.h +++ b/xen/include/xen/vpci.h @@ -25,6 +25,8 @@ int __must_check vpci_assign_device(struct pci_dev *pdev); /* Remove all handlers and free vpci related structures. */ void vpci_deassign_device(struct pci_dev *pdev); +int vpci_reinit_ext_capabilities(struct pci_dev *pdev); + /* Generic read/write handlers for the PCI config space. */ uint32_t vpci_read(pci_sbdf_t sbdf, unsigned int reg, unsigned int size); void vpci_write(pci_sbdf_t sbdf, unsigned int reg, unsigned int size, @@ -202,6 +204,11 @@ bool vpci_ecam_read(pci_sbdf_t sbdf, unsigned int reg, unsigned int len, #else /* !CONFIG_HAS_VPCI */ struct vpci_vcpu {}; +static inline int vpci_reinit_ext_capabilities(struct pci_dev *pdev) +{ + return 0; +} + static inline int vpci_assign_device(struct pci_dev *pdev) { return 0;