Re: [PATCH v11 04/60] sparc/PCI: Use correct offset for bus address to resource

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, May 4, 2016 at 11:46 AM, Yinghai Lu <yinghai@xxxxxxxxxx> wrote:
> On Wed, May 4, 2016 at 8:17 AM, Bjorn Helgaas <helgaas@xxxxxxxxxx> wrote:
>> My goal is to make pci_mmap_resource() and proc_bus_pci_mmap() look
>> very similar, e.g.,
>>
>>   /* locate resource */
>>   pci_user_to_resource()                # only in proc_bus_pci_mmap()
>>   if (!pci_mmap_fits()) {
>>     WARN(...);
>>     return -EINVAL;
>>   }
>>   pci_mmap_page_range();

Please check v2.

Subject: [RFC PATCH v2] PCI: Let pci_mmap_page_range() take resource addr

In 8c05cd08a7 ("PCI: fix offset check for sysfs mmapped files"), try
to check exposed value with resource start/end in proc mmap path.
|        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 (start >= pci_start && start < pci_start + size &&
|                        start + nr <= pci_start + size)

That would break sparc that exposed value is still BAR value.

In the patch:
1. in proc path: proc_bus_pci_mmap, try convert back to resource
   before calling pci_mmap_page_range
2. in sysfs path: pci_mmap_resource will just offset with resource start.
3. all pci_mmap_page_range will all have vma->vm_pgoff with in resource
   range instead of BAR value.
4. remove __pci_mmap_make_offset, as the checking is done
   in pci_mmap_fits().

-v2: add pci_user_to_resource and remove __pci_mmap_make_offset

Signed-off-by: Yinghai Lu <yinghai@xxxxxxxxxx>

---
 arch/microblaze/pci/pci-common.c |   94 ++++++++-----------------------
 arch/powerpc/kernel/pci-common.c |   95 ++++++++-----------------------
 arch/sparc/kernel/pci.c          |  117 ---------------------------------------
 arch/xtensa/kernel/pci.c         |   73 ++----------------------
 drivers/pci/pci-sysfs.c          |   23 ++++---
 drivers/pci/pci.h                |    2
 drivers/pci/proc.c               |   59 ++++++++++++++++---
 7 files changed, 124 insertions(+), 339 deletions(-)

Index: linux-2.6/arch/microblaze/pci/pci-common.c
===================================================================
--- linux-2.6.orig/arch/microblaze/pci/pci-common.c
+++ linux-2.6/arch/microblaze/pci/pci-common.c
@@ -153,63 +153,25 @@ void pcibios_set_master(struct pci_dev *
  *  -- paulus.
  */

-/*
- * 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 struct resource *__pci_mmap_make_offset(struct pci_dev *dev,
-                           resource_size_t *offset,
-                           enum pci_mmap_state mmap_state)
+static struct resource *pci_find_resource(struct pci_dev *dev,
+                resource_size_t offset, int flags)
 {
-    struct pci_controller *hose = pci_bus_to_host(dev->bus);
-    unsigned long io_offset = 0;
-    int i, res_bit;
-
-    if (!hose)
-        return NULL;        /* should never happen */
-
-    /* If memory, add on the PCI bridge address offset */
-    if (mmap_state == pci_mmap_mem) {
-#if 0 /* See comment in pci_resource_to_user() for why this is disabled */
-        *offset += hose->pci_mem_offset;
-#endif
-        res_bit = IORESOURCE_MEM;
-    } else {
-        io_offset = (unsigned long)hose->io_base_virt - _IO_BASE;
-        *offset += io_offset;
-        res_bit = IORESOURCE_IO;
-    }
+    int i;

-    /*
-     * Check that the offset requested corresponds to one of the
-     * resources of the device.
-     */
     for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
         struct resource *rp = &dev->resource[i];
-        int flags = rp->flags;

-        /* treat ROM as memory (should be already) */
-        if (i == PCI_ROM_RESOURCE)
-            flags |= IORESOURCE_MEM;
+        if (!(rp->flags & flags))
+            continue;

-        /* Active and same type? */
-        if ((flags & res_bit) == 0)
+        if (pci_resource_len(dev, i) == 0)
             continue;

         /* In the range of this resource? */
-        if (*offset < (rp->start & PAGE_MASK) || *offset > rp->end)
+        if (offset < (rp->start & PAGE_MASK) ||
+            offset > rp->end)
             continue;

-        /* found it! construct the final physical address */
-        if (mmap_state == pci_mmap_io)
-            *offset += hose->io_base_phys - io_offset;
         return rp;
     }

