@@ -83,6 +83,7 @@ long hvm_physdev_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
case PHYSDEVOP_pci_mmcfg_reserved:
case PHYSDEVOP_pci_device_add:
case PHYSDEVOP_pci_device_remove:
+ case PHYSDEVOP_pci_device_state_reset:
case PHYSDEVOP_dbgp_op:
if ( !is_hardware_domain(currd) )
return -ENOSYS;
@@ -2,11 +2,17 @@
#include <xen/guest_access.h>
#include <xen/hypercall.h>
#include <xen/init.h>
+#include <xen/vpci.h>
#ifndef COMPAT
typedef long ret_t;
#endif
+static const struct pci_device_state_reset_method
+ pci_device_state_reset_methods[] = {
+ [ DEVICE_RESET_FLR ].reset_fn = vpci_reset_device_state,
+};
+
ret_t pci_physdev_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
{
ret_t ret;
@@ -67,6 +73,43 @@ ret_t pci_physdev_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
break;
}
+ case PHYSDEVOP_pci_device_state_reset: {
+ struct pci_device_state_reset dev_reset;
+ struct physdev_pci_device *dev;
+ struct pci_dev *pdev;
+ pci_sbdf_t sbdf;
+
+ if ( !is_pci_passthrough_enabled() )
+ return -EOPNOTSUPP;
+
+ ret = -EFAULT;
+ if ( copy_from_guest(&dev_reset, arg, 1) != 0 )
+ break;
+ dev = &dev_reset.dev;
+ sbdf = PCI_SBDF(dev->seg, dev->bus, dev->devfn);
+
+ ret = xsm_resource_setup_pci(XSM_PRIV, sbdf.sbdf);
+ if ( ret )
+ break;
+
+ pcidevs_lock();
+ pdev = pci_get_pdev(NULL, sbdf);
+ if ( !pdev )
+ {
+ pcidevs_unlock();
+ ret = -ENODEV;
+ break;
+ }
+
+ write_lock(&pdev->domain->pci_lock);
+ pcidevs_unlock();
+ ret = pci_device_state_reset_methods[dev_reset.reset_type].reset_fn(pdev);
+ write_unlock(&pdev->domain->pci_lock);
+ if ( ret )
+ printk(XENLOG_ERR "%pp: failed to reset vPCI device state\n", &sbdf);
+ break;
+ }
+
default:
ret = -ENOSYS;
break;
@@ -172,6 +172,15 @@ int vpci_assign_device(struct pci_dev *pdev)
return rc;
}
+
+int vpci_reset_device_state(struct pci_dev *pdev)
+{
+ ASSERT(rw_is_write_locked(&pdev->domain->pci_lock));
+
+ vpci_deassign_device(pdev);
+ return vpci_assign_device(pdev);
+}
+
#endif /* __XEN__ */
static int vpci_register_cmp(const struct vpci_register *r1,
@@ -296,6 +296,13 @@ DEFINE_XEN_GUEST_HANDLE(physdev_pci_device_add_t);
*/
#define PHYSDEVOP_prepare_msix 30
#define PHYSDEVOP_release_msix 31
+/*
+ * Notify the hypervisor that a PCI device has been reset, so that any
+ * internally cached state is regenerated. Should be called after any
+ * device reset performed by the hardware domain.
+ */
+#define PHYSDEVOP_pci_device_state_reset 32
+
struct physdev_pci_device {
/* IN */
uint16_t seg;
@@ -156,6 +156,22 @@ struct pci_dev {
struct vpci *vpci;
};
+struct pci_device_state_reset_method {
+ int (*reset_fn)(struct pci_dev *pdev);
+};
+
+enum pci_device_state_reset_type {
+ DEVICE_RESET_FLR,
+ DEVICE_RESET_COLD,
+ DEVICE_RESET_WARM,
+ DEVICE_RESET_HOT,
+};
+
+struct pci_device_state_reset {
+ struct physdev_pci_device dev;
+ enum pci_device_state_reset_type reset_type;
+};
+
#define for_each_pdev(domain, pdev) \
list_for_each_entry(pdev, &(domain)->pdev_list, domain_list)
@@ -38,6 +38,7 @@ 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 __must_check vpci_reset_device_state(struct pci_dev *pdev);
/* Add/remove a register handler. */
int __must_check vpci_add_register_mask(struct vpci *vpci,
@@ -282,6 +283,11 @@ static inline int vpci_assign_device(struct pci_dev *pdev)
static inline void vpci_deassign_device(struct pci_dev *pdev) { }
+static inline int __must_check vpci_reset_device_state(struct pci_dev *pdev)
+{
+ return 0;
+}
+
static inline void vpci_dump_msi(void) { }
static inline uint32_t vpci_read(pci_sbdf_t sbdf, unsigned int reg,