WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH 1/3] save/restore PCI configuration space in pciback.

To: Keir Fraser <keir.fraser@xxxxxxxxxxxxx>, Kouya Shimura <kouya@xxxxxxxxxxxxxx>, xen-devel@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-devel] [PATCH 1/3] save/restore PCI configuration space in pciback.
From: Yuji Shimada <shimada-yxb@xxxxxxxxxxxxxxx>
Date: Wed, 22 Apr 2009 11:18:54 +0900
Cc:
Delivery-date: Tue, 21 Apr 2009 19:20:14 -0700
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
In-reply-to: <20090422105816.32A4.27C06F64@xxxxxxxxxxxxxxx>
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
References: <20090422105816.32A4.27C06F64@xxxxxxxxxxxxxxx>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
This patch enables saving/restoring PCI configuration space in
pciback.

The timing of saving/restoring configuration space is like below.

  When pciback is bound to devices.
   - Pciback saves configuration space.

  When pciback is unbound to devices.
   - Pciback restores configuration space.

  When guest OS boots or a device is hotadded.
   - Pciback restores configuration space.
   - Pciback changes state of backend device to Initialised/Reconfigured.
   - Xend waits for the transition to Initialised/Reconfigured.

  When guest OS shutdowns or a device is hotremoved.
   - Pciback restores configuration space.
   - Xend resets devices.
     * If D-state of the device is not D0, the state is changed to D0
       before resetting the device.
   - Xend deassigns devices.

  * Essentially, devices should be reset before configuration space is
    restored. But it needs big modifications. Applying these patches,
    configuration space is restored when guest OS boots, a device is
    hotadded or pciback is unbound. So it has no matter.

The following registers are saved/restored by pciback.

  Configuration Space
   - Base Address Register set
   - Cache-line size Register
   - Latency timer Register
   - Enable SERR Bit / Enable PERR Bit in Control Register
   - Device Control Register (PCI Express Capability)
   - Link Control Register (PCI Express Capability)
   - Device Control 2 Register (PCI Express Capability)
   - Link Control 2 Register (PCI Express Capability)

  AER
   - Uncorrectable Error Mask Register
   - Uncorrectable Error Severity Register
   - Correctable Error Mask Register
   - Advanced Error Capabilities and Control Register

Thanks,
--
Yuji Shimada


Signed-off-by: Yuji Shimada <shimada-yxb@xxxxxxxxxxxxxxx>

diff -r 366c31f3ab4b drivers/xen/pciback/Makefile
--- a/drivers/xen/pciback/Makefile      Tue Apr 14 11:17:47 2009 +0100
+++ b/drivers/xen/pciback/Makefile      Tue Apr 21 16:48:25 2009 +0900
@@ -5,6 +5,7 @@
             conf_space_capability.o \
             conf_space_capability_vpd.o \
             conf_space_capability_pm.o \
+            conf_space_capability_exp.o \
              conf_space_quirks.o
 pciback-$(CONFIG_PCI_MSI) += conf_space_capability_msi.o
 pciback-$(CONFIG_XEN_PCIDEV_BACKEND_VPCI) += vpci.o
diff -r 366c31f3ab4b drivers/xen/pciback/conf_space.c
--- a/drivers/xen/pciback/conf_space.c  Tue Apr 14 11:17:47 2009 +0100
+++ b/drivers/xen/pciback/conf_space.c  Tue Apr 21 16:48:25 2009 +0900
@@ -375,6 +375,18 @@
        cfg_entry->field = field;
        cfg_entry->base_offset = base_offset;
 
+       /* When the size of the registers is different, by a version of the PCI,
+        * "is_need" function can prevent addition to the list of an unnecessary
+        * register.
+        */
+       if (field->is_need) {
+               err = field->is_need(dev, base_offset);
+               if (!err) {
+                       kfree(cfg_entry);
+                       goto out;
+               }
+       }
+
        /* silently ignore duplicate fields */
        err = pciback_field_is_dup(dev,OFFSET(cfg_entry));
        if (err)
@@ -433,3 +445,69 @@
 {
        return pciback_config_capability_init();
 }
