diff options
author | Benjamin Li <benli@broadcom.com> | 2008-07-02 13:59:04 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2008-07-02 14:25:54 -0400 |
commit | 99cb233d60cbe644203f19938c729ea2bb004d70 (patch) | |
tree | 20fb5b9194982ff50d6795dc435dd8e09264233e /drivers/pci/access.c | |
parent | a94c248113b86bbbc47d027a4004b70f2be298b1 (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/access.c')
-rw-r--r-- | drivers/pci/access.c | 14 |
1 files changed, 4 insertions, 10 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 | ||
258 | static int pci_vpd_pci22_get_size(struct pci_dev *dev) | ||
259 | { | ||
260 | return PCI_VPD_PCI22_SIZE; | ||
261 | } | ||
262 | |||
263 | static void pci_vpd_pci22_release(struct pci_dev *dev) | 257 | static 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) | |||
268 | static struct pci_vpd_ops pci_vpd_pci22_ops = { | 262 | static 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; |