 402c5e15b4
			
		
	
	
	402c5e15b4
	
	
	
		
			
			This is a minor bugfix and a set of small cleanups; as it is not clear whether this needs splitting into pieces (and if so, at what granularity), it is a single combined patch. - add a missing return statement to an error path in kill_domain_by_device() - use pci_is_enabled() rather than raw atomic_read() - remove a bogus attempt to zero-terminate an already zero-terminated string - #define DRV_NAME once uniformly in the shared local header - make DRIVER_ATTR() variables static - eliminate a pointless use of list_for_each_entry_safe() - add MODULE_ALIAS() - a little bit of constification - adjust a few messages - remove stray semicolons from inline function definitions Signed-off-by: Jan Beulich <jbeulich@suse.com> [v1: Dropped the resource_size fix, altered the description] [v2: Fixed cleanpatch.pl comments] Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
		
			
				
	
	
		
			385 lines
		
	
	
	
		
			8.6 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			385 lines
		
	
	
	
		
			8.6 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * PCI Backend - Handles the virtual fields in the configuration space headers.
 | |
|  *
 | |
|  * Author: Ryan Wilson <hap9@epoch.ncsc.mil>
 | |
|  */
 | |
| 
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/pci.h>
 | |
| #include "pciback.h"
 | |
| #include "conf_space.h"
 | |
| 
 | |
| struct pci_bar_info {
 | |
| 	u32 val;
 | |
| 	u32 len_val;
 | |
| 	int which;
 | |
| };
 | |
| 
 | |
| #define is_enable_cmd(value) ((value)&(PCI_COMMAND_MEMORY|PCI_COMMAND_IO))
 | |
| #define is_master_cmd(value) ((value)&PCI_COMMAND_MASTER)
 | |
| 
 | |
| static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data)
 | |
