Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e5c13537 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* '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
parents 968ab183 e25cd062
Loading
Loading
Loading
Loading
+83 −20
Original line number Diff line number Diff line
@@ -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,14 +169,6 @@ 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 "
@@ -187,10 +176,83 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
			 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 %pR\n", res);
					 "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);
	}
	return AE_OK;
}

static void
@@ -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:
+49 −21
Original line number Diff line number Diff line
@@ -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;
		}

		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))
		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;
			}
		}
	}

	return prev;
+6 −0
Original line number Diff line number Diff line
@@ -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;
+17 −6
Original line number Diff line number Diff line
@@ -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
+12 −0
Original line number Diff line number Diff line
@@ -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 */

Loading