From 6037ec961e10e6e162c564d5306b2c2fddfb5b77 Mon Sep 17 00:00:00 2001 From: Allen Pais Date: Wed, 21 Oct 2015 00:12:59 +0530 Subject: [PATCH] sparc: Accommodate mem64_offset != mem_offset in pbm configuration 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 Signed-off-by: Allen Pais --- arch/sparc/kernel/pci.c | 52 ++++++++++++++++++++++--------- arch/sparc/kernel/pci_common.c | 57 +++++++++++++++++++++------------- arch/sparc/kernel/pci_impl.h | 1 + 3 files changed, 75 insertions(+), 35 deletions(-) diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index 65615e7ff565..2bcef15b0287 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -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, ®ion, (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) diff --git a/arch/sparc/kernel/pci_common.c b/arch/sparc/kernel/pci_common.c index 957dc9d0300d..c776a302d0b8 100644 --- a/arch/sparc/kernel/pci_common.c +++ b/arch/sparc/kernel/pci_common.c @@ -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; diff --git a/arch/sparc/kernel/pci_impl.h b/arch/sparc/kernel/pci_impl.h index 5b92ad4d1a6b..2f62fd133d75 100644 --- a/arch/sparc/kernel/pci_impl.h +++ b/arch/sparc/kernel/pci_impl.h @@ -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; -- 2.50.1