Skip to content

Commit

Permalink
PCI/x86: don't assume prefetchable ranges are 64bit
Browse files Browse the repository at this point in the history
We should not assign 64bit ranges to PCI devices that only take 32bit
prefetchable addresses.

Try to set IORESOURCE_MEM_64 in 64bit resource of pci_device/pci_bridge
and make the bus resource only have that bit set when all devices under
it support 64bit prefetchable memory.  Use that flag to allocate
resources from that range.

Reported-by: Yannick <yannick.roehlly@free.fr>
Reviewed-by: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Signed-off-by: Yinghai Lu <yinghai@kernel.org>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
  • Loading branch information
Yinghai Lu authored and Jesse Barnes committed Jun 11, 2009
1 parent 67b5db6 commit 1f82de1
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 13 deletions.
1 change: 1 addition & 0 deletions arch/x86/include/asm/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ extern void pci_iommu_alloc(void);

/* generic pci stuff */
#include <asm-generic/pci.h>
#define PCIBIOS_MAX_MEM_32 0xffffffff

#ifdef CONFIG_NUMA
/* Returns the node based on pci bus */
Expand Down
7 changes: 6 additions & 1 deletion drivers/pci/bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,14 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
void *alignf_data)
{
int i, ret = -ENOMEM;
resource_size_t max = -1;

type_mask |= IORESOURCE_IO | IORESOURCE_MEM;

/* don't allocate too high if the pref mem doesn't support 64bit*/
if (!(res->flags & IORESOURCE_MEM_64))
max = PCIBIOS_MAX_MEM_32;

for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
struct resource *r = bus->resource[i];
if (!r)
Expand All @@ -62,7 +67,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;
Expand Down
9 changes: 7 additions & 2 deletions drivers/pci/probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
res->flags |= pci_calc_resource_flags(l) | IORESOURCE_SIZEALIGN;
if (type == pci_bar_io) {
l &= PCI_BASE_ADDRESS_IO_MASK;
mask = PCI_BASE_ADDRESS_IO_MASK & 0xffff;
mask = PCI_BASE_ADDRESS_IO_MASK & IO_SPACE_LIMIT;
} else {
l &= PCI_BASE_ADDRESS_MEM_MASK;
mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;
Expand Down Expand Up @@ -237,6 +237,8 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
dev_printk(KERN_DEBUG, &dev->dev,
"reg %x 64bit mmio: %pR\n", pos, res);
}

res->flags |= IORESOURCE_MEM_64;
} else {
sz = pci_size(l, sz, mask);

Expand Down Expand Up @@ -362,7 +364,10 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child)
}
}
if (base <= limit) {
res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM | IORESOURCE_PREFETCH;
res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) |
IORESOURCE_MEM | IORESOURCE_PREFETCH;
if (res->flags & PCI_PREF_RANGE_TYPE_64)
res->flags |= IORESOURCE_MEM_64;
res->start = base;
res->end = limit + 0xfffff;
dev_printk(KERN_DEBUG, &dev->dev, "bridge %sbit mmio pref: %pR\n",
Expand Down
52 changes: 42 additions & 10 deletions drivers/pci/setup-bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ static void pci_setup_bridge(struct pci_bus *bus)
struct pci_dev *bridge = bus->self;
struct pci_bus_region region;
u32 l, bu, lu, io_upper16;
int pref_mem64;

if (pci_is_enabled(bridge))
return;
Expand Down Expand Up @@ -198,26 +199,34 @@ static void pci_setup_bridge(struct pci_bus *bus)
pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0);

/* Set up PREF base/limit. */
pref_mem64 = 0;
bu = lu = 0;
pcibios_resource_to_bus(bridge, &region, bus->resource[2]);
if (bus->resource[2]->flags & IORESOURCE_PREFETCH) {
int width = 8;
l = (region.start >> 16) & 0xfff0;
l |= region.end & 0xfff00000;
bu = upper_32_bits(region.start);
lu = upper_32_bits(region.end);
dev_info(&bridge->dev, " PREFETCH window: %#016llx-%#016llx\n",
(unsigned long long)region.start,
(unsigned long long)region.end);
if (bus->resource[2]->flags & IORESOURCE_MEM_64) {
pref_mem64 = 1;
bu = upper_32_bits(region.start);
lu = upper_32_bits(region.end);
width = 16;
}
dev_info(&bridge->dev, " PREFETCH window: %#0*llx-%#0*llx\n",
width, (unsigned long long)region.start,
width, (unsigned long long)region.end);
}
else {
l = 0x0000fff0;
dev_info(&bridge->dev, " PREFETCH window: disabled\n");
}
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l);

/* Set the upper 32 bits of PREF base & limit. */
pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu);
pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu);
if (pref_mem64) {
/* Set the upper 32 bits of PREF base & limit. */
pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu);
pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu);
}

pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl);
}
Expand Down Expand Up @@ -255,8 +264,25 @@ static void pci_bridge_check_ranges(struct pci_bus *bus)
pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);
pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0);
}
if (pmem)
if (pmem) {
b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH;
if ((pmem & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64)
b_res[2].flags |= IORESOURCE_MEM_64;
}

/* double check if bridge does support 64 bit pref */
if (b_res[2].flags & IORESOURCE_MEM_64) {
u32 mem_base_hi, tmp;
pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32,
&mem_base_hi);
pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32,
0xffffffff);
pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &tmp);
if (!tmp)
b_res[2].flags &= ~IORESOURCE_MEM_64;
pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32,
mem_base_hi);
}
}

/* Helper function for sizing routines: find first available
Expand Down Expand Up @@ -336,6 +362,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long
resource_size_t aligns[12]; /* Alignments from 1Mb to 2Gb */
int order, max_order;
struct resource *b_res = find_free_bus_resource(bus, type);
unsigned int mem64_mask = 0;

if (!b_res)
return 0;
Expand All @@ -344,9 +371,12 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long
max_order = 0;
size = 0;

mem64_mask = b_res->flags & IORESOURCE_MEM_64;
b_res->flags &= ~IORESOURCE_MEM_64;

list_for_each_entry(dev, &bus->devices, bus_list) {
int i;

for (i = 0; i < PCI_NUM_RESOURCES; i++) {
struct resource *r = &dev->resource[i];
resource_size_t r_size;
Expand All @@ -372,6 +402,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long
aligns[order] += align;
if (order > max_order)
max_order = order;
mem64_mask &= r->flags & IORESOURCE_MEM_64;
}
}

Expand All @@ -396,6 +427,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long
b_res->start = min_align;
b_res->end = size + min_align - 1;
b_res->flags |= IORESOURCE_STARTALIGN;
b_res->flags |= mem64_mask;
return 1;
}

Expand Down
2 changes: 2 additions & 0 deletions include/linux/ioport.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ struct resource_list {
#define IORESOURCE_SIZEALIGN 0x00020000 /* size indicates alignment */
#define IORESOURCE_STARTALIGN 0x00040000 /* start field is alignment */

#define IORESOURCE_MEM_64 0x00100000

#define IORESOURCE_EXCLUSIVE 0x08000000 /* Userland may not map this resource */
#define IORESOURCE_DISABLED 0x10000000
#define IORESOURCE_UNSET 0x20000000
Expand Down
4 changes: 4 additions & 0 deletions include/linux/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,10 @@ static inline struct pci_dev *pci_get_bus_and_slot(unsigned int bus,

#include <asm/pci.h>

#ifndef PCIBIOS_MAX_MEM_32
#define PCIBIOS_MAX_MEM_32 (-1)
#endif

/* these helpers provide future and backwards compatibility
* for accessing popular PCI BAR info */
#define pci_resource_start(dev, bar) ((dev)->resource[(bar)].start)
Expand Down

0 comments on commit 1f82de1

Please sign in to comment.