| {
 | |
| 	int i;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = xen_pcibk_read_config_word(dev, offset, value, data);
 | |
| 	if (!pci_is_enabled(dev))
 | |
| 		return ret;
 | |
| 
 | |
| 	for (i = 0; i < PCI_ROM_RESOURCE; i++) {
 | |
| 		if (dev->resource[i].flags & IORESOURCE_IO)
 | |
| 			*value |= PCI_COMMAND_IO;
 | |
| 		if (dev->resource[i].flags & IORESOURCE_MEM)
 | |
| 			*value |= PCI_COMMAND_MEMORY;
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int command_write(struct pci_dev *dev, int offset, u16 value, void *data)
 | |
| {
 | |
| 	struct xen_pcibk_dev_data *dev_data;
 | |
| 	int err;
 | |
| 
 | |
| 	dev_data = pci_get_drvdata(dev);
 | |
| 	if (!pci_is_enabled(dev) && is_enable_cmd(value)) {
 | |
| 		if (unlikely(verbose_request))
 | |
| 			printk(KERN_DEBUG DRV_NAME ": %s: enable\n",
 | |
| 			       pci_name(dev));
 | |
| 		err = pci_enable_device(dev);
 | |
| 		if (err)
 | |
| 			return err;
 | |
| 		if (dev_data)
 | |
| 			dev_data->enable_intx = 1;
 | |
| 	} else if (pci_is_enabled(dev) && !is_enable_cmd(value)) {
 | |
| 		if (unlikely(verbose_request))
 | |
| 			printk(KERN_DEBUG DRV_NAME ": %s: disable\n",
 | |
| 			       pci_name(dev));
 | |
| 		pci_disable_device(dev);
 | |
| 		if (dev_data)
 | |
| 			dev_data->enable_intx = 0;
 | |
| 	}
 | |
| 
 | |
| 	if (!dev->is_busmaster && is_master_cmd(value)) {
 | |
| 		if (unlikely(verbose_request))
 | |
| 			printk(KERN_DEBUG DRV_NAME ": %s: set bus master\n",
 | |
| 			       pci_name(dev));
 | |
| 		pci_set_master(dev);
 | |
| 	}
 | |
| 
 | |
| 	if (value & PCI_COMMAND_INVALIDATE) {
 | |
| 		if (unlikely(verbose_request))
 | |
| 			printk(KERN_DEBUG
 | |
| 			       DRV_NAME ": %s: enable memory-write-invalidate\n",
 | |
| 			       pci_name(dev));
 | |
| 		err = pci_set_mwi(dev);
 | |
| 		if (err) {
 | |
| 			printk(KERN_WARNING
 | |
| 			       DRV_NAME ": %s: cannot enable "
 | |
| 			       "memory-write-invalidate (%d)\n",
 | |
| 			       pci_name(dev), err);
 | |
| 			value &= ~PCI_COMMAND_INVALIDATE;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return pci_write_config_word(dev, offset, value);
 | |
| }
 | |
| 
 | |
| static int rom_write(struct pci_dev *dev, int offset, u32 value, void *data)
 | |
| {
 | |
| 	struct pci_bar_info *bar = data;
 | |
| 
 | |
| 	if (unlikely(!bar)) {
 | |
| 		printk(KERN_WARNING DRV_NAME ": driver data not found for %s\n",
 | |
| 		       pci_name(dev));
 | |
| 		return XEN_PCI_ERR_op_failed;
 | |
| 	}
 | |
| 
 | |
| 	/* A write to obtain the length must happen as a 32-bit write.
 | |
| 	 * This does not (yet) support writing individual bytes
 | |
| 	 */
 | |
| 	if (value == ~PCI_ROM_ADDRESS_ENABLE)
 | |
| 		bar->which = 1;
 | |
| 	else {
 | |
| 		u32 tmpval;
 | |
| 		pci_read_config_dword(dev, offset, &tmpval);
 | |
| 		if (tmpval != bar->val && value == bar->val) {
 | |
| 			/* Allow restoration of bar value. */
 | |
| 			pci_write_config_dword(dev, offset, bar->val);
 | |
| 		}
 | |
| 		bar->which = 0;
 | |
| 	}
 | |
| 
 | |
| 	/* Do we need to support enabling/disabling the rom address here? */
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* For the BARs, only allow writes which write ~0 or
 | |
|  * the correct resource information
 | |
|  * (Needed for when the driver probes the resource usage)
 | |
|  */
 | |
| static int bar_write(struct pci_dev *dev, int offset, u32 value, void *data)
 | |
| {
 | |
| 	struct pci_bar_info *bar = data;
 | |
| 
 | |
| 	if (unlikely(!bar)) {
 | |
| 		printk(KERN_WARNING DRV_NAME ": driver data not found for %s\n",
 | |
| 		       pci_name(dev));
 | |
| 		return XEN_PCI_ERR_op_failed;
 | |
| 	}
 | |
| 
 | |
| 	/* A write to obtain the length must happen as a 32-bit write.
 | |
| 	 * This does not (yet) support writing individual bytes
 | |
| 	 */
 | |
| 	if (value == ~0)
 | |
| 		bar->which = 1;
 | |
| 	else {
 | |
| 		u32 tmpval;
 | |
| 		pci_read_config_dword(dev, offset, &tmpval);
 | |
| 		if (tmpval != bar->val && value == bar->val) {
 | |
| 			/* Allow restoration of bar value. */
 | |
| 			pci_write_config_dword(dev, offset, bar->val);
 | |
| 		}
 | |
| 		bar->which = 0;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int bar_read(struct pci_dev *dev, int offset, u32 * value, void *data)
 | |
| {
 | |
| 	struct pci_bar_info *bar = data;
 | |
| 
 | |
| 	if (unlikely(!bar)) {
 | |
| 		printk(KERN_WARNING DRV_NAME ": driver data not found for %s\n",
 | |
| 		       pci_name(dev));
 | |
| 		return XEN_PCI_ERR_op_failed;
 | |
| 	}
 | |
| 
 | |
| 	*value = bar->which ? bar->len_val : bar->val;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static inline void read_dev_bar(struct pci_dev *dev,
 | |
| 				struct pci_bar_info *bar_info, int offset,
 | |
| 				u32 len_mask)
 | |
| {
 | |
| 	int	pos;
 | |
| 	struct resource	*res = dev->resource;
 | |
| 
 | |
| 	if (offset == PCI_ROM_ADDRESS || offset == PCI_ROM_ADDRESS1)
 | |
| 		pos = PCI_ROM_RESOURCE;
 | |
| 	else {
 | |
| 		pos = (offset - PCI_BASE_ADDRESS_0) / 4;
 | |
| 		if (pos && ((res[pos - 1].flags & (PCI_BASE_ADDRESS_SPACE |
 | |
| 				PCI_BASE_ADDRESS_MEM_TYPE_MASK)) ==
 | |
| 			   (PCI_BASE_ADDRESS_SPACE_MEMORY |
 | |
| 				PCI_BASE_ADDRESS_MEM_TYPE_64))) {
 | |
| 			bar_info->val = res[pos - 1].start >> 32;
 | |
| 			bar_info->len_val = res[pos - 1].end >> 32;
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	bar_info->val = res[pos].start |
 | |
| 			(res[pos].flags & PCI_REGION_FLAG_MASK);
 | |
| 	bar_info->len_val = resource_size(&res[pos]);
 | |
| }
 | |
| 
 | |
| static void *bar_init(struct pci_dev *dev, int offset)
 | |
| {
 | |
| 	struct pci_bar_info *bar = kmalloc(sizeof(*bar), GFP_KERNEL);
 | |
| 
 | |
| 	if (!bar)
 | |
| 		return ERR_PTR(-ENOMEM);
 | |
| 
 | |
| 	read_dev_bar(dev, bar, offset, ~0);
 | |
| 	bar->which = 0;
 | |
| 
 | |
| 	return bar;
 | |
| }
 | |
| 
 | |
| static void *rom_init(struct pci_dev *dev, int offset)
 | |
| {
 | |
| 	struct pci_bar_info *bar = kmalloc(sizeof(*bar), GFP_KERNEL);
 | |
| 
 | |
| 	if (!bar)
 | |
| 		return ERR_PTR(-ENOMEM);
 | |
| 
 | |
| 	read_dev_bar(dev, bar, offset, ~PCI_ROM_ADDRESS_ENABLE);
 | |
| 	bar->which = 0;
 | |
| 
 | |
| 	return bar;
 | |
| }
 | |
| 
 | |
| static void bar_reset(struct pci_dev *dev, int offset, void *data)
 | |
| {
 | |
| 	struct pci_bar_info *bar = data;
 | |
| 
 | |
| 	bar->which = 0;
 | |
| }
 | |
| 
 | |
| static void bar_release(struct pci_dev *dev, int offset, void *data)
 | |
| {
 | |
| 	kfree(data);
 | |
| }
 | |
| 
 | |
| static int xen_pcibk_read_vendor(struct pci_dev *dev, int offset,
 | |
| 			       u16 *value, void *data)
 | |
| {
 | |
| 	*value = dev->vendor;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int xen_pcibk_read_device(struct pci_dev *dev, int offset,
 | |
| 			       u16 *value, void *data)
 | |
| {
 | |
| 	*value = dev->device;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int interrupt_read(struct pci_dev *dev, int offset, u8 * value,
 | |
| 			  void *data)
 | |
| {
 | |
| 	*value = (u8) dev->irq;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 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 const struct config_field header_common[] = {
 | |
| 	{
 | |
| 	 .offset    = PCI_VENDOR_ID,
 | |
| 	 .size      = 2,
 | |
| 	 .u.w.read  = xen_pcibk_read_vendor,
 | |
| 	},
 | |
| 	{
 | |
| 	 .offset    = PCI_DEVICE_ID,
 | |
| 	 .size      = 2,
 | |
| 	 .u.w.read  = xen_pcibk_read_device,
 | |
| 	},
 | |
| 	{
 | |
| 	 .offset    = PCI_COMMAND,
 | |
| 	 .size      = 2,
 | |
| 	 .u.w.read  = command_read,
 | |
| 	 .u.w.write = command_write,
 | |
| 	},
 | |
| 	{
 | |
| 	 .offset    = PCI_INTERRUPT_LINE,
 | |
| 	 .size      = 1,
 | |
| 	 .u.b.read  = interrupt_read,
 | |
| 	},
 | |
| 	{
 | |
| 	 .offset    = PCI_INTERRUPT_PIN,
 | |
| 	 .size      = 1,
 | |
| 	 .u.b.read  = xen_pcibk_read_config_byte,
 | |
| 	},
 | |
| 	{
 | |
| 	 /* Any side effects of letting driver domain control cache line? */
 | |
| 	 .offset    = PCI_CACHE_LINE_SIZE,
 | |
| 	 .size      = 1,
 | |
| 	 .u.b.read  = xen_pcibk_read_config_byte,
 | |
| 	 .u.b.write = xen_pcibk_write_config_byte,
 | |
| 	},
 | |
| 	{
 | |
| 	 .offset    = PCI_LATENCY_TIMER,
 | |
| 	 .size      = 1,
 | |
| 	 .u.b.read  = xen_pcibk_read_config_byte,
 | |
| 	},
 | |
| 	{
 | |
| 	 .offset    = PCI_BIST,
 | |
| 	 .size      = 1,
 | |
| 	 .u.b.read  = xen_pcibk_read_config_byte,
 | |
| 	 .u.b.write = bist_write,
 | |
| 	},
 | |
| 	{}
 | |
| };
 | |
| 
 | |
| #define CFG_FIELD_BAR(reg_offset)			\
 | |
| 	{						\
 | |
| 	.offset     = reg_offset,			\
 | |
| 	.size       = 4,				\
 | |
| 	.init       = bar_init,				\
 | |
| 	.reset      = bar_reset,			\
 | |
| 	.release    = bar_release,			\
 | |
| 	.u.dw.read  = bar_read,				\
 | |
| 	.u.dw.write = bar_write,			\
 | |
| 	}
 | |
| 
 | |
| #define CFG_FIELD_ROM(reg_offset)			\
 | |
| 	{						\
 | |
| 	.offset     = reg_offset,			\
 | |
| 	.size       = 4,				\
 | |
| 	.init       = rom_init,				\
 | |
| 	.reset      = bar_reset,			\
 | |
| 	.release    = bar_release,			\
 | |
| 	.u.dw.read  = bar_read,				\
 | |
| 	.u.dw.write = rom_write,			\
 | |
| 	}
 | |
| 
 | |
| static const 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),
 | |
| 	CFG_FIELD_BAR(PCI_BASE_ADDRESS_3),
 | |
| 	CFG_FIELD_BAR(PCI_BASE_ADDRESS_4),
 | |
| 	CFG_FIELD_BAR(PCI_BASE_ADDRESS_5),
 | |
| 	CFG_FIELD_ROM(PCI_ROM_ADDRESS),
 | |
| 	{}
 | |
| };
 | |
| 
 | |
| static const 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),
 | |
| 	{}
 | |
| };
 | |
| 
 | |
| int xen_pcibk_config_header_add_fields(struct pci_dev *dev)
 | |
| {
 | |
| 	int err;
 | |
| 
 | |
| 	err = xen_pcibk_config_add_fields(dev, header_common);
 | |
| 	if (err)
 | |
| 		goto out;
 | |
| 
 | |
| 	switch (dev->hdr_type) {
 | |
| 	case PCI_HEADER_TYPE_NORMAL:
 | |
| 		err = xen_pcibk_config_add_fields(dev, header_0);
 | |
| 		break;
 | |
| 
 | |
| 	case PCI_HEADER_TYPE_BRIDGE:
 | |
| 		err = xen_pcibk_config_add_fields(dev, header_1);
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		err = -EINVAL;
 | |
| 		printk(KERN_ERR DRV_NAME ": %s: Unsupported header type %d!\n",
 | |
| 		       pci_name(dev), dev->hdr_type);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| out:
 | |
| 	return err;
 | |
| }
 |