Re: [Patch] PCI: check szhi when sz is 0 for 64 bit pref mem

From: Yinghai Lu
Date: Mon Nov 06 2006 - 23:57:26 EST


please check version with pci_size64. [PATCH] PCI: check szhi when sz is 0 when 64 bit iomem bigger than 4G

If the PCI device is 64-bit memory and has a size of 0xnnnnnnnn00000000 then
pci_read_bases() will incorrectly assume that it has a size of zero.

Cc: Myles Watson <myles@xxxxxxxxxxxxxxxxxxxxx>
Signed-off-by: Yinghai Lu <yinghai.lu@xxxxxxx>
Cc: Greg KH <greg@xxxxxxxxx>
Signed-off-by: Andrew Morton <akpm@xxxxxxxx>

diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index e159d66..0e2b10c 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -144,6 +144,24 @@ static u32 pci_size(u32 base, u32 maxbas
return size;
}

+static u64 pci_size64(u64 base, u64 maxbase, u64 mask)
+{
+ u64 size = mask & maxbase; /* Find the significant bits */
+ if (!size)
+ return 0;
+
+ /* Get the lowest of them to find the decode size, and
+ from that the extent. */
+ size = (size & ~(size-1)) - 1;
+
+ /* base == maxbase can be valid only if the BAR has
+ already been programmed with all 1s. */
+ if (base == maxbase && ((base | size) & mask) != mask)
+ return 0;
+
+ return size;
+}
+
static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
{
unsigned int pos, reg, next;
@@ -151,6 +169,7 @@ static void pci_read_bases(struct pci_de
struct resource *res;

for(pos=0; pos<howmany; pos = next) {
+ u64 l64, sz64;
next = pos+1;
res = &dev->resource[pos];
res->name = pci_name(dev);
@@ -164,9 +183,15 @@ static void pci_read_bases(struct pci_de
if (l == 0xffffffff)
l = 0;
if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) {
+ sz64 = sz;
sz = pci_size(l, sz, (u32)PCI_BASE_ADDRESS_MEM_MASK);
- if (!sz)
- continue;
+ /* for 64bit pref, sz could be 0, if the real size is bigger than 4G,
+ so need to check szhi for it
+ */
+ if ((l & (PCI_BASE_ADDRESS_SPACE | PCI_BASE_ADDRESS_MEM_TYPE_MASK))
+ != (PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64))
+ if (!sz)
+ continue;
res->start = l & PCI_BASE_ADDRESS_MEM_MASK;
res->flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK;
} else {
@@ -185,17 +210,21 @@ static void pci_read_bases(struct pci_de
pci_write_config_dword(dev, reg+4, ~0);
pci_read_config_dword(dev, reg+4, &szhi);
pci_write_config_dword(dev, reg+4, lhi);
- szhi = pci_size(lhi, szhi, 0xffffffff);
+ sz64 |= ((unsigned long) szhi) << 32;
+ l64 = (((unsigned long) lhi) << 32) | l;
+ sz64 = pci_size64(l64, sz64, PCI_BASE_ADDRESS_MEM_MASK);
next++;
#if BITS_PER_LONG == 64
- res->start |= ((unsigned long) lhi) << 32;
- res->end = res->start + sz;
- if (szhi) {
- /* This BAR needs > 4GB? Wow. */
- res->end |= (unsigned long)szhi<<32;
+ if (!sz64) {
+ res->start = 0;
+ res->end = 0;
+ res->flags = 0;
+ continue;
}
+ res->start = l64 & PCI_BASE_ADDRESS_MEM_MASK;
+ res->end = res->start + sz64;
#else
- if (szhi) {
+ if (sz64>0x100000000ULL) {
printk(KERN_ERR "PCI: Unable to handle 64-bit BAR for device %s\n", pci_name(dev));
res->start = 0;
res->flags = 0;