Commit c1ef3224 authored by Arnd Bergmann's avatar Arnd Bergmann Committed by Bjorn Helgaas
Browse files

sparc: Use generic pci_mmap_resource_range()

The main feature of the sparc-specific implementation of
pci_mmap_resource_range() is that it allows mapping the entire PCI I/O
space for a PCI host bridge using the /proc/bus/pci interface on a bridge
device.

The generic implementation cannot do this, but it also appears that this
got broken for sparc by commit 9eff02e2 ("PCI: check mmap range of
/proc/bus/pci files too"), which enforces that each address is part of a
BAR for kernels after 2.6.28.

Remove it all, assuming that the corresponding user space code has already
been changed to access /dev/ioport instead a long time ago.  Add
pci_iobar_pfn() to make it possible to map I/O resources. This is adapted
from the powerpc version.

Link: https://lore.kernel.org/lkml/1519887203.622.3.camel@infradead.org/t/
Link: https://lore.kernel.org/r/20220715153617.3393420-2-arnd@kernel.org


Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
parent 0ad722f1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ static inline int pci_proc_domain(struct pci_bus *bus)
#define HAVE_PCI_MMAP
#define arch_can_pci_mmap_io()	1
#define HAVE_ARCH_PCI_GET_UNMAPPED_AREA
#define ARCH_GENERIC_PCI_MMAP_RESOURCE
#define get_pci_unmapped_area get_fb_unmapped_area
#endif /* CONFIG_SPARC64 */

+4 −150
Original line number Diff line number Diff line
@@ -751,161 +751,15 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
}

/* Platform support for /proc/bus/pci/X/Y mmap()s. */

/* If the user uses a host-bridge as the PCI device, he may use
 * this to perform a raw mmap() of the I/O or MEM space behind
 * that controller.
 *
 * This can be useful for execution of x86 PCI bios initialization code
 * on a PCI card, like the xfree86 int10 stuff does.
 */
static int __pci_mmap_make_offset_bus(struct pci_dev *pdev, struct vm_area_struct *vma,
				      enum pci_mmap_state mmap_state)
int pci_iobar_pfn(struct pci_dev *pdev, int bar, struct vm_area_struct *vma)
{
	struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
	unsigned long space_size, user_offset, user_size;

	if (mmap_state == pci_mmap_io) {
		space_size = resource_size(&pbm->io_space);
	} else {
		space_size = resource_size(&pbm->mem_space);
	}

	/* Make sure the request is in range. */
	user_offset = vma->vm_pgoff << PAGE_SHIFT;
	user_size = vma->vm_end - vma->vm_start;
	resource_size_t ioaddr = pci_resource_start(pdev, bar);

	if (user_offset >= space_size ||
	    (user_offset + user_size) > space_size)
	if (!pbm)
		return -EINVAL;

	if (mmap_state == pci_mmap_io) {
		vma->vm_pgoff = (pbm->io_space.start +
				 user_offset) >> PAGE_SHIFT;
	} else {
		vma->vm_pgoff = (pbm->mem_space.start +
				 user_offset) >> PAGE_SHIFT;
	}

	return 0;
}

/* Adjust vm_pgoff of VMA such that it is the physical page offset
 * corresponding to the 32-bit pci bus offset for DEV requested by the user.
 *
 * Basically, the user finds the base address for his device which he wishes
 * to mmap.  They read the 32-bit value from the config space base register,
 * add whatever PAGE_SIZE multiple offset they wish, and feed this into the
 * offset parameter of mmap on /proc/bus/pci/XXX for that device.
 *
 * Returns negative error code on failure, zero on success.
 */
static int __pci_mmap_make_offset(struct pci_dev *pdev,
				  struct vm_area_struct *vma,
				  enum pci_mmap_state mmap_state)
{
	unsigned long user_paddr, user_size;
	int i, err;

	/* First compute the physical address in vma->vm_pgoff,
	 * making sure the user offset is within range in the
	 * appropriate PCI space.
	 */
	err = __pci_mmap_make_offset_bus(pdev, vma, mmap_state);
	if (err)
		return err;

	/* If this is a mapping on a host bridge, any address
	 * is OK.
	 */
	if ((pdev->class >> 8) == PCI_CLASS_BRIDGE_HOST)
		return err;

	/* Otherwise make sure it's in the range for one of the
	 * device's resources.
	 */
	user_paddr = vma->vm_pgoff << PAGE_SHIFT;
	user_size = vma->vm_end - vma->vm_start;

	for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
		struct resource *rp = &pdev->resource[i];
		resource_size_t aligned_end;

		/* Active? */
		if (!rp->flags)
			continue;

		/* Same type? */
		if (i == PCI_ROM_RESOURCE) {
			if (mmap_state != pci_mmap_mem)
				continue;
		} else {
			if ((mmap_state == pci_mmap_io &&
			     (rp->flags & IORESOURCE_IO) == 0) ||
			    (mmap_state == pci_mmap_mem &&
			     (rp->flags & IORESOURCE_MEM) == 0))
				continue;
		}

		/* Align the resource end to the next page address.
		 * PAGE_SIZE intentionally added instead of (PAGE_SIZE - 1),
		 * because actually we need the address of the next byte
		 * after rp->end.
		 */
		aligned_end = (rp->end + PAGE_SIZE) & PAGE_MASK;

		if ((rp->start <= user_paddr) &&
		    (user_paddr + user_size) <= aligned_end)
			break;
	}

	if (i > PCI_ROM_RESOURCE)
		return -EINVAL;

	return 0;
}

/* Set vm_page_prot of VMA, as appropriate for this architecture, for a pci
 * device mapping.
 */
static void __pci_mmap_set_pgprot(struct pci_dev *dev, struct vm_area_struct *vma,
					     enum pci_mmap_state mmap_state)
{
	/* Our io_remap_pfn_range takes care of this, do nothing.  */
}

/* Perform the actual remap of the pages for a PCI device mapping, as appropriate
 * for this architecture.  The region in the process to map is described by vm_start
 * and vm_end members of VMA, the BAR relative address is found in vm_pgoff.
 * The pci device structure is provided so that architectures may make mapping
 * decisions on a per-device or per-bus basis.
 *
 * Returns a negative error code on failure, zero on success.
 */
int pci_mmap_resource_range(struct pci_dev *dev, int bar,
			    struct vm_area_struct *vma,
			    enum pci_mmap_state mmap_state, int write_combine)
{
	int ret;
	resource_size_t start, end;

	/* convert per-BAR address to PCI bus address */
	pci_resource_to_user(dev, bar, &dev->resource[bar], &start, &end);
	vma->vm_pgoff += start >> PAGE_SHIFT;

	ret = __pci_mmap_make_offset(dev, vma, mmap_state);
	if (ret < 0)
		return ret;

	__pci_mmap_set_pgprot(dev, vma, mmap_state);

	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
	ret = io_remap_pfn_range(vma, vma->vm_start,
				 vma->vm_pgoff,
				 vma->vm_end - vma->vm_start,
				 vma->vm_page_prot);
	if (ret)
		return ret;
	vma->vm_pgoff += (ioaddr + pbm->io_space.start) >> PAGE_SHIFT;

	return 0;
}