@@ -236,7 +198,7 @@ static pgprot_t __pci_mmap_set_pgprot(st
     if (mmap_state != pci_mmap_mem)
         write_combine = 0;
     else if (write_combine == 0) {
-        if (rp->flags & IORESOURCE_PREFETCH)
+        if (rp && (rp->flags & IORESOURCE_PREFETCH))
             write_combine = 1;
     }

@@ -256,27 +218,13 @@ pgprot_t pci_phys_mem_access_prot(struct
     struct pci_dev *pdev = NULL;
     struct resource *found = NULL;
     resource_size_t offset = ((resource_size_t)pfn) << PAGE_SHIFT;
-    int i;

     if (page_is_ram(pfn))
         return prot;

     prot = pgprot_noncached(prot);
     for_each_pci_dev(pdev) {
-        for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
-            struct resource *rp = &pdev->resource[i];
-            int flags = rp->flags;
-
-            /* Active and same type? */
-            if ((flags & IORESOURCE_MEM) == 0)
-                continue;
-            /* In the range of this resource? */
-            if (offset < (rp->start & PAGE_MASK) ||
-                offset > rp->end)
-                continue;
-            found = rp;
-            break;
-        }
+        found = pci_find_resource(pdev, offset, IORESOURCE_MEM);
         if (found)
             break;
     }
@@ -305,14 +253,24 @@ pgprot_t pci_phys_mem_access_prot(struct
 int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
             enum pci_mmap_state mmap_state, int write_combine)
 {
+    struct pci_controller *hose = pci_bus_to_host(dev->bus);
     resource_size_t offset =
         ((resource_size_t)vma->vm_pgoff) << PAGE_SHIFT;
     struct resource *rp;
-    int ret;
+    int ret, flags;
+
+    if (!hose)
+        return -EINVAL;         /* should never happen */

-    rp = __pci_mmap_make_offset(dev, &offset, mmap_state);
-    if (rp == NULL)
-        return -EINVAL;
+    if (mmap_state == pci_mmap_mem)
+        flags = IORESOURCE_MEM;
+    else
+        flags = IORESOURCE_IO;
+
+    rp = pci_find_resource(dev, offset, flags);
+    if (mmap_state == pci_mmap_io)
+        *offset += hose->io_base_phys -
+             ((unsigned long)hose->io_base_virt - _IO_BASE);

     vma->vm_pgoff = offset >> PAGE_SHIFT;
     vma->vm_page_prot = __pci_mmap_set_pgprot(dev, rp,
@@ -491,9 +449,7 @@ void pci_resource_to_user(const struct p
      *
      * Hopefully, the sysfs insterface is immune to that gunk. Once X
      * has been fixed (and the fix spread enough), we can re-enable the
-     * 2 lines below and pass down a BAR value to userland. In that case
-     * we'll also have to re-enable the matching code in
-     * __pci_mmap_make_offset().
+     * 2 lines below and pass down a BAR value to userland.
      *
      * BenH.
      */
Index: linux-2.6/arch/powerpc/kernel/pci-common.c
===================================================================
--- linux-2.6.orig/arch/powerpc/kernel/pci-common.c
+++ linux-2.6/arch/powerpc/kernel/pci-common.c
@@ -292,63 +292,25 @@ static int pci_read_irq_line(struct pci_
  *  -- paulus.
  */

-/*
- * 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 struct resource *__pci_mmap_make_offset(struct pci_dev *dev,
-                           resource_size_t *offset,
-                           enum pci_mmap_state mmap_state)
+static struct resource *pci_find_resource(struct pci_dev *dev,
+        resource_size_t offset, int flags)
 {
-    struct pci_controller *hose = pci_bus_to_host(dev->bus);
-    unsigned long io_offset = 0;
-    int i, res_bit;
-
-    if (hose == NULL)
-        return NULL;        /* should never happen */
-
-    /* If memory, add on the PCI bridge address offset */
-    if (mmap_state == pci_mmap_mem) {
-#if 0 /* See comment in pci_resource_to_user() for why this is disabled */
-        *offset += hose->pci_mem_offset;
-#endif
-        res_bit = IORESOURCE_MEM;
-    } else {
-        io_offset = (unsigned long)hose->io_base_virt - _IO_BASE;
-        *offset += io_offset;
-        res_bit = IORESOURCE_IO;
-    }
+    int i;

-    /*
-     * Check that the offset requested corresponds to one of the
-     * resources of the device.
-     */
     for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
         struct resource *rp = &dev->resource[i];
-        int flags = rp->flags;

-        /* treat ROM as memory (should be already) */
-        if (i == PCI_ROM_RESOURCE)
-            flags |= IORESOURCE_MEM;
+        if (!(rp->flags & flags))
+            continue;

-        /* Active and same type? */
-        if ((flags & res_bit) == 0)
+        if (pci_resource_len(dev, i) == 0)
             continue;

         /* In the range of this resource? */
-        if (*offset < (rp->start & PAGE_MASK) || *offset > rp->end)
+        if (offset < (rp->start & PAGE_MASK) ||
+            offset > rp->end)
             continue;

-        /* found it! construct the final physical address */
-        if (mmap_state == pci_mmap_io)
-            *offset += hose->io_base_phys - io_offset;
         return rp;
     }

@@ -374,7 +336,7 @@ static pgprot_t __pci_mmap_set_pgprot(st
     if (mmap_state != pci_mmap_mem)
         write_combine = 0;
     else if (write_combine == 0) {
-        if (rp->flags & IORESOURCE_PREFETCH)
+        if (rp && (rp->flags & IORESOURCE_PREFETCH))
             write_combine = 1;
     }

@@ -385,6 +347,7 @@ static pgprot_t __pci_mmap_set_pgprot(st
         return pgprot_noncached(protection);
 }

+
 /*
  * This one is used by /dev/mem and fbdev who have no clue about the
  * PCI device, it tries to find the PCI device first and calls the
@@ -398,27 +361,13 @@ pgprot_t pci_phys_mem_access_prot(struct
     struct pci_dev *pdev = NULL;
     struct resource *found = NULL;
     resource_size_t offset = ((resource_size_t)pfn) << PAGE_SHIFT;
-    int i;

     if (page_is_ram(pfn))
         return prot;

     prot = pgprot_noncached(prot);
     for_each_pci_dev(pdev) {
-        for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
-            struct resource *rp = &pdev->resource[i];
-            int flags = rp->flags;
-
-            /* Active and same type? */
-            if ((flags & IORESOURCE_MEM) == 0)
-                continue;
-            /* In the range of this resource? */
-            if (offset < (rp->start & PAGE_MASK) ||
-                offset > rp->end)
-                continue;
-            found = rp;
-            break;
-        }
+        found = pci_find_resource(pdev, offset, IORESOURCE_MEM);
         if (found)
             break;
     }
@@ -448,14 +397,24 @@ pgprot_t pci_phys_mem_access_prot(struct
 int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
             enum pci_mmap_state mmap_state, int write_combine)
 {
+    struct pci_controller *hose = pci_bus_to_host(dev->bus);
     resource_size_t offset =
         ((resource_size_t)vma->vm_pgoff) << PAGE_SHIFT;
     struct resource *rp;
-    int ret;
+    int ret, flags;
+
+    if (hose == NULL)
+        return -EINVAL;         /* should never happen */
+
+    if (mmap_state == pci_mmap_mem)
+        flags = IORESOURCE_MEM;
+    else
+        flags = IORESOURCE_IO;
+    rp = pci_find_resource(dev, offset, flags);

-    rp = __pci_mmap_make_offset(dev, &offset, mmap_state);
-    if (rp == NULL)
-        return -EINVAL;
+    if (mmap_state == pci_mmap_io)
+        offset += hose->io_base_phys -
+              ((unsigned long)hose->io_base_virt - _IO_BASE);

     vma->vm_pgoff = offset >> PAGE_SHIFT;
     vma->vm_page_prot = __pci_mmap_set_pgprot(dev, rp,
@@ -630,9 +589,7 @@ void pci_resource_to_user(const struct p
      *
      * Hopefully, the sysfs insterface is immune to that gunk. Once X
      * has been fixed (and the fix spread enough), we can re-enable the
-     * 2 lines below and pass down a BAR value to userland. In that case
-     * we'll also have to re-enable the matching code in
-     * __pci_mmap_make_offset().
+     * 2 lines below and pass down a BAR value to userland.
      *
      * BenH.
      */
Index: linux-2.6/arch/sparc/kernel/pci.c
===================================================================
--- linux-2.6.orig/arch/sparc/kernel/pci.c
+++ linux-2.6/arch/sparc/kernel/pci.c
@@ -732,119 +732,6 @@ int pcibios_enable_device(struct pci_dev

 /* 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)
-{
-    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;
-
-    if (user_offset >= space_size ||
-        (user_offset + user_size) > space_size)
-        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.
  */
@@ -868,10 +755,6 @@ int pci_mmap_page_range(struct pci_dev *
 {
     int ret;

-    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);
Index: linux-2.6/arch/xtensa/kernel/pci.c
===================================================================
--- linux-2.6.orig/arch/xtensa/kernel/pci.c
+++ linux-2.6/arch/xtensa/kernel/pci.c
@@ -272,68 +272,6 @@ pci_controller_num(struct pci_dev *dev)
  */

 /*
- * 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 __inline__ int
-__pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vma,
-               enum pci_mmap_state mmap_state)
-{
-    struct pci_controller *pci_ctrl = (struct pci_controller*) dev->sysdata;
-    unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
-    unsigned long io_offset = 0;
-    int i, res_bit;
-
-    if (pci_ctrl == 0)
-        return -EINVAL;        /* should never happen */
-
-    /* If memory, add on the PCI bridge address offset */
-    if (mmap_state == pci_mmap_mem) {
-        res_bit = IORESOURCE_MEM;
-    } else {
-        io_offset = (unsigned long)pci_ctrl->io_space.base;
-        offset += io_offset;
-        res_bit = IORESOURCE_IO;
-    }
-
-    /*
-     * Check that the offset requested corresponds to one of the
-     * resources of the device.
-     */
-    for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
-        struct resource *rp = &dev->resource[i];
-        int flags = rp->flags;
-
-        /* treat ROM as memory (should be already) */
-        if (i == PCI_ROM_RESOURCE)
-            flags |= IORESOURCE_MEM;
-
-        /* Active and same type? */
-        if ((flags & res_bit) == 0)
-            continue;
-
-        /* In the range of this resource? */
-        if (offset < (rp->start & PAGE_MASK) || offset > rp->end)
-            continue;
-
-        /* found it! construct the final physical address */
-        if (mmap_state == pci_mmap_io)
-            offset += pci_ctrl->io_space.start - io_offset;
-        vma->vm_pgoff = offset >> PAGE_SHIFT;
-        return 0;
-    }
-
-    return -EINVAL;
-}
-
-/*
  * Set vm_page_prot of VMA, as appropriate for this architecture, for a pci
  * device mapping.
  */
@@ -366,11 +304,16 @@ int pci_mmap_page_range(struct pci_dev *
             enum pci_mmap_state mmap_state,
             int write_combine)
 {
+    struct pci_controller *pci_ctrl = (struct pci_controller *)dev->sysdata;
+    unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
     int ret;

-    ret = __pci_mmap_make_offset(dev, vma, mmap_state);
-    if (ret < 0)
-        return ret;
+    if (pci_ctrl == 0)
+        return -EINVAL;        /* should never happen */
+
+    if (mmap_state == pci_mmap_io)
+        offset += pci_ctrl->io_space.start - pci_ctrl->io_space.base;
+    vma->vm_pgoff = offset >> PAGE_SHIFT;

     __pci_mmap_set_pgprot(dev, vma, mmap_state, write_combine);

Index: linux-2.6/drivers/pci/pci-sysfs.c
===================================================================
--- linux-2.6.orig/drivers/pci/pci-sysfs.c
+++ linux-2.6/drivers/pci/pci-sysfs.c
@@ -967,12 +967,23 @@ void pci_remove_legacy_files(struct pci_
 #ifdef HAVE_PCI_MMAP

 int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma,
+          enum pci_mmap_state mmap_type,
           enum pci_mmap_api mmap_api)
 {
     unsigned long nr, start, size, pci_start;
+    int res_bit;

     if (pci_resource_len(pdev, resno) == 0)
         return 0;
+
+    if (mmap_type == pci_mmap_mem)
+        res_bit = IORESOURCE_MEM;
+    else
+        res_bit = IORESOURCE_IO;
+
+    if (!(pci_resource_flags(pdev, resno) & res_bit))
+        return 0;
+
     nr = vma_pages(vma);
     start = vma->vm_pgoff;
     size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1;
@@ -999,7 +1010,6 @@ static int pci_mmap_resource(struct kobj
     struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
     struct resource *res = attr->private;
     enum pci_mmap_state mmap_type;
-    resource_size_t start, end;
     int i;

     for (i = 0; i < PCI_ROM_RESOURCE; i++)
@@ -1011,7 +1021,8 @@ static int pci_mmap_resource(struct kobj
     if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start))
         return -EINVAL;

-    if (!pci_mmap_fits(pdev, i, vma, PCI_MMAP_SYSFS)) {
+    mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
+    if (!pci_mmap_fits(pdev, i, vma, mmap_type, 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,
@@ -1020,13 +1031,7 @@ static int pci_mmap_resource(struct kobj
         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;
+    vma->vm_pgoff += res->start >> PAGE_SHIFT;
     return pci_mmap_page_range(pdev, vma, mmap_type, write_combine);
 }

Index: linux-2.6/drivers/pci/proc.c
===================================================================
--- linux-2.6.orig/drivers/pci/proc.c
+++ linux-2.6/drivers/pci/proc.c
@@ -227,26 +227,67 @@ static long proc_bus_pci_ioctl(struct fi
 }

 #ifdef HAVE_PCI_MMAP
+
+static int pci_user_to_resource(struct pci_dev *dev, resource_size_t *offset,
+                resource_size_t size, int flags)
+{
+    int i;
+
+    for (i = 0; i < PCI_ROM_RESOURCE; i++) {
+        resource_size_t start, end;
+        struct resource *res = &dev->resource[i];
+
+        if (!(res->flags & flags))
+            continue;
+
+        if (pci_resource_len(dev, i) == 0)
+            continue;
+
+        pci_resource_to_user(dev, i, res, &start, &end);
+        if (start <= *offset && (*offset + size - 1) <= end) {
+            *offset = res->start + (*offset - start);
+            return i;
+        }
+
+        return i;
+    }
+
+    return -ENODEV;
+}
+
 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;
+    enum pci_mmap_state mmap_type = fpriv->mmap_state;
+    resource_size_t offset, size;
+    int i, ret, flags;

     if (!capable(CAP_SYS_RAWIO))
         return -EPERM;

-    /* 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))
-            break;
-    }
-
-    if (i >= PCI_ROM_RESOURCE)
+    offset = vma->vm_pgoff << PAGE_SHIFT;
+    size = vma->vm_end - vma->vm_start;
+    if (mmap_type == pci_mmap_mem)
+        flags = IORESOURCE_MEM;
+    else
+        flags = IORESOURCE_IO;
+    i = pci_user_to_resource(dev, &offset, size, flags);
+    if (i < 0)
         return -ENODEV;

+    vma->vm_pgoff = offset >> PAGE_SHIFT;
+    if (!pci_mmap_fits(dev, i, vma, mmap_type, PCI_MMAP_PROCFS)) {
+        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(dev), i,
+            (u64)pci_resource_start(dev, i),
+            (u64)pci_resource_len(dev, i));
+        return -EINVAL;
+    }
+
     ret = pci_mmap_page_range(dev, vma,
-                  fpriv->mmap_state,
+                  mmap_type,
                   fpriv->write_combine);
     if (ret < 0)
         return ret;
Index: linux-2.6/drivers/pci/pci.h
===================================================================
--- linux-2.6.orig/drivers/pci/pci.h
+++ linux-2.6/drivers/pci/pci.h
@@ -30,7 +30,7 @@ enum pci_mmap_api {
     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);
+          enum pci_mmap_state mmap_type, enum pci_mmap_api mmap_api);
 #endif
 int pci_probe_reset_function(struct pci_dev *dev);
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux