[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] This patch adds the ability to have restricted write access to some



# HG changeset patch
# User kaf24@xxxxxxxxxxxxxxxxxxxx
# Node ID e42ed43320535a13005c5cce7f3afb382d9c2af5
# Parent  d66dfd584d2f6573103172e4544309e87244631d
This patch adds the ability to have restricted write access to some
of the structures on the capability list. Specifically, this patch adds
support for reading data through the Vital Product Data capability
structure and for manipulating power management of a card. A driver
domain is not allowed to enable Power Management Events (the PME trace
may be shared across devices in many domains), but it can put a device
in its control to sleep or query it for power usage statistics. This
code could possibly be expanded in the future to add support for AGP,
PCI-X, and MSI/MSIX (all of which are controlled through structures on
the capability list).

This patch also corrects some formatting issues in the PCI backend and
adds some comments to the code regarding permissive mode.

Signed-off-by: Ryan Wilson <hap9@xxxxxxxxxxxxxx>

diff -r d66dfd584d2f -r e42ed4332053 
linux-2.6-xen-sparse/drivers/xen/pciback/Makefile
--- a/linux-2.6-xen-sparse/drivers/xen/pciback/Makefile Thu Apr 27 09:47:05 
2006 +0100
+++ b/linux-2.6-xen-sparse/drivers/xen/pciback/Makefile Thu Apr 27 09:49:48 
2006 +0100
@@ -1,7 +1,10 @@ obj-$(CONFIG_XEN_PCIDEV_BACKEND) += pcib
 obj-$(CONFIG_XEN_PCIDEV_BACKEND) += pciback.o
 
 pciback-y := pci_stub.o pciback_ops.o xenbus.o
-pciback-y += conf_space.o conf_space_header.o
+pciback-y += conf_space.o conf_space_header.o \
+            conf_space_capability.o \
+            conf_space_capability_vpd.o \
+            conf_space_capability_pm.o
 pciback-$(CONFIG_XEN_PCIDEV_BACKEND_VPCI) += vpci.o
 pciback-$(CONFIG_XEN_PCIDEV_BACKEND_PASS) += passthrough.o
 
diff -r d66dfd584d2f -r e42ed4332053 
linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.c
--- a/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.c     Thu Apr 27 
09:47:05 2006 +0100
+++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.c     Thu Apr 27 
09:49:48 2006 +0100
@@ -17,10 +17,10 @@ static int permissive = 0;
 static int permissive = 0;
 module_param(permissive, bool, 0644);
 
-#define DEFINE_PCI_CONFIG(op,size,type)                                        
\
-int pciback_##op##_config_##size                                               
        \
+#define DEFINE_PCI_CONFIG(op,size,type)                        \
+int pciback_##op##_config_##size                               \
 (struct pci_dev *dev, int offset, type value, void *data)      \
