]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
sparc: Accommodate mem64_offset != mem_offset in pbm configuration
authorAllen Pais <allen.pais@oracle.com>
Tue, 20 Oct 2015 18:42:59 +0000 (00:12 +0530)
committerSantosh Shilimkar <santosh.shilimkar@oracle.com>
Wed, 21 Oct 2015 18:19:19 +0000 (11:19 -0700)
PCI specs do not require mem_offset to be the same as mem64_offset.
This patch adds code to handle the cases where they are not the same
instead of panic'ing the kernel.

Orabug: 21826746

Signed-off-by: Khalid Aziz <khalid.aziz@oracle.com>
Signed-off-by: Allen Pais <allen.pais@oracle.com>
arch/sparc/kernel/pci.c
arch/sparc/kernel/pci_common.c
arch/sparc/kernel/pci_impl.h

index 65615e7ff565f43df4168e6613918acedbaeef1b..2bcef15b0287d647eb8b979994c260e642737b4d 100644 (file)
@@ -670,7 +670,7 @@ struct pci_bus *pci_scan_one_pbm(struct pci_pbm_info *pbm,
                                pbm->mem_offset);
        if (pbm->mem64_space.flags)
                pci_add_resource_offset(&resources, &pbm->mem64_space,
-                                       pbm->mem_offset);
+                                       pbm->mem64_offset);
        pbm->busn.start = pbm->pci_first_busno;
        pbm->busn.end   = pbm->pci_last_busno;
        pbm->busn.flags = IORESOURCE_BUS;
@@ -733,6 +733,30 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
        return 0;
 }
 
