From: Benjamin Herrenschmidt This patch was discussed at length on linux-pci and so far, the last iteration of it didn't raise any comment. It's effect is a nop on architecture that don't define the new pci_resource_to_user() callback anyway. It allows architecture like ppc who put weird things inside of PCI resource structures to convert to some different value for user visible ones. It also fixes mmap'ing of IO space on those archs. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Andrew Morton --- arch/ppc/kernel/pci.c | 21 +++++++++++++++++++-- arch/ppc64/kernel/pci.c | 30 +++++++++++++++++++++++++++--- drivers/pci/pci-sysfs.c | 26 +++++++++++++++++++++----- drivers/pci/pci.c | 2 +- drivers/pci/proc.c | 14 ++++++++++---- include/asm-ppc/pci.h | 6 ++++++ include/asm-ppc64/pci.h | 5 +++++ include/linux/pci.h | 15 +++++++++++++++ 8 files changed, 104 insertions(+), 15 deletions(-) diff -puN arch/ppc64/kernel/pci.c~fix-pci-mmap-on-ppc-and-ppc64 arch/ppc64/kernel/pci.c --- 25/arch/ppc64/kernel/pci.c~fix-pci-mmap-on-ppc-and-ppc64 2005-05-11 21:15:06.000000000 -0700 +++ 25-akpm/arch/ppc64/kernel/pci.c 2005-05-11 21:15:06.000000000 -0700 @@ -351,9 +351,12 @@ static struct resource *__pci_mmap_make_ *offset += hose->pci_mem_offset; res_bit = IORESOURCE_MEM; } else { - io_offset = (unsigned long)hose->io_base_virt; + io_offset = (unsigned long)hose->io_base_virt - pci_io_base; + printk("offset: %lx, io_base_virt: %p, pci_io_base: %lx, io_offset: %lx\n", + *offset, hose->io_base_virt, pci_io_base, io_offset); *offset += io_offset; res_bit = IORESOURCE_IO; + printk(" -> offset: %lx\n", *offset); } /* @@ -373,12 +376,15 @@ static struct resource *__pci_mmap_make_ continue; /* In the range of this resource? */ + printk(" r%d: %lx -> %lx\n", i, rp->start, 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; + if (mmap_state == pci_mmap_io) { + *offset += hose->io_base_phys - io_offset; + printk(" result: %lx\n", *offset); + } return rp; } @@ -941,4 +947,22 @@ int pci_read_irq_line(struct pci_dev *pc } EXPORT_SYMBOL(pci_read_irq_line); +void pci_resource_to_user(const struct pci_dev *dev, int bar, + const struct resource *rsrc, + u64 *start, u64 *end) +{ + struct pci_controller *hose = pci_bus_to_host(dev->bus); + unsigned long offset = 0; + + if (hose == NULL) + return; + + if (rsrc->flags & IORESOURCE_IO) + offset = pci_io_base - (unsigned long)hose->io_base_virt + + hose->io_base_phys; + + *start = rsrc->start + offset; + *end = rsrc->end + offset; +} + #endif /* CONFIG_PPC_MULTIPLATFORM */ diff -puN arch/ppc/kernel/pci.c~fix-pci-mmap-on-ppc-and-ppc64 arch/ppc/kernel/pci.c --- 25/arch/ppc/kernel/pci.c~fix-pci-mmap-on-ppc-and-ppc64 2005-05-11 21:15:06.000000000 -0700 +++ 25-akpm/arch/ppc/kernel/pci.c 2005-05-11 21:15:06.000000000 -0700 @@ -1495,7 +1495,7 @@ static struct resource *__pci_mmap_make_ *offset += hose->pci_mem_offset; res_bit = IORESOURCE_MEM; } else { - io_offset = (unsigned long)hose->io_base_virt; + io_offset = hose->io_base_virt - ___IO_BASE; *offset += io_offset; res_bit = IORESOURCE_IO; } @@ -1522,7 +1522,7 @@ static struct resource *__pci_mmap_make_ /* found it! construct the final physical address */ if (mmap_state == pci_mmap_io) - *offset += hose->io_base_phys - _IO_BASE; + *offset += hose->io_base_phys - io_offset; return rp; } @@ -1739,6 +1739,23 @@ long sys_pciconfig_iobase(long which, un return result; } +void pci_resource_to_user(const struct pci_dev *dev, int bar, + const struct resource *rsrc, + u64 *start, u64 *end) +{ + struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); + unsigned long offset = 0; + + if (hose == NULL) + return; + + if (rsrc->flags & IORESOURCE_IO) + offset = ___IO_BASE - hose->io_base_virt + hose->io_base_phys; + + *start = rsrc->start + offset; + *end = rsrc->end + offset; +} + void __init pci_init_resource(struct resource *res, unsigned long start, unsigned long end, int flags, char *name) diff -puN drivers/pci/pci.c~fix-pci-mmap-on-ppc-and-ppc64 drivers/pci/pci.c --- 25/drivers/pci/pci.c~fix-pci-mmap-on-ppc-and-ppc64 2005-05-11 21:15:06.000000000 -0700 +++ 25-akpm/drivers/pci/pci.c 2005-05-11 21:15:06.000000000 -0700 @@ -816,7 +816,7 @@ pci_set_consistent_dma_mask(struct pci_d return 0; } #endif - + static int __devinit pci_init(void) { struct pci_dev *dev = NULL; diff -puN drivers/pci/pci-sysfs.c~fix-pci-mmap-on-ppc-and-ppc64 drivers/pci/pci-sysfs.c --- 25/drivers/pci/pci-sysfs.c~fix-pci-mmap-on-ppc-and-ppc64 2005-05-11 21:15:06.000000000 -0700 +++ 25-akpm/drivers/pci/pci-sysfs.c 2005-05-11 21:15:06.000000000 -0700 @@ -60,15 +60,18 @@ resource_show(struct device * dev, char char * str = buf; int i; int max = 7; + u64 start, end; if (pci_dev->subordinate) max = DEVICE_COUNT_RESOURCE; for (i = 0; i < max; i++) { - str += sprintf(str,"0x%016lx 0x%016lx 0x%016lx\n", - pci_resource_start(pci_dev,i), - pci_resource_end(pci_dev,i), - pci_resource_flags(pci_dev,i)); + struct resource *res = &pci_dev->resource[i]; + pci_resource_to_user(pci_dev, i, res, &start, &end); + str += sprintf(str,"0x%016llx 0x%016llx 0x%016llx\n", + (unsigned long long)start, + (unsigned long long)end, + (unsigned long long)res->flags); } return (str - buf); } @@ -301,8 +304,21 @@ pci_mmap_resource(struct kobject *kobj, struct device, kobj)); struct resource *res = (struct resource *)attr->attr.private; enum pci_mmap_state mmap_type; + u64 start, end; + int i; - vma->vm_pgoff += res->start >> PAGE_SHIFT; + for (i = 0; i < PCI_ROM_RESOURCE; i++) + if (res == &pdev->resource[i]) + break; + if (i >= PCI_ROM_RESOURCE) + return -ENODEV; + + /* 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, 0); diff -puN drivers/pci/proc.c~fix-pci-mmap-on-ppc-and-ppc64 drivers/pci/proc.c --- 25/drivers/pci/proc.c~fix-pci-mmap-on-ppc-and-ppc64 2005-05-11 21:15:06.000000000 -0700 +++ 25-akpm/drivers/pci/proc.c 2005-05-11 21:15:06.000000000 -0700 @@ -355,14 +355,20 @@ static int show_device(struct seq_file * dev->device, dev->irq); /* Here should be 7 and not PCI_NUM_RESOURCES as we need to preserve compatibility */ - for(i=0; i<7; i++) + for(i=0; i<7; i++) { + u64 start, end; + pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); seq_printf(m, LONG_FORMAT, - dev->resource[i].start | + ((unsigned long)start) | (dev->resource[i].flags & PCI_REGION_FLAG_MASK)); - for(i=0; i<7; i++) + } + for(i=0; i<7; i++) { + u64 start, end; + pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); seq_printf(m, LONG_FORMAT, dev->resource[i].start < dev->resource[i].end ? - dev->resource[i].end - dev->resource[i].start + 1 : 0); + (unsigned long)(end - start) + 1 : 0); + } seq_putc(m, '\t'); if (drv) seq_printf(m, "%s", drv->name); diff -puN include/asm-ppc64/pci.h~fix-pci-mmap-on-ppc-and-ppc64 include/asm-ppc64/pci.h --- 25/include/asm-ppc64/pci.h~fix-pci-mmap-on-ppc-and-ppc64 2005-05-11 21:15:06.000000000 -0700 +++ 25-akpm/include/asm-ppc64/pci.h 2005-05-11 21:15:06.000000000 -0700 @@ -135,6 +135,11 @@ extern pgprot_t pci_phys_mem_access_prot unsigned long offset, unsigned long size, pgprot_t prot); +#define HAVE_ARCH_PCI_RESOURCE_TO_USER +extern void pci_resource_to_user(const struct pci_dev *dev, int bar, + const struct resource *rsrc, + u64 *start, u64 *end); + #endif /* __KERNEL__ */ diff -puN include/asm-ppc/pci.h~fix-pci-mmap-on-ppc-and-ppc64 include/asm-ppc/pci.h --- 25/include/asm-ppc/pci.h~fix-pci-mmap-on-ppc-and-ppc64 2005-05-11 21:15:06.000000000 -0700 +++ 25-akpm/include/asm-ppc/pci.h 2005-05-11 21:15:06.000000000 -0700 @@ -103,6 +103,12 @@ extern pgprot_t pci_phys_mem_access_prot unsigned long size, pgprot_t prot); +#define HAVE_ARCH_PCI_RESOURCE_TO_USER +extern void pci_resource_to_user(const struct pci_dev *dev, int bar, + const struct resource *rsrc, + u64 *start, u64 *end); + + #endif /* __KERNEL__ */ #endif /* __PPC_PCI_H */ diff -puN include/linux/pci.h~fix-pci-mmap-on-ppc-and-ppc64 include/linux/pci.h --- 25/include/linux/pci.h~fix-pci-mmap-on-ppc-and-ppc64 2005-05-11 21:15:06.000000000 -0700 +++ 25-akpm/include/linux/pci.h 2005-05-11 21:15:06.000000000 -0700 @@ -1020,6 +1020,21 @@ static inline char *pci_name(struct pci_ #define pci_pretty_name(dev) "" #endif + +/* Some archs don't want to expose struct resource to userland as-is + * in sysfs and /proc + */ +#ifndef HAVE_ARCH_PCI_RESOURCE_TO_USER +static void pci_resource_to_user(const struct pci_dev *dev, int bar, + const struct resource *rsrc, + u64 *start, u64 *end) +{ + *start = rsrc->start; + *end = rsrc->end; +} +#endif /* HAVE_ARCH_PCI_RESOURCE_TO_USER */ + + /* * The world is not perfect and supplies us with broken PCI devices. * For at least a part of these bugs we need a work-around, so both _