+
+/* save AER one register */
+static int pciback_aer_save_one_register(struct pci_dev *dev, int offset)
+{
+       struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
+       return pci_read_config_dword(dev, (dev_data->config.aer_base + offset),
+               (u32*)&dev_data->config.config_aer[offset]);
+}
+
+/* save AER registers */
+int pciback_aer_reg_save(struct pci_dev *dev)
+{
+       int err = 0;
+
+       /* after reset, following register values should be restored.
+        * So, save them.
+        */
+       err = pciback_aer_save_one_register(dev, PCI_ERR_UNCOR_MASK);
+       if (err)
+               goto out;
+       err = pciback_aer_save_one_register(dev, PCI_ERR_UNCOR_SEVER);
+       if (err)
+               goto out;
+       err = pciback_aer_save_one_register(dev, PCI_ERR_COR_MASK);
+       if (err)
+               goto out;
+       err = pciback_aer_save_one_register(dev, PCI_ERR_CAP);
+
+      out:
+       return err;
+}
+
+/* restore AER one register */
+static int pciback_aer_restore_one_register(struct pci_dev *dev, int offset)
+{
+       struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
+       return pci_write_config_dword(dev,
+                                (dev_data->config.aer_base + offset),
+                                 *((u32*)&dev_data->config.config[offset]));
+}
+
+/* restore AER registers */
+int pciback_aer_reg_restore(struct pci_dev *dev)
+{
+       int err = 0;
+
+       /* the following registers should be reconfigured to correct values
+        * after reset. restore them.
+        * other registers should not be reconfigured after reset
+        * if there is no reason
+        */
+       err = pciback_aer_restore_one_register(dev, PCI_ERR_UNCOR_MASK);
+       if (err)
+               goto out;
+       err = pciback_aer_restore_one_register(dev, PCI_ERR_UNCOR_SEVER);
+       if (err)
+               goto out;
+       err = pciback_aer_restore_one_register(dev, PCI_ERR_COR_MASK);
+       if (err)
+               goto out;
+       err = pciback_aer_restore_one_register(dev, PCI_ERR_CAP);
+
+      out:
+       return err;
+}
+
diff -r 366c31f3ab4b drivers/xen/pciback/conf_space.h
--- a/drivers/xen/pciback/conf_space.h  Tue Apr 14 11:17:47 2009 +0100
+++ b/drivers/xen/pciback/conf_space.h  Tue Apr 21 16:48:25 2009 +0900
@@ -27,6 +27,10 @@
                               void *data);
 typedef int (*conf_byte_read) (struct pci_dev * dev, int offset, u8 * value,
                               void *data);
+typedef int (*conf_dword_restore)(struct pci_dev *dev, int offset, u32 data);
+typedef int (*conf_word_restore)(struct pci_dev *dev, int offset, u16 data);
+typedef int (*conf_byte_restore)(struct pci_dev *dev, int offset, u8 data);
+typedef int (*conf_field_is_need)(struct pci_dev *dev, int offset);
 
 /* These are the fields within the configuration space which we
  * are interested in intercepting reads/writes to and changing their
@@ -44,16 +48,20 @@
                struct {
                        conf_dword_write write;
                        conf_dword_read read;
+                       conf_dword_restore restore;
                } dw;
                struct {
                        conf_word_write write;
                        conf_word_read read;
+                       conf_word_restore restore;
                } w;
                struct {
                        conf_byte_write write;
                        conf_byte_read read;
+                       conf_byte_restore restore;
                } b;
        } u;
+       conf_field_is_need is_need;
        struct list_head list;
 };
 
@@ -123,4 +131,22 @@
 int pciback_config_header_add_fields(struct pci_dev *dev);
 int pciback_config_capability_add_fields(struct pci_dev *dev);
 
+static inline int pciback_restore_config_dword(struct pci_dev *dev, int offset,
+                                             u32 data)
+{
+       return pci_write_config_dword(dev, offset, data);
+}
+
+static inline int pciback_restore_config_word(struct pci_dev *dev, int offset,
+                                             u16 data)
+{
+       return pci_write_config_word(dev, offset, data);
+}
+
+static inline int pciback_restore_config_byte(struct pci_dev *dev, int offset,
+                                      u8 data)
+{
+       return pci_write_config_byte(dev, offset, data);
+}
+
 #endif                         /* __XEN_PCIBACK_CONF_SPACE_H__ */
diff -r 366c31f3ab4b drivers/xen/pciback/conf_space_capability.c
--- a/drivers/xen/pciback/conf_space_capability.c       Tue Apr 14 11:17:47 
2009 +0100
+++ b/drivers/xen/pciback/conf_space_capability.c       Tue Apr 21 16:48:25 
2009 +0900
@@ -59,11 +59,13 @@
 
 extern struct pciback_config_capability pciback_config_capability_vpd;
 extern struct pciback_config_capability pciback_config_capability_pm;
+extern struct pciback_config_capability pciback_config_capability_exp;
 
 int pciback_config_capability_init(void)
 {
        register_capability(&pciback_config_capability_vpd);
        register_capability(&pciback_config_capability_pm);
+       register_capability(&pciback_config_capability_exp);
 
        return 0;
 }
diff -r 366c31f3ab4b drivers/xen/pciback/conf_space_capability_exp.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/xen/pciback/conf_space_capability_exp.c   Tue Apr 21 16:48:25 
2009 +0900
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2009, NEC Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+/*
+ * PCI Backend -- Configuration overlay for PCI Express capability
+ */
+#include <linux/pci.h>
+#include "conf_space.h"
+#include "conf_space_capability.h"
+
+/* PCI express version 1.0 does not have the registers over offset 24H */
+static int pciback_exp_is_need_config2(struct pci_dev *dev, int base_offset)
+{
+       u16 cap;
+       pci_read_config_word(dev, base_offset + PCI_EXP_FLAGS, &cap);
+       if ((cap & PCI_EXP_FLAGS_VERS) == 1)
+               return 0;
+       else
+               return 1;
+}
+
+static const struct config_field caplist_exp[] = {
+       {
+               .offset      = PCI_EXP_DEVCTL,
+               .size        = 2,
+               .u.w.restore = pciback_restore_config_word,
+       },
+       {
+               .offset      = PCI_EXP_LNKCTL,
+               .size        = 2,
+               .u.w.restore = pciback_restore_config_word,
+       },
+       {
+               .offset      = PCI_EXP_DEVCTL2,
+               .size        = 2,
+               .u.w.restore = pciback_restore_config_word,
+               .is_need     = pciback_exp_is_need_config2,
+       },
+       {
+               .offset      = PCI_EXP_LNKCTL2,
+               .size        = 2,
+               .u.w.restore = pciback_restore_config_word,
+               .is_need     = pciback_exp_is_need_config2,
+       },
+       {}
+};
+
+struct pciback_config_capability pciback_config_capability_exp = {
+       .capability = PCI_CAP_ID_EXP,
+       .fields = caplist_exp,
+};
+
diff -r 366c31f3ab4b drivers/xen/pciback/conf_space_capability_pm.c
--- a/drivers/xen/pciback/conf_space_capability_pm.c    Tue Apr 14 11:17:47 
2009 +0100
+++ b/drivers/xen/pciback/conf_space_capability_pm.c    Tue Apr 21 16:48:25 
2009 +0900
@@ -94,6 +94,27 @@
        return ERR_PTR(err);
 }
 
