]> err.no Git - linux-2.6/commitdiff
PCI: Limit VPD read/write lengths for Broadcom 5706, 5708, 5709 rev.
authorBenjamin Li <benli@broadcom.com>
Wed, 2 Jul 2008 17:59:04 +0000 (10:59 -0700)
committerJesse Barnes <jbarnes@virtuousgeek.org>
Wed, 2 Jul 2008 18:25:54 +0000 (11:25 -0700)
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>
drivers/pci/access.c
drivers/pci/pci-sysfs.c
drivers/pci/pci.h
drivers/pci/quirks.c

index ec8f7002b09d3423a8f5c80d94ed48f001e9d4b4..39bb96b413ef1b696eae53d6239c2d3a29bbd20e 100644 (file)
@@ -178,8 +178,7 @@ static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size,
        int ret;
        int begin, end, i;
 
-       if (pos < 0 || pos > PCI_VPD_PCI22_SIZE ||
-           size > PCI_VPD_PCI22_SIZE  - pos)
+       if (pos < 0 || pos > vpd->base.len || size > vpd->base.len  - pos)
                return -EINVAL;
        if (size == 0)
                return 0;
@@ -223,8 +222,8 @@ static int pci_vpd_pci22_write(struct pci_dev *dev, int pos, int size,
        u32 val;
        int ret;
 
-       if (pos < 0 || pos > PCI_VPD_PCI22_SIZE || pos & 3 ||
-           size > PCI_VPD_PCI22_SIZE - pos || size < 4)
+       if (pos < 0 || pos > vpd->base.len || pos & 3 ||
+           size > vpd->base.len - pos || size < 4)
                return -EINVAL;
 
        val = (u8) *buf++;
@@ -255,11 +254,6 @@ out:
        return 4;
 }
 
-static int pci_vpd_pci22_get_size(struct pci_dev *dev)
-{
-       return PCI_VPD_PCI22_SIZE;
-}
-
 static void pci_vpd_pci22_release(struct pci_dev *dev)
 {
        kfree(container_of(dev->vpd, struct pci_vpd_pci22, base));
@@ -268,7 +262,6 @@ static void pci_vpd_pci22_release(struct pci_dev *dev)
 static struct pci_vpd_ops pci_vpd_pci22_ops = {
        .read = pci_vpd_pci22_read,
        .write = pci_vpd_pci22_write,
-       .get_size = pci_vpd_pci22_get_size,
        .release = pci_vpd_pci22_release,
 };
 
@@ -284,6 +277,7 @@ int pci_vpd_pci22_init(struct pci_dev *dev)
        if (!vpd)
                return -ENOMEM;
 
+       vpd->base.len = PCI_VPD_PCI22_SIZE;
        vpd->base.ops = &pci_vpd_pci22_ops;
        spin_lock_init(&vpd->lock);
        vpd->cap = cap;
index 1f855f028e929f3aa543f58009019a7ee36e3ec0..9c718583a237f4f6082e7dcc5e84e23e85ee651c 100644 (file)
@@ -736,7 +736,7 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
                attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
                if (attr) {
                        pdev->vpd->attr = attr;
-                       attr->size = pdev->vpd->ops->get_size(pdev);
+                       attr->size = pdev->vpd->len;
                        attr->attr.name = "vpd";
                        attr->attr.mode = S_IRUSR | S_IWUSR;
                        attr->read = pci_read_vpd;
index 0a497c1b4227c0b80df7be89f56dcc8c5aba55f6..00408c97e5fc8cc2e38f52b408c70fdab9d133a8 100644 (file)
@@ -21,11 +21,11 @@ extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val);
 struct pci_vpd_ops {
        int (*read)(struct pci_dev *dev, int pos, int size, char *buf);
        int (*write)(struct pci_dev *dev, int pos, int size, const char *buf);
-       int (*get_size)(struct pci_dev *dev);
        void (*release)(struct pci_dev *dev);
 };
 
 struct pci_vpd {
+       unsigned int len;
        struct pci_vpd_ops *ops;
        struct bin_attribute *attr; /* descriptor for sysfs VPD entry */
 };
index dabb563f51d9466e4379f9095c85d2ce04545925..a3497dc6ebcfd3c8fb3a94de549f571111c439fe 100644 (file)
@@ -1670,6 +1670,48 @@ static void __devinit quirk_via_cx700_pci_parking_caching(struct pci_dev *dev)
 }
 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_VIA, 0x324e, quirk_via_cx700_pci_parking_caching);
 
+/*
+ * 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.
+ */
+static void __devinit quirk_brcm_570x_limit_vpd(struct pci_dev *dev)
+{
+       /*  Only disable the VPD capability for 5706, 5708, and 5709 rev. A */
+       if ((dev->device == PCI_DEVICE_ID_NX2_5706) ||
+           (dev->device == PCI_DEVICE_ID_NX2_5708) ||
+           ((dev->device == PCI_DEVICE_ID_NX2_5709) &&
+            (dev->revision & 0xf0) == 0x0)) {
+               if (dev->vpd)
+                       dev->vpd->len = 0x80;
+       }
+}
+
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
+                        PCI_DEVICE_ID_NX2_5706,
+                        quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
+                        PCI_DEVICE_ID_NX2_5706S,
+                        quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
+                        PCI_DEVICE_ID_NX2_5708,
+                        quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
+                        PCI_DEVICE_ID_NX2_5708S,
+                        quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
+                        PCI_DEVICE_ID_NX2_5709,
+                        quirk_brcm_570x_limit_vpd);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
+                        PCI_DEVICE_ID_NX2_5709S,
+                        quirk_brcm_570x_limit_vpd);
+
 #ifdef CONFIG_PCI_MSI
 /* Some chipsets do not support MSI. We cannot easily rely on setting
  * PCI_BUS_FLAGS_NO_MSI in its bus flags because there are actually