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
7 changed files with 175 additions and 50 deletions
|
@ -138,7 +138,6 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
|
|||
struct acpi_resource_address64 addr;
|
||||
acpi_status status;
|
||||
unsigned long flags;
|
||||
struct resource *root, *conflict;
|
||||
u64 start, end;
|
||||
|
||||
status = resource_to_addr(acpi_res, &addr);
|
||||
|
@ -146,12 +145,10 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
|
|||
return AE_OK;
|
||||
|
||||
if (addr.resource_type == ACPI_MEMORY_RANGE) {
|
||||
root = &iomem_resource;
|
||||
flags = IORESOURCE_MEM;
|
||||
if (addr.info.mem.caching == ACPI_PREFETCHABLE_MEMORY)
|
||||
flags |= IORESOURCE_PREFETCH;
|
||||
} else if (addr.resource_type == ACPI_IO_RANGE) {
|
||||
root = &ioport_resource;
|
||||
flags = IORESOURCE_IO;
|
||||
} else
|
||||
return AE_OK;
|
||||
|
@ -172,27 +169,92 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
|
|||
return AE_OK;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
get_current_resources(struct acpi_device *device, int busnum,
|
||||
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,
|
||||
&info);
|
||||
|
||||
add_resources(&info);
|
||||
return;
|
||||
|
||||
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
|
||||
* 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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue