]> xenbits.xen.org Git - xenclient/ioemu.git/commitdiff
passthrough: Fix error handling when interrupt hypercall fails.
authorIan Jackson <ian.jackson@eu.citrix.com>
Wed, 8 Apr 2009 16:54:45 +0000 (17:54 +0100)
committerIan Jackson <Ian.Jackson@eu.citrix.com>
Wed, 8 Apr 2009 16:54:45 +0000 (17:54 +0100)
This patch fixes error handling when interrupt hypercall fails.
This patch makes Interrupt Disable bit emulate type.

The policy of this patch is [ in a comment at the top of pass-through.c ]

Signed-off-by: Yuji Shimada <shimada-yxb@necst.nec.co.jp>
hw/pass-through.c
hw/pass-through.h
hw/pt-msi.c
hw/pt-msi.h

index 95b4a472bb23dc743ac13bdb9672d828ab25b0e7..11382fd490ffa00243a3af566cc54a3f4f5ddfee 100644 (file)
  * This file implements direct PCI assignment to a HVM guest
  */
 
+/*
+ * Interrupt Disable policy:
+ *
+ * INTx interrupt:
+ *   Initialize(register_real_device)
+ *     Map INTx(xc_physdev_map_pirq):
+ *       <fail>
+ *         - Set real Interrupt Disable bit to '1'.
+ *         - Set machine_irq and assigned_device->machine_irq to '0'.
+ *         * Don't bind INTx.
+ * 
+ *     Bind INTx(xc_domain_bind_pt_pci_irq):
+ *       <fail>
+ *         - Set real Interrupt Disable bit to '1'.
+ *         - Unmap INTx.
+ *         - Decrement mapped_machine_irq[machine_irq]
+ *         - Set assigned_device->machine_irq to '0'.
+ * 
+ *   Write to Interrupt Disable bit by guest software(pt_cmd_reg_write)
+ *     Write '0'
+ *       <ptdev->msi_trans_en is false>
+ *         - Set real bit to '0' if assigned_device->machine_irq isn't '0'.
+ * 
+ *     Write '1'
+ *       <ptdev->msi_trans_en is false>
+ *         - Set real bit to '1'.
+ * 
+ * MSI-INTx translation.
+ *   Initialize(xc_physdev_map_pirq_msi/pt_msi_setup)
+ *     Bind MSI-INTx(xc_domain_bind_pt_irq)
+ *       <fail>
+ *         - Unmap MSI.
+ *           <success>
+ *             - Set dev->msi->pirq to '-1'.
+ *           <fail>
+ *             - Do nothing.
+ * 
+ *   Write to Interrupt Disable bit by guest software(pt_cmd_reg_write)
+ *     Write '0'
+ *       <ptdev->msi_trans_en is true>
+ *         - Set MSI Enable bit to '1'.
+ * 
+ *     Write '1'
+ *       <ptdev->msi_trans_en is true>
+ *         - Set MSI Enable bit to '0'.
+ * 
+ * MSI interrupt:
+ *   Initialize MSI register(pt_msi_setup, pt_msi_update)
+ *     Bind MSI(xc_domain_update_msi_irq)
+ *       <fail>
+ *         - Unmap MSI.
+ *         - Set dev->msi->pirq to '-1'.
+ * 
+ * MSI-X interrupt:
+ *   Initialize MSI-X register(pt_msix_update_one)
+ *     Bind MSI-X(xc_domain_update_msi_irq)
+ *       <fail>
+ *         - Unmap MSI-X.
+ *         - Set entry->pirq to '-1'.
+ */
+
 #include "pass-through.h"
 #include "pci/header.h"
 #include "pci/pci.h"
