aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorBenjamin Li <benli@broadcom.com>2008-07-02 13:59:04 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2008-07-02 14:25:54 -0400
commit99cb233d60cbe644203f19938c729ea2bb004d70 (patch)
tree20fb5b9194982ff50d6795dc435dd8e09264233e /drivers/pci
parenta94c248113b86bbbc47d027a4004b70f2be298b1 (diff)
PCI: Limit VPD read/write lengths for Broadcom 5706, 5708, 5709 rev.
For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the VPD end tag will hang the device. This problem was initially observed when a vpd entry was created in sysfs ('/sys/bus/pci/devices/<id>/vpd'). A read to this sysfs entry will dump 32k of data. Reading a full 32k will cause an access beyond the VPD end tag causing the device to hang. Once the device is hung, the bnx2 driver will not be able to reset the device. We believe that it is legal to read beyond the end tag and therefore the solution is to limit the read/write length. A majority of this patch is from Matthew Wilcox who gave code for reworking the PCI vpd size information. A PCI quirk added for the Broadcom NIC's to limit the read/write's. Signed-off-by: Benjamin Li <benli@broadcom.com> Signed-off-by: Matthew Wilcox <willy@linux.intel.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/access.c14
-rw-r--r--drivers/pci/pci-sysfs.c2
-rw-r--r--drivers/pci/pci.h2
-rw-r--r--drivers/pci/quirks.c42
4 files changed, 48 insertions, 12 deletions
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index ec8f7002b09d..39bb96b413ef 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -178,8 +178,7 @@ static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size,
178 int ret; 178 int ret;
179 int begin, end, i; 179 int begin, end, i;
180 180
181 if (pos < 0 || pos > PCI_VPD_PCI22_SIZE || 181 if (pos < 0 || pos > vpd->base.len || size > vpd->base.len - pos)
182 size > PCI_VPD_PCI22_SIZE - pos)
183 return -EINVAL; 182 return -EINVAL;
184 if (size == 0) 183 if (size == 0)
185 return 0; 184 return 0;
@@ -223,8 +222,8 @@ static int pci_vpd_pci22_write(struct pci_dev *dev, int pos, int size,
223 u32 val; 222 u32 val;
224 int ret; 223 int ret;
225 224
226 if (pos < 0 || pos > PCI_VPD_PCI22_SIZE || pos & 3 || 225 if (pos < 0 || pos > vpd->base.len || pos & 3 ||
227 size > PCI_VPD_PCI22_SIZE - pos || size < 4) 226 size > vpd->base.len - pos || size < 4)
228 return -EINVAL; 227 return -EINVAL;
229 228
230 val = (u8) *buf++; 229 val = (u8) *buf++;
@@ -255,11 +254,6 @@ out:
255 return 4; 254 return 4;
256} 255}
257 256
258static int pci_vpd_pci22_get_size(struct pci_dev *dev)
259{
260 return PCI_VPD_PCI22_SIZE;
261}
262
263static void pci_vpd_pci22_release(struct pci_dev *dev) 257static void pci_vpd_pci22_release(struct pci_dev *dev)
264{ 258{
265 kfree(container_of(dev->vpd, struct pci_vpd_pci22, base)); 259 kfree(container_of(dev->vpd, struct pci_vpd_pci22, base));
@@ -268,7 +262,6 @@ static void pci_vpd_pci22_release(struct pci_dev *dev)
268static struct pci_vpd_ops pci_vpd_pci22_ops = { 262static struct pci_vpd_ops pci_vpd_pci22_ops = {
269 .read = pci_vpd_pci22_read, 263 .read = pci_vpd_pci22_read,
270 .write = pci_vpd_pci22_write, 264 .write = pci_vpd_pci22_write,
271 .get_size = pci_vpd_pci22_get_size,
272 .release = pci_vpd_pci22_release, 265 .release = pci_vpd_pci22_release,
273}; 266};
274 267
@@ -284,6 +277,7 @@ int pci_vpd_pci22_init(struct pci_dev *dev)
284 if (!vpd) 277 if (!vpd)
285 return -ENOMEM; 278 return -ENOMEM;
286 279
280 vpd->base.len = PCI_VPD_PCI22_SIZE;
287 vpd->base.ops = &pci_vpd_pci22_ops; 281 vpd->base.ops = &pci_vpd_pci22_ops;
288 spin_lock_init(&vpd->lock); 282 spin_lock_init(&vpd->lock);
289 vpd->cap = cap; 283 vpd->cap = cap;
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 1f855f028e92..9c718583a237 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -736,7 +736,7 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
736 attr = kzalloc(sizeof(*attr), GFP_ATOMIC); 736 attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
737 if (attr) { 737 if (attr) {
738 pdev->vpd->attr = attr; 738 pdev->vpd->attr = attr;
739 attr->size = pdev->vpd->ops->get_size(pdev); 739 attr->size = pdev->vpd->len;
740 attr->attr.name = "vpd"; 740 attr->attr.name = "vpd";
741 attr->attr.mode = S_IRUSR | S_IWUSR; 741 attr->attr.mode = S_IRUSR | S_IWUSR;
742 attr->read = pci_read_vpd; 742 attr->read = pci_read_vpd;
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 0a497c1b4227..00408c97e5fc 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -21,11 +21,11 @@ extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val);
21struct pci_vpd_ops { 21struct pci_vpd_ops {
22 int (*read)(struct pci_dev *dev, int pos, int size, char *buf); 22 int (*read)(struct pci_dev *dev, int pos, int size, char *buf);
23 int (*write)(struct pci_dev *dev, int pos, int size, const char *buf); 23 int (*write)(struct pci_dev *dev, int pos, int size, const char *buf);
24 int (*get_size)(struct pci_dev *dev);
25 void (*release)(struct pci_dev *dev); 24 void (*release)(struct pci_dev *dev);
26}; 25};
27 26
28struct pci_vpd { 27struct pci_vpd {
28 unsigned int len;
29 struct pci_vpd_ops *ops; 29 struct pci_vpd_ops *ops;
30 struct bin_attribute *attr; /* descriptor for sysfs VPD entry */ 30 struct bin_attribute *attr; /* descriptor for sysfs VPD entry */
31}; 31};
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index dabb563f51d9..a3497dc6ebcf 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -1670,6 +1670,48 @@ static void __devinit quirk_via_cx700_pci_parking_caching(struct pci_dev *dev)
1670} 1670}
1671DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_VIA, 0x324e, quirk_via_cx700_pci_parking_caching); 1671DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_VIA, 0x324e, quirk_via_cx700_pci_parking_caching);
1672 1672
1673/*
1674 * For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the
1675 * VPD end tag will hang the device. This problem was initially
1676 * observed when a vpd entry was created in sysfs
1677 * ('/sys/bus/pci/devices/<id>/vpd'). A read to this sysfs entry
1678 * will dump 32k of data. Reading a full 32k will cause an access
1679 * beyond the VPD end tag causing the device to hang. Once the device
1680 * is hung, the bnx2 driver will not be able to reset the device.
1681 * We believe that it is legal to read beyond the end tag and
1682 * therefore the solution is to limit the read/write length.
1683 */
1684static void __devinit quirk_brcm_570x_limit_vpd(struct pci_dev *dev)
1685{
1686 /* Only disable the VPD capability for 5706, 5708, and 5709 rev. A */
1687 if ((dev->device == PCI_DEVICE_ID_NX2_5706) ||
1688 (dev->device == PCI_DEVICE_ID_NX2_5708) ||
1689 ((dev->device == PCI_DEVICE_ID_NX2_5709) &&
1690 (dev->revision & 0xf0) == 0x0)) {
1691 if (dev->vpd)
1692 dev->vpd->len = 0x80;
1693 }
1694}
1695
1696DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
1697 PCI_DEVICE_ID_NX2_5706,
1698 quirk_brcm_570x_limit_vpd);
1699DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
1700 PCI_DEVICE_ID_NX2_5706S,
1701 quirk_brcm_570x_limit_vpd);
1702DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
1703 PCI_DEVICE_ID_NX2_5708,
1704 quirk_brcm_570x_limit_vpd);
1705DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
1706 PCI_DEVICE_ID_NX2_5708S,
1707 quirk_brcm_570x_limit_vpd);
1708DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
1709 PCI_DEVICE_ID_NX2_5709,
1710 quirk_brcm_570x_limit_vpd);
1711DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
1712 PCI_DEVICE_ID_NX2_5709S,
1713 quirk_brcm_570x_limit_vpd);
1714
1673#ifdef CONFIG_PCI_MSI 1715#ifdef CONFIG_PCI_MSI
1674/* Some chipsets do not support MSI. We cannot easily rely on setting 1716/* Some chipsets do not support MSI. We cannot easily rely on setting
1675 * PCI_BUS_FLAGS_NO_MSI in its bus flags because there are actually 1717 * PCI_BUS_FLAGS_NO_MSI in its bus flags because there are actually