-{                                                                              
                                        \
+{                                                              \
        return pci_##op##_config_##size (dev, offset, value);   \
 }
 
@@ -175,8 +175,8 @@ int pciback_config_read(struct pci_dev *
 
                req_start = offset;
                req_end = offset + size;
-               field_start = field->offset;
-               field_end = field->offset + field->size;
+               field_start = OFFSET(cfg_entry);
+               field_end = OFFSET(cfg_entry) + field->size;
 
                if ((req_start >= field_start && req_start < field_end)
                    || (req_end > field_start && req_end <= field_end)) {
@@ -222,8 +222,8 @@ int pciback_config_write(struct pci_dev 
 
                req_start = offset;
                req_end = offset + size;
-               field_start = field->offset;
-               field_end = field->offset + field->size;
+               field_start = OFFSET(cfg_entry);
+               field_end = OFFSET(cfg_entry) + field->size;
 
                if ((req_start >= field_start && req_start < field_end)
                    || (req_end > field_start && req_end <= field_end)) {
@@ -239,60 +239,99 @@ int pciback_config_write(struct pci_dev 
 
                        err = conf_space_write(dev, cfg_entry, field_start,
                                               tmp_val);
+
+                       /* handled is set true here, but not every byte
+                        * may have been written! Properly detecting if
+                        * every byte is handled is unnecessary as the
+                        * flag is used to detect devices that need
+                        * special helpers to work correctly.
+                        */
                        handled = 1;
                }
        }
 
-       if (!handled && !err && permissive) {
-               switch (size) {
-               case 1:
-                       err = pci_write_config_byte(dev, offset, (u8)value);
-                       break;
-               case 2:
-                       err = pci_write_config_word(dev, offset, (u16)value);
-                       break;
-               case 4:
-                       err = pci_write_config_dword(dev, offset, (u32)value);
-                       break;
+       if (!handled && !err) {
+               /* By default, anything not specificially handled above is
+                * read-only. The permissive flag changes this behavior so
+                * that anything not specifically handled above is writable.
+                * This means that some fields may still be read-only because
+                * they have entries in the config_field list that intercept
+                * the write and do nothing. */
+               if (permissive) {
+                       switch (size) {
+                       case 1:
+                               err = pci_write_config_byte(dev, offset,
+                                                           (u8)value);
+                               break;
+                       case 2:
+                               err = pci_write_config_word(dev, offset,
+                                                           (u16)value);
+                               break;
+                       case 4:
+                               err = pci_write_config_dword(dev, offset,
+                                                            (u32)value);
+                               break;
+                       }
+               } else if (!dev_data->warned_on_write) {
+                       dev_data->warned_on_write = 1;
+                       dev_warn(&dev->dev, "Driver wrote to a read-only "
+                                "configuration space field!\n");
+                       dev_warn(&dev->dev, "Write at offset 0x%x size %d\n",
+                               offset, size);
+                       dev_warn(&dev->dev, "This may be harmless, but if\n");
+                       dev_warn(&dev->dev, "you have problems with your "
+                                "device:\n");
+                       dev_warn(&dev->dev, "1) see the permissive "
+                                "attribute in sysfs.\n");
+                       dev_warn(&dev->dev, "2) report problems to the "
+                                "xen-devel mailing list along\n");
+                       dev_warn(&dev->dev, "   with details of your device "
+                                "obtained from lspci.\n");
                }
        }
 
        return pcibios_err_to_errno(err);
 }
 
-void pciback_config_reset(struct pci_dev *dev)
+void pciback_config_reset_dev(struct pci_dev *dev)
 {
        struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
        struct config_field_entry *cfg_entry;
        struct config_field *field;
 
+       dev_dbg(&dev->dev, "resetting virtual configuration space\n");
+
        list_for_each_entry(cfg_entry, &dev_data->config_fields, list) {
                field = cfg_entry->field;
 
                if (field->reset)
-                       field->reset(dev, field->offset, cfg_entry->data);
-       }
-}
-
-void pciback_config_free(struct pci_dev *dev)
+                       field->reset(dev, OFFSET(cfg_entry), cfg_entry->data);
+       }
+}
+
+void pciback_config_free_dev(struct pci_dev *dev)
 {
        struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
        struct config_field_entry *cfg_entry, *t;
        struct config_field *field;
 
+       dev_dbg(&dev->dev, "free-ing virtual configuration space fields\n");
+
        list_for_each_entry_safe(cfg_entry, t, &dev_data->config_fields, list) {
                list_del(&cfg_entry->list);
 
                field = cfg_entry->field;
 
                if (field->release)
-                       field->release(dev, field->offset, cfg_entry->data);
+                       field->release(dev, OFFSET(cfg_entry), cfg_entry->data);
 
                kfree(cfg_entry);
        }
 }
 
-int pciback_config_add_field(struct pci_dev *dev, struct config_field *field)
+int pciback_config_add_field_offset(struct pci_dev *dev,
+                                   struct config_field *field,
+                                   unsigned int offset)
 {
        int err = 0;
        struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
@@ -307,9 +346,10 @@ int pciback_config_add_field(struct pci_
 
        cfg_entry->data = NULL;
        cfg_entry->field = field;
+       cfg_entry->base_offset = offset;
 
        if (field->init) {
-               tmp = field->init(dev, field->offset);
+               tmp = field->init(dev, OFFSET(cfg_entry));
 
                if (IS_ERR(tmp)) {
                        err = PTR_ERR(tmp);
@@ -319,6 +359,8 @@ int pciback_config_add_field(struct pci_
                cfg_entry->data = tmp;
        }
 
+       dev_dbg(&dev->dev, "added config field at offset 0x%02x\n",
+               OFFSET(cfg_entry));
        list_add_tail(&cfg_entry->list, &dev_data->config_fields);
 
       out:
@@ -332,14 +374,30 @@ int pciback_config_add_field(struct pci_
  * certain registers (like the base address registers (BARs) so that we can
  * keep the client from manipulating them directly.
  */
-int pciback_config_init(struct pci_dev *dev)
+int pciback_config_init_dev(struct pci_dev *dev)
 {
        int err = 0;
        struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
 
+       dev_dbg(&dev->dev, "initializing virtual configuration space\n");
+
        INIT_LIST_HEAD(&dev_data->config_fields);
 
        err = pciback_config_header_add_fields(dev);
-
+       if (err)
+               goto out;
+
+       err = pciback_config_capability_add_fields(dev);
+
+      out:
        return err;
 }
+
+int pciback_config_init(void)
+{
+       int err;
+
+       err = pciback_config_capability_init();
+
+       return err;
+}
diff -r d66dfd584d2f -r e42ed4332053 
linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.h
--- a/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.h     Thu Apr 27 
09:47:05 2006 +0100
+++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.h     Thu Apr 27 
09:49:48 2006 +0100
@@ -8,7 +8,9 @@
 #define __XEN_PCIBACK_CONF_SPACE_H__
 
 #include <linux/list.h>
+#include <linux/err.h>
 
+/* conf_field_init can return an errno in a ptr with ERR_PTR() */
 typedef void *(*conf_field_init) (struct pci_dev * dev, int offset);
 typedef void (*conf_field_reset) (struct pci_dev * dev, int offset, void 
*data);
 typedef void (*conf_field_free) (struct pci_dev * dev, int offset, void *data);
@@ -55,13 +57,25 @@ struct config_field_entry {
 struct config_field_entry {
        struct list_head list;
        struct config_field *field;
+       unsigned int base_offset;
        void *data;
 };
+
+#define OFFSET(cfg_entry) ((cfg_entry)->base_offset+(cfg_entry)->field->offset)
 
 /* Add fields to a device - the add_fields macro expects to get a pointer to
  * the first entry in an array (of which the ending is marked by size==0)
  */
-int pciback_config_add_field(struct pci_dev *dev, struct config_field *field);
+int pciback_config_add_field_offset(struct pci_dev *dev,
+                                   struct config_field *field,
+                                   unsigned int offset);
+
+static inline int pciback_config_add_field(struct pci_dev *dev,
+                                          struct config_field *field)
+{
+       return pciback_config_add_field_offset(dev, field, 0);
+}
+
 static inline int pciback_config_add_fields(struct pci_dev *dev,
                                            struct config_field *field)
 {
@@ -74,11 +88,18 @@ static inline int pciback_config_add_fie
        return err;
 }
 
-/* Initializers which add fields to the virtual configuration space
- * ** We could add initializers to allow a guest domain to touch
- * the capability lists (for power management, the AGP bridge, etc.)
- */
-int pciback_config_header_add_fields(struct pci_dev *dev);
+static inline int pciback_config_add_fields_offset(struct pci_dev *dev,
+                                                  struct config_field *field,
+                                                  unsigned int offset)
+{
+       int i, err = 0;
+       for (i = 0; field[i].size != 0; i++) {
+               err = pciback_config_add_field_offset(dev, &field[i], offset);
+               if (err)
+                       break;
+       }
+       return err;
+}
 
 /* Read/Write the real configuration space */
 int pciback_read_config_byte(struct pci_dev *dev, int offset, u8 * value,
@@ -94,4 +115,9 @@ int pciback_write_config_dword(struct pc
 int pciback_write_config_dword(struct pci_dev *dev, int offset, u32 value,
                               void *data);
 
+int pciback_config_capability_init(void);
+
+int pciback_config_header_add_fields(struct pci_dev *dev);
+int pciback_config_capability_add_fields(struct pci_dev *dev);
+
 #endif                         /* __XEN_PCIBACK_CONF_SPACE_H__ */
diff -r d66dfd584d2f -r e42ed4332053 
linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_header.c
--- a/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_header.c      Thu Apr 
27 09:47:05 2006 +0100
+++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_header.c      Thu Apr 
27 09:49:48 2006 +0100
@@ -169,29 +169,61 @@ static int interrupt_read(struct pci_dev
        return 0;
 }
 
-struct config_field header_common[] = {
+static int bist_write(struct pci_dev *dev, int offset, u8 value, void *data)
+{
+       u8 cur_value;
+       int err;
+
+       err = pci_read_config_byte(dev, offset, &cur_value);
+       if (err)
+               goto out;
+
+       if ((cur_value & ~PCI_BIST_START) == (value & ~PCI_BIST_START)
+           || value == PCI_BIST_START)
+               err = pci_write_config_byte(dev, offset, value);
+
+      out:
+       return err;
+}
+
+static struct config_field header_common[] = {
        {
         .offset    = PCI_COMMAND,
         .size      = 2,
         .u.w.read  = pciback_read_config_word,
         .u.w.write = command_write,
-        },
+       },
        {
         .offset    = PCI_INTERRUPT_LINE,
         .size      = 1,
         .u.b.read  = interrupt_read,
-        .u.b.write = NULL,
-        },
+       },
+       {
+        .offset    = PCI_INTERRUPT_PIN,
+        .size      = 1,
+        .u.b.read  = pciback_read_config_byte,
+       },
        {
         /* Any side effects of letting driver domain control cache line? */
         .offset    = PCI_CACHE_LINE_SIZE,
         .size      = 1,
         .u.b.read  = pciback_read_config_byte,
         .u.b.write = pciback_write_config_byte,
-        },
-       {
-        .size = 0,
-        },
+       },
+       {
+        .offset    = PCI_LATENCY_TIMER,
+        .size      = 1,
+        .u.b.read  = pciback_read_config_byte,
+       },
+       {
+        .offset    = PCI_BIST,
+        .size      = 1,
+        .u.b.read  = pciback_read_config_byte,
+        .u.b.write = bist_write,
+       },
+       {
+        .size = 0,
+       },
 };
 
 #define CFG_FIELD_BAR(reg_offset)                      \
@@ -216,7 +248,7 @@ struct config_field header_common[] = {
         .u.dw.write = rom_write,                       \
         }
 
-struct config_field header_0[] = {
+static struct config_field header_0[] = {
        CFG_FIELD_BAR(PCI_BASE_ADDRESS_0),
        CFG_FIELD_BAR(PCI_BASE_ADDRESS_1),
        CFG_FIELD_BAR(PCI_BASE_ADDRESS_2),
@@ -226,16 +258,16 @@ struct config_field header_0[] = {
        CFG_FIELD_ROM(PCI_ROM_ADDRESS),
        {
         .size = 0,
-        },
-};
-
-struct config_field header_1[] = {
+       },
+};
+
+static struct config_field header_1[] = {
        CFG_FIELD_BAR(PCI_BASE_ADDRESS_0),
        CFG_FIELD_BAR(PCI_BASE_ADDRESS_1),
        CFG_FIELD_ROM(PCI_ROM_ADDRESS1),
        {
         .size = 0,
-        },
+       },
 };
 
 int pciback_config_header_add_fields(struct pci_dev *dev)
diff -r d66dfd584d2f -r e42ed4332053 
linux-2.6-xen-sparse/drivers/xen/pciback/pci_stub.c
--- a/linux-2.6-xen-sparse/drivers/xen/pciback/pci_stub.c       Thu Apr 27 
09:47:05 2006 +0100
+++ b/linux-2.6-xen-sparse/drivers/xen/pciback/pci_stub.c       Thu Apr 27 
09:49:48 2006 +0100
@@ -76,7 +76,7 @@ static void pcistub_device_release(struc
 
        /* Clean-up the device */
        pciback_reset_device(psdev->dev);
-       pciback_config_free(psdev->dev);
+       pciback_config_free_dev(psdev->dev);
        kfree(pci_get_drvdata(psdev->dev));
        pci_set_drvdata(psdev->dev, NULL);
 
@@ -180,7 +180,7 @@ void pcistub_put_pci_dev(struct pci_dev 
         * (so it's ready for the next domain)
         */
        pciback_reset_device(found_psdev->dev);
-       pciback_config_reset(found_psdev->dev);
+       pciback_config_reset_dev(found_psdev->dev);
 
        spin_lock_irqsave(&found_psdev->lock, flags);
        found_psdev->pdev = NULL;
@@ -235,7 +235,7 @@ static int __devinit pcistub_init_device
         * would need to be called somewhere to free the memory allocated
         * here and then to call kfree(pci_get_drvdata(psdev->dev)).
         */
-       dev_data = kmalloc(sizeof(*dev_data), GFP_ATOMIC);
+       dev_data = kzalloc(sizeof(*dev_data), GFP_ATOMIC);
        if (!dev_data) {
                err = -ENOMEM;
                goto out;
@@ -243,7 +243,7 @@ static int __devinit pcistub_init_device
        pci_set_drvdata(dev, dev_data);
 
        dev_dbg(&dev->dev, "initializing config\n");
-       err = pciback_config_init(dev);
+       err = pciback_config_init_dev(dev);
        if (err)
                goto out;
 
@@ -268,7 +268,7 @@ static int __devinit pcistub_init_device
        return 0;
 
       config_release:
-       pciback_config_free(dev);
+       pciback_config_free_dev(dev);
 
       out:
        pci_set_drvdata(dev, NULL);
@@ -324,40 +324,31 @@ static int __devinit pcistub_seize(struc
 {
        struct pcistub_device *psdev;
        unsigned long flags;
-       int initialize_devices_copy;
        int err = 0;
 
        psdev = pcistub_device_alloc(dev);
        if (!psdev)
                return -ENOMEM;
 
-       /* initialize_devices has to be accessed under a spin lock. But since
-        * it can only change from 0 -> 1, if it's already 1, we don't have to
-        * worry about it changing. That's why we can take a *copy* of
-        * initialize_devices and wait till we're outside of the lock to
-        * check if it's 1 (don't ever check if it's 0 outside of the lock)
-        */
        spin_lock_irqsave(&pcistub_devices_lock, flags);
 
-       initialize_devices_copy = initialize_devices;
-
-       if (!initialize_devices_copy) {
+       if (initialize_devices) {
+               spin_unlock_irqrestore(&pcistub_devices_lock, flags);
+
+               /* don't want irqs disabled when calling pcistub_init_device */
+               err = pcistub_init_device(psdev->dev);
+
+               spin_lock_irqsave(&pcistub_devices_lock, flags);
+
+               if (!err)
+                       list_add(&psdev->dev_list, &pcistub_devices);
+       } else {
                dev_dbg(&dev->dev, "deferring initialization\n");
                list_add(&psdev->dev_list, &seized_devices);
        }
 
        spin_unlock_irqrestore(&pcistub_devices_lock, flags);
 
-       if (initialize_devices_copy) {
-               /* don't want irqs disabled when calling pcistub_init_device */
-               err = pcistub_init_device(psdev->dev);
-               if (err)
-                       goto out;
-
-               list_add(&psdev->dev_list, &pcistub_devices);
-       }
-
-      out:
        if (err)
                pcistub_device_put(psdev);
 
@@ -663,9 +654,13 @@ fs_initcall(pcistub_init);
 
 static int __init pciback_init(void)
 {
+       int err;
+
+       err = pciback_config_init();
+       if (err)
+               return err;
+
 #ifdef MODULE
-       int err;
-
        err = pcistub_init();
        if (err < 0)
                return err;
diff -r d66dfd584d2f -r e42ed4332053 
linux-2.6-xen-sparse/drivers/xen/pciback/pciback.h
--- a/linux-2.6-xen-sparse/drivers/xen/pciback/pciback.h        Thu Apr 27 
09:47:05 2006 +0100
+++ b/linux-2.6-xen-sparse/drivers/xen/pciback/pciback.h        Thu Apr 27 
09:49:48 2006 +0100
@@ -44,6 +44,7 @@ struct pciback_device {
 
 struct pciback_dev_data {
        struct list_head config_fields;
+       int warned_on_write;
 };
 
 /* Get/Put PCI Devices that are hidden from the PCI Backend Domain */
@@ -58,9 +59,10 @@ void pciback_reset_device(struct pci_dev
 void pciback_reset_device(struct pci_dev *pdev);
 
 /* Access a virtual configuration space for a PCI device */
-int pciback_config_init(struct pci_dev *dev);
-void pciback_config_reset(struct pci_dev *dev);
-void pciback_config_free(struct pci_dev *dev);
+int pciback_config_init(void);
+int pciback_config_init_dev(struct pci_dev *dev);
+void pciback_config_reset_dev(struct pci_dev *dev);
+void pciback_config_free_dev(struct pci_dev *dev);
 int pciback_config_read(struct pci_dev *dev, int offset, int size,
                        u32 * ret_val);
 int pciback_config_write(struct pci_dev *dev, int offset, int size, u32 value);
diff -r d66dfd584d2f -r e42ed4332053 
linux-2.6-xen-sparse/drivers/xen/pciback/pciback_ops.c
--- a/linux-2.6-xen-sparse/drivers/xen/pciback/pciback_ops.c    Thu Apr 27 
09:47:05 2006 +0100
+++ b/linux-2.6-xen-sparse/drivers/xen/pciback/pciback_ops.c    Thu Apr 27 
09:49:48 2006 +0100
@@ -36,8 +36,6 @@ void pciback_reset_device(struct pci_dev
                        dev->is_busmaster = 0;
                }
        }
-
-       pciback_config_reset(dev);
 }
 
 static inline void test_and_schedule_op(struct pciback_device *pdev)
diff -r d66dfd584d2f -r e42ed4332053 
linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability.c  Thu Apr 
27 09:49:48 2006 +0100
@@ -0,0 +1,71 @@
+/*
+ * PCI Backend - Handles the virtual fields found on the capability lists
+ *               in the configuration space.
+ *
+ * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx>
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include "pciback.h"
+#include "conf_space.h"
+#include "conf_space_capability.h"
+
+static LIST_HEAD(capabilities);
+
+static struct config_field caplist_header[] = {
+       {
+        .offset    = PCI_CAP_LIST_ID,
+        .size      = 2, /* encompass PCI_CAP_LIST_ID & PCI_CAP_LIST_NEXT */
+        .u.w.read  = pciback_read_config_word,
+        .u.w.write = NULL,
+       },
+       {
+        .size = 0,
+       },
+};
+
+static inline void register_capability(struct pciback_config_capability *cap)
+{
+       list_add_tail(&cap->cap_list, &capabilities);
+}
+
+int pciback_config_capability_add_fields(struct pci_dev *dev)
+{
+       int err = 0;
+       struct pciback_config_capability *cap;
+       int cap_offset;
+
+       list_for_each_entry(cap, &capabilities, cap_list) {
+               cap_offset = pci_find_capability(dev, cap->capability);
+               if (cap_offset) {
+                       dev_dbg(&dev->dev, "Found capability 0x%x at 0x%x\n",
+                               cap->capability, cap_offset);
+
+                       err = pciback_config_add_fields_offset(dev,
+                                                              caplist_header,
+                                                              cap_offset);
+                       if (err)
+                               goto out;
+                       err = pciback_config_add_fields_offset(dev,
+                                                              cap->fields,
+                                                              cap_offset);
+                       if (err)
+                               goto out;
+               }
+       }
+
+      out:
+       return err;
+}
+
+extern struct pciback_config_capability pciback_config_capability_vpd;
+extern struct pciback_config_capability pciback_config_capability_pm;
+
+int pciback_config_capability_init(void)
+{
+       register_capability(&pciback_config_capability_vpd);
+       register_capability(&pciback_config_capability_pm);
+
+       return 0;
+}
diff -r d66dfd584d2f -r e42ed4332053 
linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability.h  Thu Apr 
27 09:49:48 2006 +0100
@@ -0,0 +1,23 @@
+/*
+ * PCI Backend - Data structures for special overlays for structures on
+ *               the capability list.
+ *
+ * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx>
+ */
+
+#ifndef __PCIBACK_CONFIG_CAPABILITY_H__
+#define __PCIBACK_CONFIG_CAPABILITY_H__
+
+#include <linux/pci.h>
+#include <linux/list.h>
+
+struct pciback_config_capability {
+       struct list_head cap_list;
+
+       int capability;
+
+       /* If the device has the capability found above, add these fields */
+       struct config_field *fields;
+};
+
+#endif
diff -r d66dfd584d2f -r e42ed4332053 
linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability_pm.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability_pm.c       
Thu Apr 27 09:49:48 2006 +0100
@@ -0,0 +1,113 @@
+/*
+ * PCI Backend - Configuration space overlay for power management
+ *
+ * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx>
+ */
+
+#include <linux/pci.h>
+#include "conf_space.h"
+#include "conf_space_capability.h"
+
+static int pm_caps_read(struct pci_dev *dev, int offset, u16 *value,
+                       void *data)
+{
+       int err;
+       u16 real_value;
+
+       err = pci_read_config_word(dev, offset, &real_value);
+       if (err)
+               goto out;
+
+       *value = real_value & ~PCI_PM_CAP_PME_MASK;
+
+      out:
+       return err;
+}
+
+/* PM_OK_BITS specifies the bits that the driver domain is allowed to change.
+ * Can't allow driver domain to enable PMEs - they're shared */
+#define PM_OK_BITS (PCI_PM_CTRL_PME_STATUS|PCI_PM_CTRL_DATA_SEL_MASK)
+
+static int pm_ctrl_write(struct pci_dev *dev, int offset, u16 new_value,
+                        void *data)
+{
+       int err;
+       u16 cur_value;
+       pci_power_t new_state;
+
+       /* Handle setting power state separately */
+       new_state = (pci_power_t)(new_value & PCI_PM_CTRL_STATE_MASK);
+
+       err = pci_read_config_word(dev, offset, &cur_value);
+       if (err)
+               goto out;
+
+       new_value &= PM_OK_BITS;
+       if ((cur_value & PM_OK_BITS) != new_value) {
+               new_value = (cur_value & ~PM_OK_BITS) | new_value;
+               err = pci_write_config_word(dev, offset, new_value);
+               if (err)
+                       goto out;
+       }
+
+       /* Let pci core handle the power management change */
+       dev_dbg(&dev->dev, "set power state to %x\n", new_state);
+       err = pci_set_power_state(dev, new_state);
+       if (err)
+               err = PCIBIOS_SET_FAILED;
+
+      out:
+       return err;
+}
+
+/* Ensure PMEs are disabled */
+static void *pm_ctrl_init(struct pci_dev *dev, int offset)
+{
+       int err;
+       u16 value;
+
+       err = pci_read_config_word(dev, offset, &value);
+       if (err)
+               goto out;
+
+       if (value & PCI_PM_CTRL_PME_ENABLE) {
+               value &= ~PCI_PM_CTRL_PME_ENABLE;
+               err = pci_write_config_word(dev, offset, value);
+       }
+
+      out:
+       return ERR_PTR(err);
+}
+
+static struct config_field caplist_pm[] = {
+       {
+               .offset     = PCI_PM_PMC,
+               .size       = 2,
+               .u.w.read   = pm_caps_read,
+       },
+       {
+               .offset     = PCI_PM_CTRL,
+               .size       = 2,
+               .init       = pm_ctrl_init,
+               .u.w.read   = pciback_read_config_word,
+               .u.w.write  = pm_ctrl_write,
+       },
+       {
+               .offset     = PCI_PM_PPB_EXTENSIONS,
+               .size       = 1,
+               .u.b.read   = pciback_read_config_byte,
+       },
+       {
+               .offset     = PCI_PM_DATA_REGISTER,
+               .size       = 1,
+               .u.b.read   = pciback_read_config_byte,
+       },
+       {
+               .size = 0,
+       },
+};
+
+struct pciback_config_capability pciback_config_capability_pm = {
+       .capability = PCI_CAP_ID_PM,
+       .fields = caplist_pm,
+};
diff -r d66dfd584d2f -r e42ed4332053 
linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability_vpd.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_capability_vpd.c      
Thu Apr 27 09:49:48 2006 +0100
@@ -0,0 +1,42 @@
+/*
+ * PCI Backend - Configuration space overlay for Vital Product Data
+ *
+ * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx>
+ */
+
+#include <linux/pci.h>
+#include "conf_space.h"
+#include "conf_space_capability.h"
+
+static int vpd_address_write(struct pci_dev *dev, int offset, u16 value,
+                            void *data)
+{
+       /* Disallow writes to the vital product data */
+       if (value & PCI_VPD_ADDR_F)
+               return PCIBIOS_SET_FAILED;
+       else
+               return pci_write_config_word(dev, offset, value);
+}
+
+static struct config_field caplist_vpd[] = {
+       {
+        .offset    = PCI_VPD_ADDR,
+        .size      = 2,
+        .u.w.read  = pciback_read_config_word,
+        .u.w.write = vpd_address_write,
+        },
+       {
+        .offset     = PCI_VPD_DATA,
+        .size       = 4,
+        .u.dw.read  = pciback_read_config_dword,
+        .u.dw.write = NULL,
+        },
+       {
+        .size = 0,
+        },
+};
+ 
+struct pciback_config_capability pciback_config_capability_vpd = {
+       .capability = PCI_CAP_ID_VPD,
+       .fields = caplist_vpd,
+};

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


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.