@@ -208,7 +269,7 @@ static struct pt_reg_info_tbl pt_emu_reg_header0_tbl[] = {
         .size       = 2,
         .init_val   = 0x0000,
         .ro_mask    = 0xF880,
-        .emu_mask   = 0x0340,
+        .emu_mask   = 0x0740,
         .init       = pt_common_reg_init,
         .u.w.read   = pt_cmd_reg_read,
         .u.w.write  = pt_cmd_reg_write,
@@ -2945,6 +3006,23 @@ static int pt_cmd_reg_write(struct pt_dev *ptdev,
 
     /* create value for writing to I/O device register */
     throughable_mask = ~emu_mask & valid_mask;
+
+    if (*value & PCI_COMMAND_DISABLE_INTx)
+    {
+        if (ptdev->msi_trans_en)
+            msi_set_enable(ptdev, 0);
+        else
+            throughable_mask |= PCI_COMMAND_DISABLE_INTx;
+    }
+    else
+    {
+        if (ptdev->msi_trans_en)
+            msi_set_enable(ptdev, 1);
+        else
+            if (ptdev->machine_irq)
+                throughable_mask |= PCI_COMMAND_DISABLE_INTx;
+    }
+
     *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask);
 
     /* mapping BAR */
@@ -3312,8 +3390,12 @@ static int pt_msgctrl_reg_write(struct pt_dev *ptdev,
                    return 0;
                 }
             }
-            pt_msi_update(ptdev);
-
+            if (pt_msi_update(ptdev))
+            {
+                *value &= ~PCI_MSI_FLAGS_ENABLE;
+                PT_LOG("Warning: Can not bind MSI for dev %x\n", pd->devfn);
+                return 0;
+            }
             ptdev->msi->flags &= ~MSI_FLAG_UNINIT;
             ptdev->msi->flags |= PT_MSI_MAPPED;
         }
@@ -3543,6 +3625,11 @@ static int pt_cmd_reg_restore(struct pt_dev *ptdev,
     restorable_mask = reg->emu_mask & ~PCI_COMMAND_FAST_BACK;
     *value = PT_MERGE_VALUE(*value, dev_value, restorable_mask);
 
+    if (!ptdev->machine_irq)
+        *value |= PCI_COMMAND_DISABLE_INTx;
+    else
+        *value &= ~PCI_COMMAND_DISABLE_INTx;
+
     return 0;
 }
 
