Skip to content

Commit

Permalink
PCI: check szhi when sz is 0 when 64 bit iomem bigger than 4G
Browse files Browse the repository at this point in the history
For pci mem resource that size is bigger than 4G, the sz returned by
pc_size will be 0.
So that resource is skipped, and register contained hi address will be
treated as another 32bit resource. We need to use sz64 and pci_sz64 for
64 bit resource for clear logical.  Typical usages for this: Opteron
system with co-processor and the co-processor could take more than 4G
RAM as pre-fetchable mem resource.


Signed-off-by: Yinghai Lu <yinghai.lu@amd.com>
Cc: Andi Kleen <ak@suse.de>
Cc: Andrew Morton <akpm@osdl.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Yinghai Lu authored and Greg Kroah-Hartman committed Feb 7, 2007
1 parent 5331be0 commit 07eddf3
Showing 1 changed file with 56 additions and 13 deletions.
69 changes: 56 additions & 13 deletions drivers/pci/probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,43 @@ static u32 pci_size(u32 base, u32 maxbase, u32 mask)
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 inline int is_64bit_memory(u32 mask)
{
if ((mask & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) ==
(PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64))
return 1;
return 0;
}

static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
{
unsigned int pos, reg, next;
u32 l, sz;
struct resource *res;

for(pos=0; pos<howmany; pos = next) {
u64 l64;
u64 sz64;
u32 raw_sz;

next = pos+1;
res = &dev->resource[pos];
res->name = pci_name(dev);
Expand All @@ -163,9 +193,16 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
continue;
if (l == 0xffffffff)
l = 0;
if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) {
raw_sz = sz;
if ((l & PCI_BASE_ADDRESS_SPACE) ==
PCI_BASE_ADDRESS_SPACE_MEMORY) {
sz = pci_size(l, sz, (u32)PCI_BASE_ADDRESS_MEM_MASK);
if (!sz)
/*
* For 64bit prefetchable memory sz could be 0, if the
* real size is bigger than 4G, so we need to check
* szhi for that.
*/
if (!is_64bit_memory(l) && !sz)
continue;
res->start = l & PCI_BASE_ADDRESS_MEM_MASK;
res->flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK;
Expand All @@ -178,30 +215,36 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
}
res->end = res->start + (unsigned long) sz;
res->flags |= pci_calc_resource_flags(l);
if ((l & (PCI_BASE_ADDRESS_SPACE | PCI_BASE_ADDRESS_MEM_TYPE_MASK))
== (PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64)) {
if (is_64bit_memory(l)) {
u32 szhi, lhi;

pci_read_config_dword(dev, reg+4, &lhi);
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 = ((u64)szhi << 32) | raw_sz;
l64 = ((u64)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) {
printk(KERN_ERR "PCI: Unable to handle 64-bit BAR for device %s\n", pci_name(dev));
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;
} else if (lhi) {
/* 64-bit wide address, treat as disabled */
pci_write_config_dword(dev, reg, l & ~(u32)PCI_BASE_ADDRESS_MEM_MASK);
pci_write_config_dword(dev, reg,
l & ~(u32)PCI_BASE_ADDRESS_MEM_MASK);
pci_write_config_dword(dev, reg+4, 0);
res->start = 0;
res->end = sz;
Expand Down

0 comments on commit 07eddf3

Please sign in to comment.