ia64/xen-unstable

changeset 10842:4c97599398fe

[PCI] Allow per-device configuration for fine-grained control over PCI
configuration space writes, with a goal that was also previously
described by Ryan:

"Permissive mode should only be used as a fall back for unknown
devices.
I think the correct solution for dealing with these device-specific
configuration space registers is to identify them and add the
device-specific fields to the overlay. This patch adds a special
configuration space handler for network cards based on the tg3 linux
network device driver. This handler should allow for reads/writes to
all of the configuration space registers that the tg3 driver requires."

This patch attempts to address concerns with Ryan's original
submission by moving policy from the dom0 kernel into dom0 user-space.
As new quirky devices emerge they can be incorporated into the user-space
policy. An added benefit is that changes to the policy are effective
for domains created after the changes are written (no need rebuild the
hypervisor or restart xend).

Signed-off-by: Chris Bookholt <hap10@tycho.ncsc.mil>
author kfraser@localhost.localdomain
date Fri Jul 28 12:54:58 2006 +0100 (2006-07-28)
parents 7ee2c02c6db0
children 4f6d858ea570
files linux-2.6-xen-sparse/drivers/xen/pciback/Makefile linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.c linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.h linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_quirks.c linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_quirks.h linux-2.6-xen-sparse/drivers/xen/pciback/pci_stub.c linux-2.6-xen-sparse/drivers/xen/pciback/pciback.h
line diff
     1.1 --- a/linux-2.6-xen-sparse/drivers/xen/pciback/Makefile	Fri Jul 28 11:10:08 2006 +0100
     1.2 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/Makefile	Fri Jul 28 12:54:58 2006 +0100
     1.3 @@ -4,7 +4,8 @@ pciback-y := pci_stub.o pciback_ops.o xe
     1.4  pciback-y += conf_space.o conf_space_header.o \
     1.5  	     conf_space_capability.o \
     1.6  	     conf_space_capability_vpd.o \
     1.7 -	     conf_space_capability_pm.o
     1.8 +	     conf_space_capability_pm.o \
     1.9 +             conf_space_quirks.o
    1.10  pciback-$(CONFIG_XEN_PCIDEV_BACKEND_VPCI) += vpci.o
    1.11  pciback-$(CONFIG_XEN_PCIDEV_BACKEND_PASS) += passthrough.o
    1.12  
     2.1 --- a/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.c	Fri Jul 28 11:10:08 2006 +0100
     2.2 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.c	Fri Jul 28 12:54:58 2006 +0100
     2.3 @@ -13,6 +13,7 @@
     2.4  #include <linux/pci.h>
     2.5  #include "pciback.h"
     2.6  #include "conf_space.h"
     2.7 +#include "conf_space_quirks.h"
     2.8  
     2.9  static int permissive = 0;
    2.10  module_param(permissive, bool, 0644);
    2.11 @@ -81,7 +82,7 @@ static int conf_space_write(struct pci_d
    2.12  	case 4:
    2.13  		if (field->u.dw.write)
    2.14  			ret = field->u.dw.write(dev, offset, value,
    2.15 -					        entry->data);
    2.16 +						entry->data);
    2.17  		break;
    2.18  	}
    2.19  	return ret;
    2.20 @@ -261,38 +262,58 @@ int pciback_config_write(struct pci_dev 
    2.21  			switch (size) {
    2.22  			case 1:
    2.23  				err = pci_write_config_byte(dev, offset,
    2.24 -							    (u8)value);
    2.25 +							    (u8) value);
    2.26  				break;
    2.27  			case 2:
    2.28  				err = pci_write_config_word(dev, offset,
    2.29 -							    (u16)value);
    2.30 +							    (u16) value);
    2.31  				break;
    2.32  			case 4:
    2.33  				err = pci_write_config_dword(dev, offset,
    2.34 -							     (u32)value);
    2.35 +							     (u32) value);
    2.36  				break;
    2.37  			}
    2.38  		} else if (!dev_data->warned_on_write) {
    2.39  			dev_data->warned_on_write = 1;
    2.40 -			dev_warn(&dev->dev, "Driver wrote to a read-only "
    2.41 -				 "configuration space field!\n");
    2.42 -			dev_warn(&dev->dev, "Write at offset 0x%x size %d\n",
    2.43 -				offset, size);
    2.44 -			dev_warn(&dev->dev, "This may be harmless, but if\n");
    2.45 -			dev_warn(&dev->dev, "you have problems with your "
    2.46 -				 "device:\n");
    2.47 -			dev_warn(&dev->dev, "1) see the permissive "
    2.48 -				 "attribute in sysfs.\n");
    2.49 -			dev_warn(&dev->dev, "2) report problems to the "
    2.50 -				 "xen-devel mailing list along\n");
    2.51 -			dev_warn(&dev->dev, "   with details of your device "
    2.52 -				 "obtained from lspci.\n");
    2.53 +			dev_warn(&dev->dev, "Driver tried to write to a "
    2.54 +				 "read-only configuration space field at offset "
    2.55 +				 "0x%x, size %d. This may be harmless, but if "
    2.56 +				 "you have problems with your device:\n"
    2.57 +				 "1) see permissive attribute in sysfs\n"
    2.58 +				 "2) report problems to the xen-devel "
    2.59 +				 "mailing list along with details of your "
    2.60 +				 "device obtained from lspci.\n", offset, size);
    2.61  		}
    2.62  	}
    2.63  
    2.64  	return pcibios_err_to_errno(err);
    2.65  }
    2.66  
    2.67 +void pciback_config_free_dyn_fields(struct pci_dev *dev)
    2.68 +{
    2.69 +	struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
    2.70 +	struct config_field_entry *cfg_entry, *t;
    2.71 +	struct config_field *field;
    2.72 +
    2.73 +	dev_dbg(&dev->dev,
    2.74 +		"free-ing dynamically allocated virtual configuration space fields\n");
    2.75 +
    2.76 +	list_for_each_entry_safe(cfg_entry, t, &dev_data->config_fields, list) {
    2.77 +		field = cfg_entry->field;
    2.78 +
    2.79 +		if (field->clean) {
    2.80 +			field->clean(field);
    2.81 +
    2.82 +			if (cfg_entry->data)
    2.83 +				kfree(cfg_entry->data);
    2.84 +
    2.85 +			list_del(&cfg_entry->list);
    2.86 +			kfree(cfg_entry);
    2.87 +		}
    2.88 +
    2.89 +	}
    2.90 +}
    2.91 +
    2.92  void pciback_config_reset_dev(struct pci_dev *dev)
    2.93  {
    2.94  	struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
    2.95 @@ -338,6 +359,10 @@ int pciback_config_add_field_offset(stru
    2.96  	struct config_field_entry *cfg_entry;
    2.97  	void *tmp;
    2.98  
    2.99 +	/* silently ignore duplicate fields */
   2.100 +	if (pciback_field_is_dup(dev, field->offset))
   2.101 +		goto out;
   2.102 +
   2.103  	cfg_entry = kmalloc(sizeof(*cfg_entry), GFP_KERNEL);
   2.104  	if (!cfg_entry) {
   2.105  		err = -ENOMEM;
   2.106 @@ -388,6 +413,10 @@ int pciback_config_init_dev(struct pci_d
   2.107  		goto out;
   2.108  
   2.109  	err = pciback_config_capability_add_fields(dev);
   2.110 +	if (err)
   2.111 +		goto out;
   2.112 +
   2.113 +	err = pciback_config_quirks_init(dev);
   2.114  
   2.115        out:
   2.116  	return err;
   2.117 @@ -395,9 +424,5 @@ int pciback_config_init_dev(struct pci_d
   2.118  
   2.119  int pciback_config_init(void)
   2.120  {
   2.121 -	int err;
   2.122 -
   2.123 -	err = pciback_config_capability_init();
   2.124 -
   2.125 -	return err;
   2.126 +	return pciback_config_capability_init();
   2.127  }
     3.1 --- a/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.h	Fri Jul 28 11:10:08 2006 +0100
     3.2 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.h	Fri Jul 28 12:54:58 2006 +0100
     3.3 @@ -33,11 +33,13 @@ typedef int (*conf_byte_read) (struct pc
     3.4   * values.
     3.5   */
     3.6  struct config_field {
     3.7 -	unsigned int     offset;
     3.8 -	unsigned int     size;
     3.9 -	conf_field_init  init;
    3.10 +	unsigned int offset;
    3.11 +	unsigned int size;
    3.12 +	unsigned int mask;
    3.13 +	conf_field_init init;
    3.14  	conf_field_reset reset;
    3.15 -	conf_field_free  release;
    3.16 +	conf_field_free release;
    3.17 +	void (*clean) (struct config_field * field);
    3.18  	union {
    3.19  		struct {
    3.20  			conf_dword_write write;
    3.21 @@ -52,6 +54,7 @@ struct config_field {
    3.22  			conf_byte_read read;
    3.23  		} b;
    3.24  	} u;
    3.25 +	struct list_head list;
    3.26  };
    3.27  
    3.28  struct config_field_entry {
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_quirks.c	Fri Jul 28 12:54:58 2006 +0100
     4.3 @@ -0,0 +1,128 @@
     4.4 +/*
     4.5 + * PCI Backend - Handle special overlays for broken devices.
     4.6 + *
     4.7 + * Author: Ryan Wilson <hap9@epoch.ncsc.mil>
     4.8 + * Author: Chris Bookholt <hap10@epoch.ncsc.mil>
     4.9 + */
    4.10 +
    4.11 +#include <linux/kernel.h>
    4.12 +#include <linux/pci.h>
    4.13 +#include "pciback.h"
    4.14 +#include "conf_space.h"
    4.15 +#include "conf_space_quirks.h"
    4.16 +
    4.17 +LIST_HEAD(pciback_quirks);
    4.18 +
    4.19 +struct pciback_config_quirk *pciback_find_quirk(struct pci_dev *dev)
    4.20 +{
    4.21 +	struct pciback_config_quirk *tmp_quirk;
    4.22 +
    4.23 +	list_for_each_entry(tmp_quirk, &pciback_quirks, quirks_list)
    4.24 +	    if (pci_match_id(&tmp_quirk->devid, dev))
    4.25 +		goto out;
    4.26 +	tmp_quirk = NULL;
    4.27 +	printk(KERN_DEBUG
    4.28 +	       "quirk didn't match any device pciback knows about\n");
    4.29 +      out:
    4.30 +	return tmp_quirk;
    4.31 +}
    4.32 +
    4.33 +static inline void register_quirk(struct pciback_config_quirk *quirk)
    4.34 +{
    4.35 +	list_add_tail(&quirk->quirks_list, &pciback_quirks);
    4.36 +}
    4.37 +
    4.38 +int pciback_field_is_dup(struct pci_dev *dev, int reg)
    4.39 +{
    4.40 +	int ret = 0;
    4.41 +	struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
    4.42 +	struct config_field *field;
    4.43 +	struct config_field_entry *cfg_entry;
    4.44 +
    4.45 +	list_for_each_entry(cfg_entry, &dev_data->config_fields, list) {
    4.46 +		field = cfg_entry->field;
    4.47 +		if (field->offset == reg) {
    4.48 +			ret = 1;
    4.49 +			break;
    4.50 +		}
    4.51 +	}
    4.52 +	return ret;
    4.53 +}
    4.54 +
    4.55 +int pciback_config_quirks_add_field(struct pci_dev *dev, struct config_field
    4.56 +				    *field)
    4.57 +{
    4.58 +	int err = 0;
    4.59 +
    4.60 +	switch (field->size) {
    4.61 +	case 1:
    4.62 +		field->u.b.read = pciback_read_config_byte;
    4.63 +		field->u.b.write = pciback_write_config_byte;
    4.64 +		break;
    4.65 +	case 2:
    4.66 +		field->u.w.read = pciback_read_config_word;
    4.67 +		field->u.w.write = pciback_write_config_word;
    4.68 +		break;
    4.69 +	case 4:
    4.70 +		field->u.dw.read = pciback_read_config_dword;
    4.71 +		field->u.dw.write = pciback_write_config_dword;
    4.72 +		break;
    4.73 +	default:
    4.74 +		err = -EINVAL;
    4.75 +		goto out;
    4.76 +	}
    4.77 +
    4.78 +	pciback_config_add_field(dev, field);
    4.79 +
    4.80 +      out:
    4.81 +	return err;
    4.82 +}
    4.83 +
    4.84 +int pciback_config_quirks_init(struct pci_dev *dev)
    4.85 +{
    4.86 +	struct pciback_config_quirk *quirk;
    4.87 +	int ret = 0;
    4.88 +
    4.89 +	quirk = kzalloc(sizeof(*quirk), GFP_ATOMIC);
    4.90 +	if (!quirk) {
    4.91 +		ret = -ENOMEM;
    4.92 +		goto out;
    4.93 +	}
    4.94 +
    4.95 +	quirk->devid.vendor = dev->vendor;
    4.96 +	quirk->devid.device = dev->device;
    4.97 +	quirk->devid.subvendor = dev->subsystem_vendor;
    4.98 +	quirk->devid.subdevice = dev->subsystem_device;
    4.99 +	quirk->devid.class = 0;
   4.100 +	quirk->devid.class_mask = 0;
   4.101 +	quirk->devid.driver_data = 0UL;
   4.102 +
   4.103 +	quirk->pdev = dev;
   4.104 +
   4.105 +	register_quirk(quirk);
   4.106 +      out:
   4.107 +	return ret;
   4.108 +}
   4.109 +
   4.110 +void pciback_config_field_free(struct config_field *field)
   4.111 +{
   4.112 +	kfree(field);
   4.113 +}
   4.114 +
   4.115 +int pciback_config_quirk_release(struct pci_dev *dev)
   4.116 +{
   4.117 +	struct pciback_config_quirk *quirk;
   4.118 +	int ret = 0;
   4.119 +
   4.120 +	quirk = pciback_find_quirk(dev);
   4.121 +	if (!quirk) {
   4.122 +		ret = -ENXIO;
   4.123 +		goto out;
   4.124 +	}
   4.125 +
   4.126 +	list_del(&quirk->quirks_list);
   4.127 +	kfree(quirk);
   4.128 +
   4.129 +      out:
   4.130 +	return ret;
   4.131 +}
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_quirks.h	Fri Jul 28 12:54:58 2006 +0100
     5.3 @@ -0,0 +1,35 @@
     5.4 +/*
     5.5 + * PCI Backend - Data structures for special overlays for broken devices.
     5.6 + *
     5.7 + * Ryan Wilson <hap9@epoch.ncsc.mil>
     5.8 + * Chris Bookholt <hap10@epoch.ncsc.mil>
     5.9 + */
    5.10 +
    5.11 +#ifndef __XEN_PCIBACK_CONF_SPACE_QUIRKS_H__
    5.12 +#define __XEN_PCIBACK_CONF_SPACE_QUIRKS_H__
    5.13 +
    5.14 +#include <linux/pci.h>
    5.15 +#include <linux/list.h>
    5.16 +
    5.17 +struct pciback_config_quirk {
    5.18 +	struct list_head quirks_list;
    5.19 +	struct pci_device_id devid;
    5.20 +	struct pci_dev *pdev;
    5.21 +};
    5.22 +
    5.23 +struct pciback_config_quirk *pciback_find_quirk(struct pci_dev *dev);
    5.24 +
    5.25 +int pciback_config_quirks_add_field(struct pci_dev *dev, struct config_field
    5.26 +				    *field);
    5.27 +
    5.28 +int pciback_config_quirks_remove_field(struct pci_dev *dev, int reg);
    5.29 +
    5.30 +int pciback_config_quirks_init(struct pci_dev *dev);
    5.31 +
    5.32 +void pciback_config_field_free(struct config_field *field);
    5.33 +
    5.34 +int pciback_config_quirk_release(struct pci_dev *dev);
    5.35 +
    5.36 +int pciback_field_is_dup(struct pci_dev *dev, int reg);
    5.37 +
    5.38 +#endif
     6.1 --- a/linux-2.6-xen-sparse/drivers/xen/pciback/pci_stub.c	Fri Jul 28 11:10:08 2006 +0100
     6.2 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/pci_stub.c	Fri Jul 28 12:54:58 2006 +0100
     6.3 @@ -1,7 +1,8 @@
     6.4  /*
     6.5   * PCI Stub Driver - Grabs devices in backend to be exported later
     6.6   *
     6.7 - *   Author: Ryan Wilson <hap9@epoch.ncsc.mil>
     6.8 + * Ryan Wilson <hap9@epoch.ncsc.mil>
     6.9 + * Chris Bookholt <hap10@epoch.ncsc.mil>
    6.10   */
    6.11  #include <linux/module.h>
    6.12  #include <linux/init.h>
    6.13 @@ -10,6 +11,8 @@
    6.14  #include <linux/kref.h>
    6.15  #include <asm/atomic.h>
    6.16  #include "pciback.h"
    6.17 +#include "conf_space.h"
    6.18 +#include "conf_space_quirks.h"
    6.19  
    6.20  static char *pci_devs_to_hide = NULL;
    6.21  module_param_named(hide, pci_devs_to_hide, charp, 0444);
    6.22 @@ -31,6 +34,7 @@ struct pcistub_device {
    6.23  	struct pci_dev *dev;
    6.24  	struct pciback_device *pdev;	/* non-NULL if struct pci_dev is in use */
    6.25  };
    6.26 +
    6.27  /* Access to pcistub_devices & seized_devices lists and the initialize_devices
    6.28   * flag must be locked with pcistub_devices_lock
    6.29   */
    6.30 @@ -76,6 +80,7 @@ static void pcistub_device_release(struc
    6.31  
    6.32  	/* Clean-up the device */
    6.33  	pciback_reset_device(psdev->dev);
    6.34 +	pciback_config_free_dyn_fields(psdev->dev);
    6.35  	pciback_config_free_dev(psdev->dev);
    6.36  	kfree(pci_get_drvdata(psdev->dev));
    6.37  	pci_set_drvdata(psdev->dev, NULL);
    6.38 @@ -95,6 +100,32 @@ static inline void pcistub_device_put(st
    6.39  	kref_put(&psdev->kref, pcistub_device_release);
    6.40  }
    6.41  
    6.42 +static struct pcistub_device *pcistub_device_find(int domain, int bus,
    6.43 +						  int slot, int func)
    6.44 +{
    6.45 +	struct pcistub_device *psdev = NULL;
    6.46 +	unsigned long flags;
    6.47 +
    6.48 +	spin_lock_irqsave(&pcistub_devices_lock, flags);
    6.49 +
    6.50 +	list_for_each_entry(psdev, &pcistub_devices, dev_list) {
    6.51 +		if (psdev->dev != NULL
    6.52 +		    && domain == pci_domain_nr(psdev->dev->bus)
    6.53 +		    && bus == psdev->dev->bus->number
    6.54 +		    && PCI_DEVFN(slot, func) == psdev->dev->devfn) {
    6.55 +			pcistub_device_get(psdev);
    6.56 +			goto out;
    6.57 +		}
    6.58 +	}
    6.59 +
    6.60 +	/* didn't find it */
    6.61 +	psdev = NULL;
    6.62 +
    6.63 +      out:
    6.64 +	spin_unlock_irqrestore(&pcistub_devices_lock, flags);
    6.65 +	return psdev;
    6.66 +}
    6.67 +
    6.68  static struct pci_dev *pcistub_device_get_pci_dev(struct pciback_device *pdev,
    6.69  						  struct pcistub_device *psdev)
    6.70  {
    6.71 @@ -180,6 +211,7 @@ void pcistub_put_pci_dev(struct pci_dev 
    6.72  	 * (so it's ready for the next domain)
    6.73  	 */
    6.74  	pciback_reset_device(found_psdev->dev);
    6.75 +	pciback_config_free_dyn_fields(found_psdev->dev);
    6.76  	pciback_config_reset_dev(found_psdev->dev);
    6.77  
    6.78  	spin_lock_irqsave(&found_psdev->lock, flags);
    6.79 @@ -392,6 +424,8 @@ static void pcistub_remove(struct pci_de
    6.80  
    6.81  	spin_lock_irqsave(&pcistub_devices_lock, flags);
    6.82  
    6.83 +	pciback_config_quirk_release(dev);
    6.84 +
    6.85  	list_for_each_entry(psdev, &pcistub_devices, dev_list) {
    6.86  		if (psdev->dev == dev) {
    6.87  			found_psdev = psdev;
    6.88 @@ -471,6 +505,19 @@ static inline int str_to_slot(const char
    6.89  	return -EINVAL;
    6.90  }
    6.91  
    6.92 +static inline int str_to_quirk(const char *buf, int *domain, int *bus, int
    6.93 +			       *slot, int *func, int *reg, int *size, int *mask)
    6.94 +{
    6.95 +	int err;
    6.96 +
    6.97 +	err =
    6.98 +	    sscanf(buf, " %04x:%02x:%02x.%1x-%08x:%1x:%08x", domain, bus, slot,
    6.99 +		   func, reg, size, mask);
   6.100 +	if (err == 7)
   6.101 +		return 0;
   6.102 +	return -EINVAL;
   6.103 +}
   6.104 +
   6.105  static int pcistub_device_id_add(int domain, int bus, int slot, int func)
   6.106  {
   6.107  	struct pcistub_device_id *pci_dev_id;
   6.108 @@ -523,6 +570,46 @@ static int pcistub_device_id_remove(int 
   6.109  	return err;
   6.110  }
   6.111  
   6.112 +static int pcistub_reg_add(int domain, int bus, int slot, int func, int reg,
   6.113 +			   int size, int mask)
   6.114 +{
   6.115 +	int err = 0;
   6.116 +	struct pcistub_device *psdev;
   6.117 +	struct pci_dev *dev;
   6.118 +	struct config_field *field;
   6.119 +
   6.120 +	psdev = pcistub_device_find(domain, bus, slot, func);
   6.121 +	if (!psdev || !psdev->dev) {
   6.122 +		err = -ENODEV;
   6.123 +		goto out;
   6.124 +	}
   6.125 +	dev = psdev->dev;
   6.126 +
   6.127 +	/* check for duplicate field */
   6.128 +	if (pciback_field_is_dup(dev, reg))
   6.129 +		goto out;
   6.130 +
   6.131 +	field = kzalloc(sizeof(*field), GFP_ATOMIC);
   6.132 +	if (!field) {
   6.133 +		err = -ENOMEM;
   6.134 +		goto out;
   6.135 +	}
   6.136 +
   6.137 +	field->offset = reg;
   6.138 +	field->size = size;
   6.139 +	field->mask = mask;
   6.140 +	field->init = NULL;
   6.141 +	field->reset = NULL;
   6.142 +	field->release = NULL;
   6.143 +	field->clean = pciback_config_field_free;
   6.144 +
   6.145 +	err = pciback_config_quirks_add_field(dev, field);
   6.146 +	if (err)
   6.147 +		kfree(field);
   6.148 +      out:
   6.149 +	return err;
   6.150 +}
   6.151 +
   6.152  static ssize_t pcistub_slot_add(struct device_driver *drv, const char *buf,
   6.153  				size_t count)
   6.154  {
   6.155 @@ -587,6 +674,71 @@ static ssize_t pcistub_slot_show(struct 
   6.156  
   6.157  DRIVER_ATTR(slots, S_IRUSR, pcistub_slot_show, NULL);
   6.158  
   6.159 +static ssize_t pcistub_quirk_add(struct device_driver *drv, const char *buf,
   6.160 +				 size_t count)
   6.161 +{
   6.162 +	int domain, bus, slot, func, reg, size, mask;
   6.163 +	int err;
   6.164 +
   6.165 +	err = str_to_quirk(buf, &domain, &bus, &slot, &func, &reg, &size,
   6.166 +			   &mask);
   6.167 +	if (err)
   6.168 +		goto out;
   6.169 +
   6.170 +	err = pcistub_reg_add(domain, bus, slot, func, reg, size, mask);
   6.171 +
   6.172 +      out:
   6.173 +	if (!err)
   6.174 +		err = count;
   6.175 +	return err;
   6.176 +}
   6.177 +
   6.178 +static ssize_t pcistub_quirk_show(struct device_driver *drv, char *buf)
   6.179 +{
   6.180 +	int count = 0;
   6.181 +	unsigned long flags;
   6.182 +	extern struct list_head pciback_quirks;
   6.183 +	struct pciback_config_quirk *quirk;
   6.184 +	struct pciback_dev_data *dev_data;
   6.185 +	struct config_field *field;
   6.186 +	struct config_field_entry *cfg_entry;
   6.187 +
   6.188 +	spin_lock_irqsave(&device_ids_lock, flags);
   6.189 +	list_for_each_entry(quirk, &pciback_quirks, quirks_list) {
   6.190 +		if (count >= PAGE_SIZE)
   6.191 +			goto out;
   6.192 +
   6.193 +		count += scnprintf(buf + count, PAGE_SIZE - count,
   6.194 +				   "%02x:%02x.%01x\n\t%04x:%04x:%04x:%04x\n",
   6.195 +				   quirk->pdev->bus->number,
   6.196 +				   PCI_SLOT(quirk->pdev->devfn),
   6.197 +				   PCI_FUNC(quirk->pdev->devfn),
   6.198 +				   quirk->devid.vendor, quirk->devid.device,
   6.199 +				   quirk->devid.subvendor,
   6.200 +				   quirk->devid.subdevice);
   6.201 +
   6.202 +		dev_data = pci_get_drvdata(quirk->pdev);
   6.203 +
   6.204 +		list_for_each_entry(cfg_entry, &dev_data->config_fields, list) {
   6.205 +			field = cfg_entry->field;
   6.206 +			if (count >= PAGE_SIZE)
   6.207 +				goto out;
   6.208 +
   6.209 +			count += scnprintf(buf + count, PAGE_SIZE -
   6.210 +					   count, "\t\t%08x:%01x:%08x\n",
   6.211 +					   field->offset, field->size,
   6.212 +					   field->mask);
   6.213 +		}
   6.214 +	}
   6.215 +
   6.216 +      out:
   6.217 +	spin_unlock_irqrestore(&device_ids_lock, flags);
   6.218 +
   6.219 +	return count;
   6.220 +}
   6.221 +
   6.222 +DRIVER_ATTR(quirks, S_IRUSR | S_IWUSR, pcistub_quirk_show, pcistub_quirk_add);
   6.223 +
   6.224  static int __init pcistub_init(void)
   6.225  {
   6.226  	int pos = 0;
   6.227 @@ -631,6 +783,7 @@ static int __init pcistub_init(void)
   6.228  	driver_create_file(&pciback_pci_driver.driver,
   6.229  			   &driver_attr_remove_slot);
   6.230  	driver_create_file(&pciback_pci_driver.driver, &driver_attr_slots);
   6.231 +	driver_create_file(&pciback_pci_driver.driver, &driver_attr_quirks);
   6.232  
   6.233        out:
   6.234  	return err;
   6.235 @@ -680,6 +833,7 @@ static void __exit pciback_cleanup(void)
   6.236  	driver_remove_file(&pciback_pci_driver.driver,
   6.237  			   &driver_attr_remove_slot);
   6.238  	driver_remove_file(&pciback_pci_driver.driver, &driver_attr_slots);
   6.239 +	driver_remove_file(&pciback_pci_driver.driver, &driver_attr_quirks);
   6.240  
   6.241  	pci_unregister_driver(&pciback_pci_driver);
   6.242  }
     7.1 --- a/linux-2.6-xen-sparse/drivers/xen/pciback/pciback.h	Fri Jul 28 11:10:08 2006 +0100
     7.2 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/pciback.h	Fri Jul 28 12:54:58 2006 +0100
     7.3 @@ -61,6 +61,7 @@ void pciback_reset_device(struct pci_dev
     7.4  /* Access a virtual configuration space for a PCI device */
     7.5  int pciback_config_init(void);
     7.6  int pciback_config_init_dev(struct pci_dev *dev);
     7.7 +void pciback_config_free_dyn_fields(struct pci_dev *dev);
     7.8  void pciback_config_reset_dev(struct pci_dev *dev);
     7.9  void pciback_config_free_dev(struct pci_dev *dev);
    7.10  int pciback_config_read(struct pci_dev *dev, int offset, int size,