+static resource_size_t get_mem_offset(struct pci_pbm_info *pbm,
+                                 resource_size_t bus_addr)
+{
+       resource_size_t start;
+
+       if (pbm->mem_space.flags) {
+               start = bus_addr + pbm->mem_offset;
+
+               if (start >= pbm->mem_space.start &&
+                   start <= pbm->mem_space.end)
+                       return pbm->mem_offset;
+       }
+
+       if (pbm->mem64_space.flags) {
+               start = bus_addr + pbm->mem64_offset;
+
+               if (start >= pbm->mem64_space.start &&
+                   start <= pbm->mem64_space.end)
+                       return pbm->mem64_offset;
+       }
+
+       return 0;
+}
+
 /* Platform support for /proc/bus/pci/X/Y mmap()s. */
 
 /* If the user uses a host-bridge as the PCI device, he may use
@@ -747,17 +771,21 @@ static int __pci_mmap_make_offset_bus(struct pci_dev *pdev, struct vm_area_struc
 {
        struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
        unsigned long space_size, user_offset, user_size;
+       resource_size_t mem_offset = 0;
+
+       /* Make sure the request is in range. */
+       user_offset = vma->vm_pgoff << PAGE_SHIFT;
+       user_size = vma->vm_end - vma->vm_start;
 
        if (mmap_state == pci_mmap_io) {
                space_size = resource_size(&pbm->io_space);
        } else {
+               mem_offset = get_mem_offset(pbm, user_offset);
                space_size = resource_size(&pbm->mem_space);
+               if (mem_offset != pbm->mem_offset && pbm->mem64_space.flags)
+                       space_size = resource_size(&pbm->mem64_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;
@@ -766,7 +794,7 @@ static int __pci_mmap_make_offset_bus(struct pci_dev *pdev, struct vm_area_struc
                vma->vm_pgoff = (pbm->io_offset +
                                 user_offset) >> PAGE_SHIFT;
        } else {
-               vma->vm_pgoff = (pbm->mem_offset +
+               vma->vm_pgoff = (mem_offset +
                                 user_offset) >> PAGE_SHIFT;
        }
 
@@ -989,16 +1017,12 @@ void pci_resource_to_user(const struct pci_dev *pdev, int bar,
                          const struct resource *rp, resource_size_t *start,
                          resource_size_t *end)
 {
-       struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
-       unsigned long offset;
+       struct pci_bus_region region;
 
-       if (rp->flags & IORESOURCE_IO)
-               offset = pbm->io_offset;
-       else
-               offset = pbm->mem_offset;
+       pcibios_resource_to_bus(pdev->bus, &region, (struct resource *)rp);
 
-       *start = rp->start - offset;
-       *end = rp->end - offset;
+       *start = region.start;
+       *end = region.end;
 }
 
 void pcibios_set_master(struct pci_dev *dev)
index 957dc9d0300d936315c2b09935538864d8a90476..c776a302d0b8284c79d4990d7d8897d9d2985988 100644 (file)
@@ -328,39 +328,56 @@ void pci_get_pbm_props(struct pci_pbm_info *pbm)
        }
 }
 
-static void pci_register_region(struct pci_pbm_info *pbm, const char *name,
+static int pci_register_region_one(struct pci_pbm_info *pbm, const char *name,
+                               struct resource *mem_res,
+                               resource_size_t mem_offset,
                                resource_size_t rstart, resource_size_t size)
 {
        struct resource *res, *conflict;
-       struct resource *mem_res = &pbm->mem_space;
-       resource_size_t offset = pbm->mem_offset;
        resource_size_t mem_rstart, mem_rend;
        resource_size_t rend = rstart + size - 1UL;
 
        if (!mem_res->flags)
-               return;
+               return -1;
 
-       mem_rstart = mem_res->start - offset;
-       mem_rend = mem_res->end - offset;
+       mem_rstart = mem_res->start - mem_offset;
+       mem_rend = mem_res->end - mem_offset;
 
        /* contain checking */
        if (!(mem_rstart <= rstart && mem_rend >= rend))
-               return;
+               return -1;
 
        res = kzalloc(sizeof(*res), GFP_KERNEL);
        if (!res)
-               return;
+               return 0;
 
        res->name = name;
        res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
-       res->start = rstart + offset;
-       res->end = rend + offset;
+       res->start = rstart + mem_offset;
+       res->end = rend + mem_offset;
        conflict = request_resource_conflict(mem_res, res);
        if (conflict) {
                printk(KERN_DEBUG "PCI: %s can't claim %s %pR: address conflict with %s %pR\n",
                        pbm->name, res->name, res, conflict->name, conflict);
                kfree(res);
        }
+
+       return 0;
+}
+
+static void pci_register_region(struct pci_pbm_info *pbm, const char *name,
+                               resource_size_t rstart, resource_size_t size)
+{
+       int ret;
+
+       ret = pci_register_region_one(pbm, name, &pbm->mem_space,
+                                     pbm->mem_offset, rstart, size);
+
+       if (!ret)
+               return;
+
+       pci_register_region_one(pbm, name, &pbm->mem64_space,
+                               pbm->mem64_offset, rstart, size);
 }
 
 void pci_register_legacy_regions(struct pci_pbm_info *pbm)
@@ -385,7 +402,6 @@ static void pci_register_iommu_region(struct pci_pbm_info *pbm)
 void pci_determine_mem_io_space(struct pci_pbm_info *pbm)
 {
        const struct linux_prom_pci_ranges *pbm_ranges;
-       resource_size_t mem64_offset = 0;
        int i, saw_mem, saw_io;
        int num_pbm_ranges;
 
@@ -456,7 +472,7 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm)
                        pbm->mem64_space.start = a;
                        pbm->mem64_space.end = a + size - 1UL;
                        pbm->mem64_space.flags = IORESOURCE_MEM;
-                       mem64_offset = a - region_a;
+                       pbm->mem64_offset = a - region_a;
                        saw_mem = 1;
                        break;
 
@@ -478,17 +494,16 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm)
        if (pbm->mem_space.flags)
                printk("%s: PCI MEM %pR offset %llx\n",
                       pbm->name, &pbm->mem_space, pbm->mem_offset);
-       if (pbm->mem64_space.flags) {
-               if (pbm->mem_space.flags) {
-                       if (mem64_offset != pbm->mem_offset)
-                               panic("mem offset %llx != mem64 offset %llx\n",
-                                       pbm->mem_offset, mem64_offset);
-               } else
-                       pbm->mem_offset = mem64_offset;
+       if (pbm->mem64_space.flags && pbm->mem_space.flags) {
+               if (pbm->mem64_space.start <= pbm->mem_space.end)
+                       pbm->mem64_space.start = pbm->mem_space.end + 1;
+               if (pbm->mem64_space.start > pbm->mem64_space.end)
+                       pbm->mem64_space.flags = 0;
+       }
 
+       if (pbm->mem64_space.flags)
                printk("%s: PCI MEM64 %pR offset %llx\n",
-                      pbm->name, &pbm->mem64_space, pbm->mem_offset);
-       }
+                      pbm->name, &pbm->mem64_space, pbm->mem64_offset);
 
        pbm->io_space.name = pbm->mem_space.name = pbm->name;
        pbm->mem64_space.name = pbm->name;
index 5b92ad4d1a6bfbc4f0c606e9357641c1288f3d41..2f62fd133d753bda7b4b63b1b432fe4ccc305fdb 100644 (file)
@@ -102,6 +102,7 @@ struct pci_pbm_info {
        /* offset */
        resource_size_t                 io_offset;
        resource_size_t                 mem_offset;
+       resource_size_t                 mem64_offset;
 
        /* Base of PCI Config space, can be per-PBM or shared. */
        unsigned long                   config_space;