Browse Source

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6:
  PCI: acpiphp: cleanup notify handler on all root bridges
  PCI: Limit VPD read/write lengths for Broadcom 5706, 5708, 5709 rev.
  PCI: Restrict VPD read permission to root
Linus Torvalds 17 years ago
parent
commit
c461a97311
5 changed files with 63 additions and 16 deletions
  1. 4 10
      drivers/pci/access.c
  2. 14 3
      drivers/pci/hotplug/acpiphp_glue.c
  3. 2 2
      drivers/pci/pci-sysfs.c
  4. 1 1
      drivers/pci/pci.h
  5. 42 0
      drivers/pci/quirks.c

+ 4 - 10
drivers/pci/access.c

@@ -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;

+ 14 - 3
drivers/pci/hotplug/acpiphp_glue.c

@@ -700,9 +700,10 @@ cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
 	acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
 				cleanup_p2p_bridge, NULL, NULL);
 
-	if (!(bridge = acpiphp_handle_to_bridge(handle)))
-		return AE_OK;
-	cleanup_bridge(bridge);
+	bridge = acpiphp_handle_to_bridge(handle);
+	if (bridge)
+		cleanup_bridge(bridge);
+
 	return AE_OK;
 }
 
@@ -715,9 +716,19 @@ static void remove_bridge(acpi_handle handle)
 	acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
 				(u32)1, cleanup_p2p_bridge, NULL, NULL);
 
+	/*
+	 * On root bridges with hotplug slots directly underneath (ie,
+	 * no p2p bridge inbetween), we call cleanup_bridge(). 
+	 *
+	 * The else clause cleans up root bridges that either had no
+	 * hotplug slots at all, or had a p2p bridge underneath.
+	 */
 	bridge = acpiphp_handle_to_bridge(handle);
 	if (bridge)
 		cleanup_bridge(bridge);
+	else
+		acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+					   handle_hotplug_event_bridge);
 }
 
 static struct pci_dev * get_apic_pci_info(acpi_handle handle)

+ 2 - 2
drivers/pci/pci-sysfs.c

@@ -736,9 +736,9 @@ 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_IRUGO | S_IWUSR;
+			attr->attr.mode = S_IRUSR | S_IWUSR;
 			attr->read = pci_read_vpd;
 			attr->write = pci_write_vpd;
 			retval = sysfs_create_bin_file(&pdev->dev.kobj, attr);

+ 1 - 1
drivers/pci/pci.h

@@ -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 */
 };

+ 42 - 0
drivers/pci/quirks.c

@@ -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