[PATCH v2] PCI: don't allocate resource above 4G for 32-bit BAR

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Yu Zhao <yu.zhao@xxxxxxxxx>
---
 drivers/char/agp/intel-agp.c    |    2 +-
 drivers/pci/bus.c               |    5 ++-
 drivers/pci/setup-res.c         |   70 ++++++++++++++++++++++++++++++++++++--
 drivers/pcmcia/rsrc_mgr.c       |    2 +-
 drivers/pcmcia/rsrc_nonstatic.c |    4 +-
 include/linux/pci.h             |    2 +-
 6 files changed, 74 insertions(+), 11 deletions(-)

diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c
index c771418..a811fc4 100644
--- a/drivers/char/agp/intel-agp.c
+++ b/drivers/char/agp/intel-agp.c
@@ -886,7 +886,7 @@ static int intel_alloc_chipset_flush_resource(void)
 {
 	int ret;
 	ret = pci_bus_alloc_resource(agp_bridge->dev->bus, &intel_private.ifp_resource, PAGE_SIZE,
-				     PAGE_SIZE, PCIBIOS_MIN_MEM, 0,
+				     PAGE_SIZE, PCIBIOS_MIN_MEM, -1, 0,
 				     pcibios_align_resource, agp_bridge->dev);
 
 	return ret;
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 118c777..6821cd3 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -35,7 +35,8 @@
 int
 pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 		resource_size_t size, resource_size_t align,
-		resource_size_t min, unsigned int type_mask,
+		resource_size_t min, resource_size_t max,
+		unsigned int type_mask,
 		void (*alignf)(void *, struct resource *, resource_size_t,
 				resource_size_t),
 		void *alignf_data)
@@ -62,7 +63,7 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
 		/* Ok, try it out.. */
 		ret = allocate_resource(r, res, size,
 					r->start ? : min,
-					-1, align,
+					max, align,
 					alignf, alignf_data);
 		if (ret == 0)
 			break;
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index 32e8d88..6685250 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -120,11 +120,71 @@ int pci_claim_resource(struct pci_dev *dev, int resource)
 	return err;
 }
 
+static resource_size_t pci_resource_limit(struct pci_dev *dev, int resno)
+{
+	u8 io;
+	u16 mem;
+	struct resource r, *res = dev->resource + resno;
+	struct pci_bus_region region;
+
+	if (resno < PCI_BRIDGE_RESOURCES) {
+		if ((res->flags & PCI_BASE_ADDRESS_SPACE) ==
+				PCI_BASE_ADDRESS_SPACE_MEMORY &&
+		    (res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
+				PCI_BASE_ADDRESS_MEM_TYPE_64) {
+			/* Memory BAR could be 64-bit */
+			region.end = (resource_size_t)(u64)-1;
+		} else {
+			/* I/O and ROM BAR are always 32-bit */
+			region.end = (u32)-1;
+		}
+	} else if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+		switch (resno - PCI_BRIDGE_RESOURCES) {
+		case PCI_BRIDGE_IO:
+			/* I/O window could be 16 or 32-bit */
+			pci_read_config_byte(dev, PCI_IO_BASE, &io);
+			region.end = (io & 0xf) ? (u32)-1 : (u16)-1;
+			break;
+		case PCI_BRIDGE_MEM:
+			/* Memory window is always 32-bit */
+			region.end = (u32)-1;
+			break;
+		case PCI_BRIDGE_PREF_MEM:
+			/* Prefetch memory window could be 32 or 64-bit */
+			pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem);
+			region.end = (mem & 0xf) ? (resource_size_t)(u64)-1 :
+						   (u32)-1;
+			break;
+		default:
+			goto bad;
+		}
+	} else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
+		if (resno - PCI_BRIDGE_RESOURCES > PCI_CARDBUS_MAX_RESOURCE)
+			goto bad;
+
+		/* Cardbus resource windows are always 32-bit??? */
+		region.end = (u32)-1;
+	} else {
+		dev_err(&dev->dev, "invalid header type: %d\n", dev->hdr_type);
+		goto bad;
+	}
+
+	pcibios_bus_to_resource(dev, &r, &region);
+
+	return r.end;
+
+bad:
+	dev_err(&dev->dev, "BAR %d: unknown resource %pR flags %#lx\n",
+		resno, res, res->flags);
+
+	return -1;
+}
+
 int pci_assign_resource(struct pci_dev *dev, int resno)
 {
 	struct pci_bus *bus = dev->bus;
 	struct resource *res = dev->resource + resno;
-	resource_size_t size, min, align;
+	resource_size_t size, min, max, align;
 	int ret;
 
 	size = resource_size(res);
@@ -138,8 +198,10 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
 		return -EINVAL;
 	}
 
+	max = pci_resource_limit(dev, resno);
+
 	/* First, try exact prefetching match.. */
-	ret = pci_bus_alloc_resource(bus, res, size, align, min,
+	ret = pci_bus_alloc_resource(bus, res, size, align, min, max,
 				     IORESOURCE_PREFETCH,
 				     pcibios_align_resource, dev);
 
@@ -150,8 +212,8 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
 		 * But a prefetching area can handle a non-prefetching
 		 * window (it will just not perform as well).
 		 */
-		ret = pci_bus_alloc_resource(bus, res, size, align, min, 0,
-					     pcibios_align_resource, dev);
+		ret = pci_bus_alloc_resource(bus, res, size, align, min, max,
+					     0, pcibios_align_resource, dev);
 	}
 
 	if (ret) {
diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c
index c0e2afc..35bd25e 100644
--- a/drivers/pcmcia/rsrc_mgr.c
+++ b/drivers/pcmcia/rsrc_mgr.c
@@ -167,7 +167,7 @@ static struct resource *iodyn_find_io_region(unsigned long base, int num,
 #ifdef CONFIG_PCI
 	if (s->cb_dev) {
 		ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
-					     min, 0, pcmcia_align, &data);
+					     min, -1, 0, pcmcia_align, &data);
 	} else
 #endif
 		ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c
index 9ca22c7..46cb9b7 100644
--- a/drivers/pcmcia/rsrc_nonstatic.c
+++ b/drivers/pcmcia/rsrc_nonstatic.c
@@ -641,7 +641,7 @@ static struct resource *nonstatic_find_io_region(unsigned long base, int num,
 #ifdef CONFIG_PCI
 	if (s->cb_dev) {
 		ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
-					     min, 0, pcmcia_align, &data);
+					     min, -1, 0, pcmcia_align, &data);
 	} else
 #endif
 		ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
@@ -683,7 +683,7 @@ static struct resource * nonstatic_find_mem_region(u_long base, u_long num,
 #ifdef CONFIG_PCI
 		if (s->cb_dev) {
 			ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num,
-						     1, min, 0,
+						     1, min, -1, 0,
 						     pcmcia_align, &data);
 		} else
 #endif
diff --git a/include/linux/pci.h b/include/linux/pci.h
index bebc745..1ed2edf 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -748,7 +748,7 @@ void pci_release_selected_regions(struct pci_dev *, int);
 int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
 			struct resource *res, resource_size_t size,
 			resource_size_t align, resource_size_t min,
-			unsigned int type_mask,
+			resource_size_t max, unsigned int type_mask,
 			void (*alignf)(void *, struct resource *,
 				resource_size_t, resource_size_t),
 			void *alignf_data);
-- 
1.5.6.4

--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux