From: Keir Fraser Date: Mon, 1 Mar 2010 09:55:09 +0000 (+0000) Subject: Guest SR-IOV: Replace previous changeset with a more complete implementation from... X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=b54d4a755be56f5879bd65c0e3d4d8f0e680166e;p=legacy%2Flinux-2.6.18-xen.git Guest SR-IOV: Replace previous changeset with a more complete implementation from Intel """Guest SR-IOV support for PV guest These changes are for PV guest to use Virtual Function. Because the VF's vendor, device registers in cfg space are 0xffff, which are invalid and ignored by PCI device scan. Values in 'struct pci_dev' are fixed up by SR-IOV code, and using these values will present correct VID and DID to PV guest kernel. And command registers in the cfg space are read only 0, which means we have to emulate MMIO enable bit (VF only uses MMIO resource) so PV kernel can work properly.""" Signed-off-by: Keir Fraser --- diff --git a/drivers/xen/pciback/conf_space_header.c b/drivers/xen/pciback/conf_space_header.c index 0854b76e..f579d64a 100644 --- a/drivers/xen/pciback/conf_space_header.c +++ b/drivers/xen/pciback/conf_space_header.c @@ -18,6 +18,25 @@ struct pci_bar_info { #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 = pciback_read_config_word(dev, offset, value, data); + /*if (!atomic_read(&dev->enable_cnt)) + 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) { int err; @@ -73,7 +92,17 @@ static int rom_write(struct pci_dev *dev, int offset, u32 value, void *data) /* A write to obtain the length must happen as a 32-bit write. * This does not (yet) support writing individual bytes */ - bar->which = (value == ~PCI_ROM_ADDRESS_ENABLE); + 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? */ @@ -97,7 +126,17 @@ static int bar_write(struct pci_dev *dev, int offset, u32 value, void *data) /* A write to obtain the length must happen as a 32-bit write. * This does not (yet) support writing individual bytes */ - bar->which = (value == ~0); + 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; } @@ -105,10 +144,6 @@ static int bar_write(struct pci_dev *dev, int offset, u32 value, void *data) static int bar_read(struct pci_dev *dev, int offset, u32 * value, void *data) { struct pci_bar_info *bar = data; - int idx = (offset - 0x10) >> 2; - - if (idx > PCI_STD_RESOURCE_END ) - idx = PCI_ROM_RESOURCE; if (unlikely(!bar)) { printk(KERN_WARNING "pciback: driver data not found for %s\n", @@ -116,8 +151,7 @@ static int bar_read(struct pci_dev *dev, int offset, u32 * value, void *data) return XEN_PCI_ERR_op_failed; } - *value = bar->which ? ~(pci_resource_len(dev, idx)-1) : pci_resource_start(dev, idx); - *value |= pci_resource_flags(dev, idx) & 0xf; + *value = bar->which ? bar->len_val : bar->val; return 0; } @@ -126,10 +160,26 @@ static inline void read_dev_bar(struct pci_dev *dev, struct pci_bar_info *bar_info, int offset, u32 len_mask) { - pci_read_config_dword(dev, offset, &bar_info->val); - pci_write_config_dword(dev, offset, len_mask); - pci_read_config_dword(dev, offset, &bar_info->len_val); - pci_write_config_dword(dev, offset, bar_info->val); + 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 = res[pos].end - res[pos].start + 1; } static void *bar_init(struct pci_dev *dev, int offset) @@ -170,26 +220,26 @@ static void bar_release(struct pci_dev *dev, int offset, void *data) kfree(data); } -static int interrupt_read(struct pci_dev *dev, int offset, u8 * value, - void *data) +static int pciback_read_vendor(struct pci_dev *dev, int offset, + u16 *value, void *data) { - *value = (u8) dev->irq; + *value = dev->vendor; return 0; } -static int vendor_read(struct pci_dev *dev, int offset, u16 * value, - void *data) +static int pciback_read_device(struct pci_dev *dev, int offset, + u16 *value, void *data) { - *value = dev->vendor; + *value = dev->device; return 0; } -static int device_read(struct pci_dev *dev, int offset, u16 * value, - void *data) +static int interrupt_read(struct pci_dev *dev, int offset, u8 * value, + void *data) { - *value = dev->device; + *value = (u8) dev->irq; return 0; } @@ -215,17 +265,17 @@ static const struct config_field header_common[] = { { .offset = PCI_VENDOR_ID, .size = 2, - .u.w.read = vendor_read + .u.w.read = pciback_read_vendor, }, { .offset = PCI_DEVICE_ID, .size = 2, - .u.w.read = device_read + .u.w.read = pciback_read_device, }, { .offset = PCI_COMMAND, .size = 2, - .u.w.read = pciback_read_config_word, + .u.w.read = command_read, .u.w.write = command_write, }, {