Support S3 for MSI interrupt
authorKeir Fraser <keir.fraser@citrix.com>
Fri, 19 Dec 2008 14:56:36 +0000 (14:56 +0000)
committerKeir Fraser <keir.fraser@citrix.com>
Fri, 19 Dec 2008 14:56:36 +0000 (14:56 +0000)
From: "Jiang, Yunhong" <yunhong.jiang@intel.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
xen/arch/x86/msi.c
xen/arch/x86/physdev.c
xen/include/asm-x86/msi.h
xen/include/public/physdev.h

index 30bf9052c4331c1777f22d040051f44d7a46e5fe..fdca2453cea1e943bb205f40cf13430678ababa8 100644 (file)
@@ -741,3 +741,43 @@ void pci_cleanup_msi(struct pci_dev *pdev)
     msi_free_vectors(pdev);
 }
 
+int pci_restore_msi_state(struct pci_dev *pdev)
+{
+    unsigned long flags;
+    int vector;
+    struct msi_desc *entry, *tmp;
+    irq_desc_t *desc;
+
+    ASSERT(spin_is_locked(&pcidevs_lock));
+
+    if (!pdev)
+        return -EINVAL;
+
+    list_for_each_entry_safe( entry, tmp, &pdev->msi_list, list )
+    {
+        vector = entry->vector;
+        desc = &irq_desc[vector];
+
+        spin_lock_irqsave(&desc->lock, flags);
+
+        ASSERT(desc->msi_desc == entry);
+
+        if (desc->msi_desc != entry)
+        {
+            dprintk(XENLOG_ERR, "Restore MSI for dev %x:%x not set before?\n",
+                                pdev->bus, pdev->devfn);
+            spin_unlock_irqrestore(&desc->lock, flags);
+            return -EINVAL;
+        }
+
+        msi_set_enable(pdev, 0);
+        write_msi_msg(entry, &entry->msg);
+
+        msi_set_enable(pdev, 1);
+        msi_set_mask_bit(vector, entry->msi_attrib.masked);
+        spin_unlock_irqrestore(&desc->lock, flags);
+    }
+
+    return 0;
+}
+
index fafe74f5462d57ed5f1e7d578ef444761462ed21..89a1d686fa4d55e5c87caf92072c828348c8293c 100644 (file)
@@ -415,6 +415,24 @@ ret_t do_physdev_op(int cmd, XEN_GUEST_HANDLE(void) arg)
         break;
     }
 
+    case PHYSDEVOP_restore_msi: {
+        struct physdev_restore_msi restore_msi;
+        struct pci_dev *pdev;
+
+        ret = -EPERM;
+        if ( !IS_PRIV(v->domain) )
+            break;
+
+        ret = -EFAULT;
+        if ( copy_from_guest(&restore_msi, arg, 1) != 0 )
+            break;
+
+        spin_lock(&pcidevs_lock);
+        pdev = pci_get_pdev(restore_msi.bus, restore_msi.devfn);
+        ret = pdev ? pci_restore_msi_state(pdev) : -ENODEV;
+        spin_unlock(&pcidevs_lock);
+        break;
+    }
     default:
         ret = -ENOSYS;
         break;
index 779ee8d46b35c42ae83ddf1a7ae9271b85bf4480..1f18b7c5e3cc4f1283fef81f9c4589222c523462 100644 (file)
@@ -79,6 +79,7 @@ extern void pci_cleanup_msi(struct pci_dev *pdev);
 extern int setup_msi_irq(struct pci_dev *dev, struct msi_desc *desc);
 extern void teardown_msi_vector(int vector);
 extern int msi_free_vector(struct msi_desc *entry);
+extern int pci_restore_msi_state(struct pci_dev *pdev);
 
 struct msi_desc {
        struct {
index b4d303ffe34cb0bbdfeee09e3388e9f60ec95bd0..72a42cf265ce9f5aedfdd6118caca03cf53d1366 100644 (file)
@@ -183,6 +183,15 @@ struct physdev_manage_pci {
 typedef struct physdev_manage_pci physdev_manage_pci_t;
 DEFINE_XEN_GUEST_HANDLE(physdev_manage_pci_t);
 
+#define PHYSDEVOP_restore_msi            19
+struct physdev_restore_msi {
+    /* IN */
+    uint8_t bus;
+    uint8_t devfn;
+};
+typedef struct physdev_restore_msi physdev_restore_msi_t;
+DEFINE_XEN_GUEST_HANDLE(physdev_restore_msi_t);
+
 /*
  * Argument to physdev_op_compat() hypercall. Superceded by new physdev_op()
  * hypercall since 0x00030202.