@@ -3756,8 +3843,14 @@ static struct pt_dev * register_real_device(PCIBus *e_bus,
 
         if ( rc )
         {
-            /* TBD: unregister device in case of an error */
             PT_LOG("Error: Mapping irq failed, rc = %d\n", rc);
+
+            /* Disable PCI intx assertion (turn on bit10 of devctl) */
+            pci_write_word(pci_dev, PCI_COMMAND,
+                *(uint16_t *)(&assigned_device->dev.config[PCI_COMMAND])
+                | PCI_COMMAND_DISABLE_INTx);
+            machine_irq = 0;
+            assigned_device->machine_irq = 0;
         }
         else
         {
@@ -3781,16 +3874,23 @@ static struct pt_dev * register_real_device(PCIBus *e_bus,
                                        e_device, e_intx);
         if ( rc < 0 )
         {
-            /* TBD: unregister device in case of an error */
             PT_LOG("Error: Binding of interrupt failed! rc=%d\n", rc);
+
+            /* Disable PCI intx assertion (turn on bit10 of devctl) */
+            pci_write_word(pci_dev, PCI_COMMAND,
+                *(uint16_t *)(&assigned_device->dev.config[PCI_COMMAND])
+                | PCI_COMMAND_DISABLE_INTx);
+            mapped_machine_irq[machine_irq]--;
+
+            if (mapped_machine_irq[machine_irq] == 0)
+            {
+                if (xc_physdev_unmap_pirq(xc_handle, domid, machine_irq))
+                    PT_LOG("Error: Unmapping of interrupt failed! rc=%d\n",
+                        rc);
+            }
+            assigned_device->machine_irq = 0;
         }
     }
-    else {
-        /* Disable PCI intx assertion (turn on bit10 of devctl) */
-        assigned_device->dev.config[0x05] |= 0x04;
-        pci_write_word(pci_dev, 0x04,
-            *(uint16_t *)(&assigned_device->dev.config[0x04]));
-    }
 
 out:
     PT_LOG("Real physical device %02x:%02x.%x registered successfuly!\n"
index 3132387fa486cb505b1f5f9313716bf3cb8fbd49..a503e8096aa6b647c8c5a188c2793eca383db87c 100644 (file)
 /* because the current version of libpci (2.2.0) doesn't define these ID,
  * so we define Capability ID here.
  */
+#ifndef PCI_COMMAND_DISABLE_INTx
+/* Disable INTx interrupts */
+#define PCI_COMMAND_DISABLE_INTx 0x400
+#endif
+
 #ifndef PCI_CAP_ID_HOTPLUG
 /* SHPC Capability List Item reg group */
 #define PCI_CAP_ID_HOTPLUG      0x0C
index d28038a126d09182a1b049e79fc606dfe056fc44..4a54ba3378a72b6f40d98d4dc6dc792190f11086 100644 (file)
@@ -22,7 +22,7 @@
 #include "pt-msi.h"
 #include <sys/mman.h>
 
-static void msi_set_enable(struct pt_dev *dev, int en)
+void msi_set_enable(struct pt_dev *dev, int en)
 {
     uint16_t val = 0;
     uint32_t address = 0;
@@ -119,6 +119,7 @@ int pt_msi_update(struct pt_dev *d)
     uint8_t gvec = 0;
     uint32_t gflags = 0;
     uint64_t addr = 0;
+    int ret = 0;
 
     /* get vector, address, flags info, etc. */
     gvec = d->msi->data & 0xFF;
@@ -126,8 +127,20 @@ int pt_msi_update(struct pt_dev *d)
     gflags = __get_msi_gflags(d->msi->data, addr);
 
     PT_LOG("Update msi with pirq %x gvec %x\n", d->msi->pirq, gvec);
-    return xc_domain_update_msi_irq(xc_handle, domid, gvec,
+
+    ret = xc_domain_update_msi_irq(xc_handle, domid, gvec,
                                      d->msi->pirq, gflags, 0);
+
+    if (ret)
+    {
+        PT_LOG("Error: Binding of MSI failed.\n");
+
+        if (xc_physdev_unmap_pirq(xc_handle, domid, d->msi->pirq))
+            PT_LOG("Error: Unmapping of MSI failed.\n");
+        d->msi->pirq = -1;
+        return ret;
+    }
+    return 0;
 }
 
 void pt_msi_disable(struct pt_dev *dev)
@@ -222,6 +235,10 @@ int pt_enable_msi_translate(struct pt_dev* dev)
                                e_device, e_intx, 0))
     {
         PT_LOG("Error: MSI-INTx translation bind failed, fallback\n");
+
+        if (xc_physdev_unmap_pirq(xc_handle, domid, dev->msi->pirq))
+            PT_LOG("Error: Unmapping of MSI failed.\n");
+        dev->msi->pirq = -1;
         return -1;
     }
 
@@ -302,6 +319,10 @@ static int pt_msix_update_one(struct pt_dev *dev, int entry_nr)
     if ( ret )
     {
         PT_LOG("Error: Updating msix irq info for entry %d\n", entry_nr);
+
+        if (xc_physdev_unmap_pirq(xc_handle, domid, entry->pirq))
+            PT_LOG("Error: Unmapping of MSI-X failed.\n");
+        entry->pirq = -1;
         return ret;
     }
 
index 585f6072ba7843d95fe5f728da697ff50d541a97..9664f8908cfdbb626734167b605ba2bad49190b3 100644 (file)
@@ -76,6 +76,9 @@
 #define GLFAGS_SHIFT_DELIV_MODE     12
 #define GLFAGS_SHIFT_TRG_MODE       15
 
+void
+msi_set_enable(struct pt_dev *dev, int en);
+
 int
 pt_msi_setup(struct pt_dev *dev);