Commit 99cb233d60cbe644203f19938c729ea2bb004d70

Authored by Benjamin Li
Committed by Jesse Barnes
1 parent a94c248113

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>

Showing 4 changed files with 48 additions and 12 deletions Side-by-side Diff

drivers/pci/access.c
... ... @@ -178,8 +178,7 @@
178 178 int ret;
179 179 int begin, end, i;
180 180  
181   - if (pos < 0 || pos > PCI_VPD_PCI22_SIZE ||
182   - size > PCI_VPD_PCI22_SIZE - pos)
  181 + if (pos < 0 || pos > vpd->base.len || size > vpd->base.len - pos)
183 182 return -EINVAL;
184 183 if (size == 0)
185 184 return 0;
... ... @@ -223,8 +222,8 @@
223 222 u32 val;
224 223 int ret;
225 224  
226   - if (pos < 0 || pos > PCI_VPD_PCI22_SIZE || pos & 3 ||
227   - size > PCI_VPD_PCI22_SIZE - pos || size < 4)
  225 + if (pos < 0 || pos > vpd->base.len || pos & 3 ||
  226 + size > vpd->base.len - pos || size < 4)
228 227 return -EINVAL;
229 228  
230 229 val = (u8) *buf++;
... ... @@ -255,11 +254,6 @@
255 254 return 4;
256 255 }
257 256  
258   -static int pci_vpd_pci22_get_size(struct pci_dev *dev)
259   -{
260   - return PCI_VPD_PCI22_SIZE;
261   -}
262   -
263 257 static void pci_vpd_pci22_release(struct pci_dev *dev)
264 258 {
265 259 kfree(container_of(dev->vpd, struct pci_vpd_pci22, base));
... ... @@ -268,7 +262,6 @@
268 262 static struct pci_vpd_ops pci_vpd_pci22_ops = {
269 263 .read = pci_vpd_pci22_read,
270 264 .write = pci_vpd_pci22_write,
271   - .get_size = pci_vpd_pci22_get_size,
272 265 .release = pci_vpd_pci22_release,
273 266 };
274 267  
... ... @@ -284,6 +277,7 @@
284 277 if (!vpd)
285 278 return -ENOMEM;
286 279  
  280 + vpd->base.len = PCI_VPD_PCI22_SIZE;
287 281 vpd->base.ops = &pci_vpd_pci22_ops;
288 282 spin_lock_init(&vpd->lock);
289 283 vpd->cap = cap;
drivers/pci/pci-sysfs.c
... ... @@ -736,7 +736,7 @@
736 736 attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
737 737 if (attr) {
738 738 pdev->vpd->attr = attr;
739   - attr->size = pdev->vpd->ops->get_size(pdev);
  739 + attr->size = pdev->vpd->len;
740 740 attr->attr.name = "vpd";
741 741 attr->attr.mode = S_IRUSR | S_IWUSR;
742 742 attr->read = pci_read_vpd;
... ... @@ -21,11 +21,11 @@
21 21 struct pci_vpd_ops {
22 22 int (*read)(struct pci_dev *dev, int pos, int size, char *buf);
23 23 int (*write)(struct pci_dev *dev, int pos, int size, const char *buf);
24   - int (*get_size)(struct pci_dev *dev);
25 24 void (*release)(struct pci_dev *dev);
26 25 };
27 26  
28 27 struct pci_vpd {
  28 + unsigned int len;
29 29 struct pci_vpd_ops *ops;
30 30 struct bin_attribute *attr; /* descriptor for sysfs VPD entry */
31 31 };
drivers/pci/quirks.c
... ... @@ -1670,6 +1670,48 @@
1670 1670 }
1671 1671 DECLARE_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 + */
  1684 +static 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 +
  1696 +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
  1697 + PCI_DEVICE_ID_NX2_5706,
  1698 + quirk_brcm_570x_limit_vpd);
  1699 +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
  1700 + PCI_DEVICE_ID_NX2_5706S,
  1701 + quirk_brcm_570x_limit_vpd);
  1702 +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
  1703 + PCI_DEVICE_ID_NX2_5708,
  1704 + quirk_brcm_570x_limit_vpd);
  1705 +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
  1706 + PCI_DEVICE_ID_NX2_5708S,
  1707 + quirk_brcm_570x_limit_vpd);
  1708 +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
  1709 + PCI_DEVICE_ID_NX2_5709,
  1710 + quirk_brcm_570x_limit_vpd);
  1711 +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM,
  1712 + PCI_DEVICE_ID_NX2_5709S,
  1713 + quirk_brcm_570x_limit_vpd);
  1714 +
1673 1715 #ifdef CONFIG_PCI_MSI
1674 1716 /* Some chipsets do not support MSI. We cannot easily rely on setting
1675 1717 * PCI_BUS_FLAGS_NO_MSI in its bus flags because there are actually