Merge branch 'pci/resource-mmap' into next
* pci/resource-mmap: ia64: Use generic pci_mmap_resource_range() ia64: Remove redundant checks for WC in pci_mmap_page_range() ia64: Remove redundant valid_mmap_phys_addr_range() from pci_mmap_page_range() PCI: Add I/O BAR support to generic pci_mmap_resource_range() x86/PCI: Use generic pci_mmap_resource_range() unicore32/PCI: Use generic pci_mmap_resource_range() sh/PCI: Use generic pci_mmap_resource_range() parisc: Use generic pci_mmap_resource_range() mn10300/PCI: Use generic pci_mmap_resource_range() MIPS: PCI: Use generic pci_mmap_resource_range() cris/PCI: Use generic pci_mmap_resource_range() ARM/PCI: Use generic pci_mmap_resource_range() PCI: Add pci_mmap_resource_range() and use it for ARM64 PCI: Add BAR index argument to pci_mmap_page_range() PCI: Use BAR index in sysfs attr->private instead of resource pointer PCI: Add arch_can_pci_mmap_io() on architectures which can mmap() I/O space PCI: Move multiple declarations of pci_mmap_page_range() to <linux/pci.h> PCI: Add arch_can_pci_mmap_wc() macro xtensa/PCI: Do not mmap PCI BARs to userspace as write-through PCI: Only allow WC mmap on prefetchable resources PCI: Fix another sanity check bug in /proc/pci mmap PCI: Fix pci_mmap_fits() for HAVE_PCI_RESOURCE_TO_USER platforms
这个提交包含在:
@@ -4,7 +4,7 @@
|
||||
|
||||
obj-y += access.o bus.o probe.o host-bridge.o remove.o pci.o \
|
||||
pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \
|
||||
irq.o vpd.o setup-bus.o vc.o
|
||||
irq.o vpd.o setup-bus.o vc.o mmap.o
|
||||
obj-$(CONFIG_PROC_FS) += proc.o
|
||||
obj-$(CONFIG_SYSFS) += slot.o
|
||||
|
||||
|
99
drivers/pci/mmap.c
普通文件
99
drivers/pci/mmap.c
普通文件
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* mmap.c — generic PCI resource mmap helper
|
||||
*
|
||||
* Copyright © 2017 Amazon.com, Inc. or its affiliates.
|
||||
*
|
||||
* Author: David Woodhouse <dwmw2@infradead.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#ifdef ARCH_GENERIC_PCI_MMAP_RESOURCE
|
||||
|
||||
/*
|
||||
* Modern setup: generic pci_mmap_resource_range(), and implement the legacy
|
||||
* pci_mmap_page_range() (if needed) as a wrapper round it.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_PCI_MMAP
|
||||
int pci_mmap_page_range(struct pci_dev *pdev, int bar,
|
||||
struct vm_area_struct *vma,
|
||||
enum pci_mmap_state mmap_state, int write_combine)
|
||||
{
|
||||
resource_size_t start, end;
|
||||
|
||||
pci_resource_to_user(pdev, bar, &pdev->resource[bar], &start, &end);
|
||||
|
||||
/* Adjust vm_pgoff to be the offset within the resource */
|
||||
vma->vm_pgoff -= start >> PAGE_SHIFT;
|
||||
return pci_mmap_resource_range(pdev, bar, vma, mmap_state,
|
||||
write_combine);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct vm_operations_struct pci_phys_vm_ops = {
|
||||
#ifdef CONFIG_HAVE_IOREMAP_PROT
|
||||
.access = generic_access_phys,
|
||||
#endif
|
||||
};
|
||||
|
||||
int pci_mmap_resource_range(struct pci_dev *pdev, int bar,
|
||||
struct vm_area_struct *vma,
|
||||
enum pci_mmap_state mmap_state, int write_combine)
|
||||
{
|
||||
unsigned long size;
|
||||
int ret;
|
||||
|
||||
size = ((pci_resource_len(pdev, bar) - 1) >> PAGE_SHIFT) + 1;
|
||||
if (vma->vm_pgoff + vma_pages(vma) > size)
|
||||
return -EINVAL;
|
||||
|
||||
if (write_combine)
|
||||
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
|
||||
else
|
||||
vma->vm_page_prot = pgprot_device(vma->vm_page_prot);
|
||||
|
||||
if (mmap_state == pci_mmap_io) {
|
||||
ret = pci_iobar_pfn(pdev, bar, vma);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else
|
||||
vma->vm_pgoff += (pci_resource_start(pdev, bar) >> PAGE_SHIFT);
|
||||
|
||||
vma->vm_ops = &pci_phys_vm_ops;
|
||||
|
||||
return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
|
||||
vma->vm_end - vma->vm_start,
|
||||
vma->vm_page_prot);
|
||||
}
|
||||
|
||||
#elif defined(HAVE_PCI_MMAP) /* && !ARCH_GENERIC_PCI_MMAP_RESOURCE */
|
||||
|
||||
/*
|
||||
* Legacy setup: Impement pci_mmap_resource_range() as a wrapper around
|
||||
* the architecture's pci_mmap_page_range(), converting to "user visible"
|
||||
* addresses as necessary.
|
||||
*/
|
||||
|
||||
int pci_mmap_resource_range(struct pci_dev *pdev, int bar,
|
||||
struct vm_area_struct *vma,
|
||||
enum pci_mmap_state mmap_state, int write_combine)
|
||||
{
|
||||
resource_size_t start, end;
|
||||
|
||||
/*
|
||||
* 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
|
||||
* different from the resource itself, arch will do necessary fixup.
|
||||
*/
|
||||
pci_resource_to_user(pdev, bar, &pdev->resource[bar], &start, &end);
|
||||
vma->vm_pgoff += start >> PAGE_SHIFT;
|
||||
return pci_mmap_page_range(pdev, bar, vma, mmap_state, write_combine);
|
||||
}
|
||||
#endif
|
@@ -980,20 +980,24 @@ void pci_remove_legacy_files(struct pci_bus *b)
|
||||
}
|
||||
#endif /* HAVE_PCI_LEGACY */
|
||||
|
||||
#ifdef HAVE_PCI_MMAP
|
||||
#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)
|
||||
|
||||
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, pci_start;
|
||||
unsigned long nr, start, size;
|
||||
resource_size_t pci_start = 0, pci_end;
|
||||
|
||||
if (pci_resource_len(pdev, resno) == 0)
|
||||
return 0;
|
||||
nr = vma_pages(vma);
|
||||
start = vma->vm_pgoff;
|
||||
size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1;
|
||||
pci_start = (mmap_api == PCI_MMAP_PROCFS) ?
|
||||
pci_resource_start(pdev, resno) >> PAGE_SHIFT : 0;
|
||||
if (mmap_api == PCI_MMAP_PROCFS) {
|
||||
pci_resource_to_user(pdev, resno, &pdev->resource[resno],
|
||||
&pci_start, &pci_end);
|
||||
pci_start >>= PAGE_SHIFT;
|
||||
}
|
||||
if (start >= pci_start && start < pci_start + size &&
|
||||
start + nr <= pci_start + size)
|
||||
return 1;
|
||||
@@ -1013,37 +1017,24 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
|
||||
struct vm_area_struct *vma, int write_combine)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
|
||||
struct resource *res = attr->private;
|
||||
int bar = (unsigned long)attr->private;
|
||||
enum pci_mmap_state mmap_type;
|
||||
resource_size_t start, end;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PCI_ROM_RESOURCE; i++)
|
||||
if (res == &pdev->resource[i])
|
||||
break;
|
||||
if (i >= PCI_ROM_RESOURCE)
|
||||
return -ENODEV;
|
||||
struct resource *res = &pdev->resource[bar];
|
||||
|
||||
if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start))
|
||||
return -EINVAL;
|
||||
|
||||
if (!pci_mmap_fits(pdev, i, vma, PCI_MMAP_SYSFS)) {
|
||||
if (!pci_mmap_fits(pdev, bar, 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));
|
||||
pci_name(pdev), bar,
|
||||
(u64)pci_resource_start(pdev, bar),
|
||||
(u64)pci_resource_len(pdev, bar));
|
||||
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
|
||||
* different from the resource itself, arch will do necessary fixup.
|
||||
*/
|
||||
pci_resource_to_user(pdev, i, res, &start, &end);
|
||||
vma->vm_pgoff += start >> PAGE_SHIFT;
|
||||
mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
|
||||
return pci_mmap_page_range(pdev, vma, mmap_type, write_combine);
|
||||
|
||||
return pci_mmap_resource_range(pdev, bar, vma, mmap_type, write_combine);
|
||||
}
|
||||
|
||||
static int pci_mmap_resource_uc(struct file *filp, struct kobject *kobj,
|
||||
@@ -1065,22 +1056,18 @@ static ssize_t pci_resource_io(struct file *filp, struct kobject *kobj,
|
||||
loff_t off, size_t count, bool write)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
|
||||
struct resource *res = attr->private;
|
||||
int bar = (unsigned long)attr->private;
|
||||
struct resource *res;
|
||||
unsigned long port = off;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PCI_ROM_RESOURCE; i++)
|
||||
if (res == &pdev->resource[i])
|
||||
break;
|
||||
if (i >= PCI_ROM_RESOURCE)
|
||||
return -ENODEV;
|
||||
res = &pdev->resource[bar];
|
||||
|
||||
port += pci_resource_start(pdev, i);
|
||||
port += pci_resource_start(pdev, bar);
|
||||
|
||||
if (port > pci_resource_end(pdev, i))
|
||||
if (port > pci_resource_end(pdev, bar))
|
||||
return 0;
|
||||
|
||||
if (port + count - 1 > pci_resource_end(pdev, i))
|
||||
if (port + count - 1 > pci_resource_end(pdev, bar))
|
||||
return -EINVAL;
|
||||
|
||||
switch (count) {
|
||||
@@ -1170,16 +1157,19 @@ static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine)
|
||||
} else {
|
||||
pdev->res_attr[num] = res_attr;
|
||||
sprintf(res_attr_name, "resource%d", num);
|
||||
res_attr->mmap = pci_mmap_resource_uc;
|
||||
}
|
||||
if (pci_resource_flags(pdev, num) & IORESOURCE_IO) {
|
||||
res_attr->read = pci_read_resource_io;
|
||||
res_attr->write = pci_write_resource_io;
|
||||
if (pci_resource_flags(pdev, num) & IORESOURCE_IO) {
|
||||
res_attr->read = pci_read_resource_io;
|
||||
res_attr->write = pci_write_resource_io;
|
||||
if (arch_can_pci_mmap_io())
|
||||
res_attr->mmap = pci_mmap_resource_uc;
|
||||
} else {
|
||||
res_attr->mmap = pci_mmap_resource_uc;
|
||||
}
|
||||
}
|
||||
res_attr->attr.name = res_attr_name;
|
||||
res_attr->attr.mode = S_IRUSR | S_IWUSR;
|
||||
res_attr->size = pci_resource_len(pdev, num);
|
||||
res_attr->private = &pdev->resource[num];
|
||||
res_attr->private = (void *)(unsigned long)num;
|
||||
retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
|
||||
if (retval)
|
||||
kfree(res_attr);
|
||||
@@ -1207,9 +1197,9 @@ static int pci_create_resource_files(struct pci_dev *pdev)
|
||||
|
||||
retval = pci_create_attr(pdev, i, 0);
|
||||
/* for prefetchable resources, create a WC mappable file */
|
||||
if (!retval && pdev->resource[i].flags & IORESOURCE_PREFETCH)
|
||||
if (!retval && arch_can_pci_mmap_wc() &&
|
||||
pdev->resource[i].flags & IORESOURCE_PREFETCH)
|
||||
retval = pci_create_attr(pdev, i, 1);
|
||||
|
||||
if (retval) {
|
||||
pci_remove_resource_files(pdev);
|
||||
return retval;
|
||||
|
@@ -21,14 +21,14 @@ void pci_create_firmware_label_files(struct pci_dev *pdev);
|
||||
void pci_remove_firmware_label_files(struct pci_dev *pdev);
|
||||
#endif
|
||||
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> */
|
||||
};
|
||||
int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai,
|
||||
enum pci_mmap_api mmap_api);
|
||||
#endif
|
||||
|
||||
int pci_probe_reset_function(struct pci_dev *dev);
|
||||
|
||||
/**
|
||||
|
@@ -202,6 +202,8 @@ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd,
|
||||
|
||||
#ifdef HAVE_PCI_MMAP
|
||||
case PCIIOC_MMAP_IS_IO:
|
||||
if (!arch_can_pci_mmap_io())
|
||||
return -EINVAL;
|
||||
fpriv->mmap_state = pci_mmap_io;
|
||||
break;
|
||||
|
||||
@@ -210,14 +212,15 @@ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd,
|
||||
break;
|
||||
|
||||
case PCIIOC_WRITE_COMBINE:
|
||||
if (arg)
|
||||
fpriv->write_combine = 1;
|
||||
else
|
||||
fpriv->write_combine = 0;
|
||||
break;
|
||||
|
||||
if (arch_can_pci_mmap_wc()) {
|
||||
if (arg)
|
||||
fpriv->write_combine = 1;
|
||||
else
|
||||
fpriv->write_combine = 0;
|
||||
break;
|
||||
}
|
||||
/* If arch decided it can't, fall through... */
|
||||
#endif /* HAVE_PCI_MMAP */
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
@@ -231,25 +234,35 @@ static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct pci_dev *dev = PDE_DATA(file_inode(file));
|
||||
struct pci_filp_private *fpriv = file->private_data;
|
||||
int i, ret, write_combine;
|
||||
int i, ret, write_combine = 0, res_bit = IORESOURCE_MEM;
|
||||
|
||||
if (!capable(CAP_SYS_RAWIO))
|
||||
return -EPERM;
|
||||
|
||||
if (fpriv->mmap_state == pci_mmap_io) {
|
||||
if (!arch_can_pci_mmap_io())
|
||||
return -EINVAL;
|
||||
res_bit = IORESOURCE_IO;
|
||||
}
|
||||
|
||||
/* 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, PCI_MMAP_PROCFS))
|
||||
if (dev->resource[i].flags & res_bit &&
|
||||
pci_mmap_fits(dev, i, vma, PCI_MMAP_PROCFS))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= PCI_ROM_RESOURCE)
|
||||
return -ENODEV;
|
||||
|
||||
if (fpriv->mmap_state == pci_mmap_mem)
|
||||
write_combine = fpriv->write_combine;
|
||||
else
|
||||
write_combine = 0;
|
||||
ret = pci_mmap_page_range(dev, vma,
|
||||
if (fpriv->mmap_state == pci_mmap_mem &&
|
||||
fpriv->write_combine) {
|
||||
if (dev->resource[i].flags & IORESOURCE_PREFETCH)
|
||||
write_combine = 1;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = pci_mmap_page_range(dev, i, vma,
|
||||
fpriv->mmap_state, write_combine);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
在新工单中引用
屏蔽一个用户