+/* restore Power Management Control/Status register */
+static int pm_ctrl_restore(struct pci_dev *dev, int offset, u16 data)
+{
+       int err;
+       u16 value;
+
+       err = pci_read_config_word(dev, offset, &value);
+       if (err)
+               goto out;
+
+       /* No need to restore, just clear PME Enable and PME Status bit
+        * Note: register type of PME Status bit is RW1C, so clear by writing 1b
+        */
+       value = (value & ~PCI_PM_CTRL_PME_ENABLE) | PCI_PM_CTRL_PME_STATUS;
+
+       err = pci_write_config_word(dev, offset, value);
+
+      out:
+       return err;
+}
+
 static const struct config_field caplist_pm[] = {
        {
                .offset     = PCI_PM_PMC,
@@ -106,6 +127,7 @@
                .init       = pm_ctrl_init,
                .u.w.read   = pciback_read_config_word,
                .u.w.write  = pm_ctrl_write,
+               .u.w.restore = pm_ctrl_restore,
        },
        {
                .offset     = PCI_PM_PPB_EXTENSIONS,
diff -r 366c31f3ab4b drivers/xen/pciback/conf_space_header.c
--- a/drivers/xen/pciback/conf_space_header.c   Tue Apr 14 11:17:47 2009 +0100
+++ b/drivers/xen/pciback/conf_space_header.c   Tue Apr 21 16:48:25 2009 +0900
@@ -210,12 +210,34 @@
        return err;
 }
 
+#define CONF_COMMAND_MASK (PCI_COMMAND_PARITY | PCI_COMMAND_SERR)
+
+static int command_restore(struct pci_dev *dev, int offset, u16 data)
+{
+       int err;
+       u16 tmp_val;
+
+       err = pci_read_config_word(dev, offset, &tmp_val);
+       if (err)
+               goto out;
+
+       tmp_val &= ~CONF_COMMAND_MASK;
+       tmp_val |= (data & CONF_COMMAND_MASK);
+
+       err = pci_write_config_word(dev, offset, tmp_val);
+
+      out:
+       return err;
+}
+
+
 static const struct config_field header_common[] = {
        {
         .offset    = PCI_COMMAND,
         .size      = 2,
         .u.w.read  = pciback_read_config_word,
         .u.w.write = command_write,
+        .u.w.restore = command_restore,
        },
        {
         .offset    = PCI_INTERRUPT_LINE,
@@ -233,11 +255,13 @@
         .size      = 1,
         .u.b.read  = pciback_read_config_byte,
         .u.b.write = pciback_write_config_byte,
+        .u.b.restore = pciback_restore_config_byte,
        },
        {
         .offset    = PCI_LATENCY_TIMER,
         .size      = 1,
         .u.b.read  = pciback_read_config_byte,
+        .u.b.restore = pciback_restore_config_byte,
        },
        {
         .offset    = PCI_BIST,
@@ -257,6 +281,7 @@
         .release    = bar_release,                     \
         .u.dw.read  = bar_read,                        \
         .u.dw.write = bar_write,                       \
+        .u.dw.restore = pciback_restore_config_dword,  \
         }
 
 #define CFG_FIELD_ROM(reg_offset)                      \
@@ -268,6 +293,7 @@
         .release    = bar_release,                     \
         .u.dw.read  = bar_read,                        \
         .u.dw.write = rom_write,                       \
+        .u.dw.restore = pciback_restore_config_dword,  \
         }
 
 static const struct config_field header_0[] = {
diff -r 366c31f3ab4b drivers/xen/pciback/pci_stub.c
--- a/drivers/xen/pciback/pci_stub.c    Tue Apr 14 11:17:47 2009 +0100
+++ b/drivers/xen/pciback/pci_stub.c    Tue Apr 21 16:48:25 2009 +0900
@@ -135,6 +135,91 @@
        return psdev;
 }
 
+int pcistub_save_config_space(struct pci_dev *dev)
+{
+       int err = 0;
+       int offset;
+       u8  value;
+       struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
+
+       /* save configuration space */
+       for (offset = 0; offset < 256; offset++) {
+               err = pci_read_config_byte(dev, offset, &value);
+               if (err) {
+                       dev_err(&dev->dev, 
+                               "(%s) Read error in config space[%x]!\n",
+                               __func__, offset);
+                       goto out;
+               }
+               dev_data->config.config[offset] = value;
+       }
+       dev_dbg(&dev->dev, "Save real configuration space \n");
+
+       dev_data->config.aer_base = 
+                       (u32)pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+       if (!dev_data->config.aer_base)
+               goto out;
+
+       /* save advanced error reporting registers */
+       err = pciback_aer_reg_save(dev);
+       dev_dbg(&dev->dev, "Save advanced error reporting[%d]\n", err);
+
+      out:
+       return err;
+}
+
+int pcistub_restore_config_space(struct pci_dev *dev)
+{
+       int err = 0;
+       struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
+       struct config_field_entry *cfg_entry;
+       const struct config_field *field;
+       int offset;
+
+       /* Restore configuration space */
+       list_for_each_entry(cfg_entry, &dev_data->config_fields, list) {
+               field = cfg_entry->field;
+               offset = OFFSET(cfg_entry);
+               switch (field->size) {
+               case 1:
+                       if (!field->u.b.restore)
+                               continue;
+                       err = field->u.b.restore(dev,
+                               offset, dev_data->config.config[offset]);
+                       break;
+               case 2:
+                       if (!field->u.w.restore)
+                               continue;
+                       err = field->u.w.restore(dev, offset,
+                               *((u16 *)&dev_data->config.config[offset]));
+                       break;
+               case 4:
+                       if (!field->u.dw.restore)
+                               continue;
+                       err = field->u.dw.restore(dev, offset,
+                               *((u32 *)&dev_data->config.config[offset]));
+                       break;
+               }
+               if (err) {
+                       dev_err(&dev->dev, 
+                               "(%s) Restore error<%d> in config space"
+                               "at offset 0x%x!\n", __func__, err, offset);
+                       goto out;
+               }
+       }
+       dev_dbg(&dev->dev, "Restore base configuration space \n");
+
+       if (!dev_data->config.aer_base)
+               goto out;
+
+       /* restore advanced error reporting registers */
+       err = pciback_aer_reg_restore(dev);
+       dev_dbg(&dev->dev, "Restore advanced error reporting\n");
+
+      out:
+       return err;
+}
+
 static struct pci_dev *pcistub_device_get_pci_dev(struct pciback_device *pdev,
                                                  struct pcistub_device *psdev)
 {
@@ -292,6 +377,10 @@
        }
        pci_set_drvdata(dev, dev_data);
 
+       /* Save configuration space */
+       dev_dbg(&dev->dev, "Save configuration space\n");
+       pcistub_save_config_space(dev);
+
        dev_dbg(&dev->dev, "initializing config\n");
 
        init_waitqueue_head(&aer_wait_queue);
diff -r 366c31f3ab4b drivers/xen/pciback/pciback.h
--- a/drivers/xen/pciback/pciback.h     Tue Apr 14 11:17:47 2009 +0100
+++ b/drivers/xen/pciback/pciback.h     Tue Apr 21 16:48:25 2009 +0900
@@ -44,10 +44,17 @@
        struct work_struct op_work;
 };
 
