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:
commit
e5c13537b0
|
@ -138,7 +138,6 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
|
||||||
struct acpi_resource_address64 addr;
|
struct acpi_resource_address64 addr;
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct resource *root, *conflict;
|
|
||||||
u64 start, end;
|
u64 start, end;
|
||||||
|
|
||||||
status = resource_to_addr(acpi_res, &addr);
|
status = resource_to_addr(acpi_res, &addr);
|
||||||
|
@ -146,12 +145,10 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
|
|
||||||
if (addr.resource_type == ACPI_MEMORY_RANGE) {
|
if (addr.resource_type == ACPI_MEMORY_RANGE) {
|
||||||
root = &iomem_resource;
|
|
||||||
flags = IORESOURCE_MEM;
|
flags = IORESOURCE_MEM;
|
||||||
if (addr.info.mem.caching == ACPI_PREFETCHABLE_MEMORY)
|
if (addr.info.mem.caching == ACPI_PREFETCHABLE_MEMORY)
|
||||||
flags |= IORESOURCE_PREFETCH;
|
flags |= IORESOURCE_PREFETCH;
|
||||||
} else if (addr.resource_type == ACPI_IO_RANGE) {
|
} else if (addr.resource_type == ACPI_IO_RANGE) {
|
||||||
root = &ioport_resource;
|
|
||||||
flags = IORESOURCE_IO;
|
flags = IORESOURCE_IO;
|
||||||
} else
|
} else
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
|
@ -172,27 +169,92 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
conflict = insert_resource_conflict(root, res);
|
info->res_num++;
|
||||||
if (conflict) {
|
if (addr.translation_offset)
|
||||||
dev_err(&info->bridge->dev,
|
dev_info(&info->bridge->dev, "host bridge window %pR "
|
||||||
"address space collision: host bridge window %pR "
|
"(PCI address [%#llx-%#llx])\n",
|
||||||
"conflicts with %s %pR\n",
|
res, res->start - addr.translation_offset,
|
||||||
res, conflict->name, conflict);
|
res->end - addr.translation_offset);
|
||||||
} else {
|
else
|
||||||
pci_bus_add_resource(info->bus, res, 0);
|
dev_info(&info->bridge->dev, "host bridge window %pR\n", res);
|
||||||
info->res_num++;
|
|
||||||
if (addr.translation_offset)
|
|
||||||
dev_info(&info->bridge->dev, "host bridge window %pR "
|
|
||||||
"(PCI address [%#llx-%#llx])\n",
|
|
||||||
res, res->start - addr.translation_offset,
|
|
||||||
res->end - addr.translation_offset);
|
|
||||||
else
|
|
||||||
dev_info(&info->bridge->dev,
|
|
||||||
"host bridge window %pR\n", res);
|
|
||||||
}
|
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool resource_contains(struct resource *res, resource_size_t point)
|
||||||
|
{
|
||||||
|
if (res->start <= point && point <= res->end)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void coalesce_windows(struct pci_root_info *info, int type)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
struct resource *res1, *res2;
|
||||||
|
|
||||||
|
for (i = 0; i < info->res_num; i++) {
|
||||||
|
res1 = &info->res[i];
|
||||||
|
if (!(res1->flags & type))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (j = i + 1; j < info->res_num; j++) {
|
||||||
|
res2 = &info->res[j];
|
||||||
|
if (!(res2->flags & type))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* I don't like throwing away windows because then
|
||||||
|
* our resources no longer match the ACPI _CRS, but
|
||||||
|
* the kernel resource tree doesn't allow overlaps.
|
||||||
|
*/
|
||||||
|
if (resource_contains(res1, res2->start) ||
|
||||||
|
resource_contains(res1, res2->end) ||
|
||||||
|
resource_contains(res2, res1->start) ||
|
||||||
|
resource_contains(res2, res1->end)) {
|
||||||
|
res1->start = min(res1->start, res2->start);
|
||||||
|
res1->end = max(res1->end, res2->end);
|
||||||
|
dev_info(&info->bridge->dev,
|
||||||
|
"host bridge window expanded to %pR; %pR ignored\n",
|
||||||
|
res1, res2);
|
||||||
|
res2->flags = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_resources(struct pci_root_info *info)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct resource *res, *root, *conflict;
|
||||||
|
|
||||||
|
if (!pci_use_crs)
|
||||||
|
return;
|
||||||
|
|
||||||
|
coalesce_windows(info, IORESOURCE_MEM);
|
||||||
|
coalesce_windows(info, IORESOURCE_IO);
|
||||||
|
|
||||||
|
for (i = 0; i < info->res_num; i++) {
|
||||||
|
res = &info->res[i];
|
||||||
|
|
||||||
|
if (res->flags & IORESOURCE_MEM)
|
||||||
|
root = &iomem_resource;
|
||||||
|
else if (res->flags & IORESOURCE_IO)
|
||||||
|
root = &ioport_resource;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
|
||||||
|
conflict = insert_resource_conflict(root, res);
|
||||||
|
if (conflict)
|
||||||
|
dev_err(&info->bridge->dev,
|
||||||
|
"address space collision: host bridge window %pR "
|
||||||
|
"conflicts with %s %pR\n",
|
||||||
|
res, conflict->name, conflict);
|
||||||
|
else
|
||||||
|
pci_bus_add_resource(info->bus, res, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
get_current_resources(struct acpi_device *device, int busnum,
|
get_current_resources(struct acpi_device *device, int busnum,
|
||||||
int domain, struct pci_bus *bus)
|
int domain, struct pci_bus *bus)
|
||||||
|
@ -224,6 +286,7 @@ get_current_resources(struct acpi_device *device, int busnum,
|
||||||
acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource,
|
acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource,
|
||||||
&info);
|
&info);
|
||||||
|
|
||||||
|
add_resources(&info);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
name_alloc_fail:
|
name_alloc_fail:
|
||||||
|
|
|
@ -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
|
* Find the next-best bus resource after the cursor "res". If the cursor is
|
||||||
* cursor is NULL, return the highest resource.
|
* 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,
|
static struct resource *pci_bus_find_resource_prev(struct pci_bus *bus,
|
||||||
unsigned int type,
|
unsigned int type,
|
||||||
struct resource *res)
|
struct resource *res)
|
||||||
{
|
{
|
||||||
|
bool res_pos, r_pos, prev_pos = false;
|
||||||
struct resource *r, *prev = NULL;
|
struct resource *r, *prev = NULL;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
res_pos = pci_bus_resource_positive(bus, res);
|
||||||
pci_bus_for_each_resource(bus, r, i) {
|
pci_bus_for_each_resource(bus, r, i) {
|
||||||
if (!r)
|
if (!r)
|
||||||
continue;
|
continue;
|
||||||
|
@ -82,26 +122,14 @@ static struct resource *pci_bus_find_resource_prev(struct pci_bus *bus,
|
||||||
if ((r->flags & IORESOURCE_TYPE_BITS) != type)
|
if ((r->flags & IORESOURCE_TYPE_BITS) != type)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* If this resource is at or past the cursor, skip it */
|
r_pos = pci_bus_resource_positive(bus, r);
|
||||||
if (res) {
|
if (!res || pci_bus_resource_better(res, res_pos, r, r_pos)) {
|
||||||
if (r == res)
|
if (!prev || pci_bus_resource_better(r, r_pos,
|
||||||
continue;
|
prev, prev_pos)) {
|
||||||
if (r->end > res->end)
|
prev = r;
|
||||||
continue;
|
prev_pos = r_pos;
|
||||||
if (r->end == res->end && r->start > res->start)
|
}
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
return prev;
|
||||||
|
|
|
@ -276,6 +276,12 @@ int __init ibmphp_access_ebda (void)
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
offset = next_offset;
|
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 */
|
next_offset = readw (io_mem + offset); /* offset of next blk */
|
||||||
|
|
||||||
offset += 2;
|
offset += 2;
|
||||||
|
|
|
@ -705,17 +705,21 @@ void pci_remove_legacy_files(struct pci_bus *b)
|
||||||
|
|
||||||
#ifdef HAVE_PCI_MMAP
|
#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;
|
nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
|
||||||
start = vma->vm_pgoff;
|
start = vma->vm_pgoff;
|
||||||
size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1;
|
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;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -745,8 +749,15 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
|
||||||
if (i >= PCI_ROM_RESOURCE)
|
if (i >= PCI_ROM_RESOURCE)
|
||||||
return -ENODEV;
|
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;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/* pci_mmap_page_range() expects the same kind of entry as coming
|
/* 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
|
* from /proc/bus/pci/ which is a "user visible" value. If this is
|
||||||
|
|
|
@ -1007,6 +1007,18 @@ static int __pci_enable_device_flags(struct pci_dev *dev,
|
||||||
int err;
|
int err;
|
||||||
int i, bars = 0;
|
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)
|
if (atomic_add_return(1, &dev->enable_cnt) > 1)
|
||||||
return 0; /* already enabled */
|
return 0; /* already enabled */
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,13 @@ extern void pci_remove_firmware_label_files(struct pci_dev *pdev);
|
||||||
#endif
|
#endif
|
||||||
extern void pci_cleanup_rom(struct pci_dev *dev);
|
extern void pci_cleanup_rom(struct pci_dev *dev);
|
||||||
#ifdef HAVE_PCI_MMAP
|
#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,
|
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
|
#endif
|
||||||
int pci_probe_reset_function(struct pci_dev *dev);
|
int pci_probe_reset_function(struct pci_dev *dev);
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
/* Make sure the caller is mapping a real resource for this device */
|
||||||
for (i = 0; i < PCI_ROM_RESOURCE; i++) {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue