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: sysfs: fix printk warnings
  PCI: fix pci_bus_alloc_resource() hang, prefer positive decode
  PCI: read current power state at enable time
  PCI: fix size checks for mmap() on /proc/bus/pci files
  x86/PCI: coalesce overlapping host bridge windows
  PCI hotplug: ibmphp: Add check to prevent reading beyond mapped area
This commit is contained in:
Linus Torvalds 2010-11-15 14:01:33 -08:00
commit e5c13537b0
7 changed files with 174 additions and 49 deletions

View file

@ -64,17 +64,57 @@ void pci_bus_remove_resources(struct pci_bus *bus)
}
}
static bool pci_bus_resource_better(struct resource *res1, bool pos1,
struct resource *res2, bool pos2)
{
/* If exactly one is positive decode, always prefer that one */
if (pos1 != pos2)
return pos1 ? true : false;
/* Prefer the one that contains the highest address */
if (res1->end != res2->end)
return (res1->end > res2->end) ? true : false;
/* Otherwise, prefer the one with highest "center of gravity" */
if (res1->start != res2->start)
return (res1->start > res2->start) ? true : false;
/* Otherwise, choose one arbitrarily (but consistently) */
return (res1 > res2) ? true : false;
}
static bool pci_bus_resource_positive(struct pci_bus *bus, struct resource *res)
{
struct pci_bus_resource *bus_res;
/*
* This relies on the fact that pci_bus.resource[] refers to P2P or
* CardBus bridge base/limit registers, which are always positively
* decoded. The pci_bus.resources list contains host bridge or
* subtractively decoded resources.
*/
list_for_each_entry(bus_res, &bus->resources, list) {
if (bus_res->res == res)
return (bus_res->flags & PCI_SUBTRACTIVE_DECODE) ?
false : true;
}
return true;
}
/*
* Find the highest-address bus resource below the cursor "res". If the
* cursor is NULL, return the highest resource.
* Find the next-best bus resource after the cursor "res". If the cursor is
* NULL, return the best resource. "Best" means that we prefer positive
* decode regions over subtractive decode, then those at higher addresses.
*/
static struct resource *pci_bus_find_resource_prev(struct pci_bus *bus,
unsigned int type,
struct resource *res)
{
bool res_pos, r_pos, prev_pos = false;
struct resource *r, *prev = NULL;
int i;
res_pos = pci_bus_resource_positive(bus, res);
pci_bus_for_each_resource(bus, r, i) {
if (!r)
continue;
@ -82,26 +122,14 @@ static struct resource *pci_bus_find_resource_prev(struct pci_bus *bus,
if ((r->flags & IORESOURCE_TYPE_BITS) != type)
continue;
/* If this resource is at or past the cursor, skip it */
if (res) {
if (r == res)
continue;
if (r->end > res->end)
continue;
if (r->end == res->end && r->start > res->start)
continue;
r_pos = pci_bus_resource_positive(bus, r);
if (!res || pci_bus_resource_better(res, res_pos, r, r_pos)) {
if (!prev || pci_bus_resource_better(r, r_pos,
prev, prev_pos)) {
prev = r;
prev_pos = r_pos;
}
}
if (!prev)
prev = r;
/*
* A small resource is higher than a large one that ends at
* the same address.
*/
if (r->end > prev->end ||
(r->end == prev->end && r->start > prev->start))
prev = r;
}
return prev;

View file

@ -276,6 +276,12 @@ int __init ibmphp_access_ebda (void)
for (;;) {
offset = next_offset;
/* Make sure what we read is still in the mapped section */
if (WARN(offset > (ebda_sz * 1024 - 4),
"ibmphp_ebda: next read is beyond ebda_sz\n"))
break;
next_offset = readw (io_mem + offset); /* offset of next blk */
offset += 2;

View file

@ -705,17 +705,21 @@ void pci_remove_legacy_files(struct pci_bus *b)
#ifdef HAVE_PCI_MMAP
int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma)
int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma,
enum pci_mmap_api mmap_api)
{
unsigned long nr, start, size;
unsigned long nr, start, size, pci_start;
if (pci_resource_len(pdev, resno) == 0)
return 0;
nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
start = vma->vm_pgoff;
size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1;
if (start < size && size - start >= nr)
pci_start = (mmap_api == PCI_MMAP_SYSFS) ?
pci_resource_start(pdev, resno) >> PAGE_SHIFT : 0;
if (start >= pci_start && start < pci_start + size &&
start + nr <= pci_start + size)
return 1;
WARN(1, "process \"%s\" tried to map 0x%08lx-0x%08lx on %s BAR %d (size 0x%08lx)\n",
current->comm, start, start+nr, pci_name(pdev), resno, size);
return 0;
}
@ -745,8 +749,15 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
if (i >= PCI_ROM_RESOURCE)
return -ENODEV;
if (!pci_mmap_fits(pdev, i, vma))
if (!pci_mmap_fits(pdev, i, vma, PCI_MMAP_SYSFS)) {
WARN(1, "process \"%s\" tried to map 0x%08lx bytes "
"at page 0x%08lx on %s BAR %d (start 0x%16Lx, size 0x%16Lx)\n",
current->comm, vma->vm_end-vma->vm_start, vma->vm_pgoff,
pci_name(pdev), i,
(u64)pci_resource_start(pdev, i),
(u64)pci_resource_len(pdev, i));
return -EINVAL;
}
/* pci_mmap_page_range() expects the same kind of entry as coming
* from /proc/bus/pci/ which is a "user visible" value. If this is

View file

@ -1007,6 +1007,18 @@ static int __pci_enable_device_flags(struct pci_dev *dev,
int err;
int i, bars = 0;
/*
* Power state could be unknown at this point, either due to a fresh
* boot or a device removal call. So get the current power state
* so that things like MSI message writing will behave as expected
* (e.g. if the device really is in D0 at enable time).
*/
if (dev->pm_cap) {
u16 pmcsr;
pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
}
if (atomic_add_return(1, &dev->enable_cnt) > 1)
return 0; /* already enabled */

View file

@ -22,8 +22,13 @@ extern void pci_remove_firmware_label_files(struct pci_dev *pdev);
#endif
extern void pci_cleanup_rom(struct pci_dev *dev);
#ifdef HAVE_PCI_MMAP
enum pci_mmap_api {
PCI_MMAP_SYSFS, /* mmap on /sys/bus/pci/devices/<BDF>/resource<N> */
PCI_MMAP_PROCFS /* mmap on /proc/bus/pci/<BDF> */
};
extern int pci_mmap_fits(struct pci_dev *pdev, int resno,
struct vm_area_struct *vma);
struct vm_area_struct *vmai,
enum pci_mmap_api mmap_api);
#endif
int pci_probe_reset_function(struct pci_dev *dev);

View file

@ -257,7 +257,7 @@ static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma)
/* Make sure the caller is mapping a real resource for this device */
for (i = 0; i < PCI_ROM_RESOURCE; i++) {
if (pci_mmap_fits(dev, i, vma))
if (pci_mmap_fits(dev, i, vma, PCI_MMAP_PROCFS))
break;
}