+struct pciback_config {
+       u8  config[256];
+       u32 aer_base;
+       u8  config_aer[72];
+};
+
 struct pciback_dev_data {
        struct list_head config_fields;
        int permissive;
        int warned_on_write;
+       struct pciback_config config;
 };
 
 /* Get/Put PCI Devices that are hidden from the PCI Backend Domain */
@@ -122,5 +129,12 @@
 extern int verbose_request;
 
 void test_and_schedule_op(struct pciback_device *pdev);
+
+extern int pciback_aer_reg_save(struct pci_dev *dev);
+extern int pciback_aer_reg_restore(struct pci_dev *dev);
+
+extern int pcistub_save_config_space(struct pci_dev *dev);
+extern int pcistub_restore_config_space(struct pci_dev *dev);
+
 #endif
 
diff -r 366c31f3ab4b drivers/xen/pciback/pciback_ops.c
--- a/drivers/xen/pciback/pciback_ops.c Tue Apr 14 11:17:47 2009 +0100
+++ b/drivers/xen/pciback/pciback_ops.c Tue Apr 21 16:48:25 2009 +0900
@@ -20,6 +20,9 @@
 {
        u16 cmd;
 
+       /* restore configuration space */
+       pcistub_restore_config_space(dev);
+
        /* Disable devices (but not bridges) */
        if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL) {
                pci_disable_device(dev);
diff -r 366c31f3ab4b drivers/xen/pciback/xenbus.c
--- a/drivers/xen/pciback/xenbus.c      Tue Apr 14 11:17:47 2009 +0100
+++ b/drivers/xen/pciback/xenbus.c      Tue Apr 21 16:48:25 2009 +0900
@@ -43,6 +43,30 @@
        return pdev;
 }
 
+static int pciback_reinit_device(struct pciback_device *pdev,
+                              int domain, int bus, int slot, int func)
+{
+       int err = 0;
+       struct pci_dev *dev;
+
+       dev_dbg(&pdev->xdev->dev, "Reinitializing dom %x bus %x slot %x"
+               " func %x\n", domain, bus, slot, func);
+
+       dev = pciback_get_pci_dev(pdev, domain, bus, PCI_DEVFN(slot, func));
+       if (!dev) {
+               err = -EINVAL;
+               dev_dbg(&pdev->xdev->dev, "Couldn't locate PCI device "
+                       "(%04x:%02x:%02x.%01x)! not owned by this domain\n",
+                       domain, bus, slot, func);
+               goto out;
+       }
+
+       pciback_reset_device(dev);
+       
+      out:
+       return err;
+}
+
 static void pciback_disconnect(struct pciback_device *pdev)
 {
        spin_lock(&pdev->dev_lock);
@@ -394,6 +418,15 @@
                        if (err)
                                goto out;
 
+                       err = pciback_reinit_device(pdev, domain, bus, slot,
+                                                 func);
+                       if (err) {
+                               xenbus_dev_fatal(pdev->xdev, err,
+                                                "Error reinitialize pci device"
+                                                "configuration");
+                               goto out;
+                       }
+
                        /* Publish pci roots. */
                        err = pciback_publish_pci_roots(pdev, 
pciback_publish_pci_root);
                        if (err) {
@@ -575,6 +608,14 @@
                if (err)
                        goto out;
 
+               err = pciback_reinit_device(pdev, domain, bus, slot, func);
+               if (err) {
+                       xenbus_dev_fatal(pdev->xdev, err,
+                                        "Error reinitialize pci device "
+                                        "configuration");
+                       goto out;
+               }
+
                /* Switch substate of this device. */
                l = snprintf(state_str, sizeof(state_str), "state-%d", i);
                if (unlikely(l >= (sizeof(state_str) - 1))) {
@@ -627,6 +668,10 @@
                pciback_setup_backend(pdev);
                break;
 
+       case XenbusStateReconfiguring:
+               pciback_reconfigure(pdev);
+               break;
+
        default:
                break;
        }

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel