From 6bccc7f426abd640f08d8c75fb22f99483f201b4 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:25:50 +0100 Subject: PCI: Fix pci_mmap_fits() for HAVE_PCI_RESOURCE_TO_USER platforms In the PCI_MMAP_PROCFS case when the address being passed by the user is a 'user visible' resource address based on the bus window, and not the actual contents of the resource, that's what we need to be checking it against. Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org --- drivers/pci/pci-sysfs.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 25d010d449a3..7ac258fd3c5c 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -985,15 +985,19 @@ void pci_remove_legacy_files(struct pci_bus *b) 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; -- cgit v1.2.3 From 17caf56731311c9596e7d38a70c88fcb6afa6a1b Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:25:51 +0100 Subject: PCI: Fix another sanity check bug in /proc/pci mmap Don't match MMIO maps with I/O BARs and vice versa. Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org --- drivers/pci/proc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index f82710a8694d..62a0c3e788d3 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -231,14 +231,20 @@ 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, res_bit; if (!capable(CAP_SYS_RAWIO)) return -EPERM; + if (fpriv->mmap_state == pci_mmap_io) + res_bit = IORESOURCE_IO; + else + res_bit = IORESOURCE_MEM; + /* 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; } -- cgit v1.2.3 From cef4d02305a06be581bb7f4353446717a1b319ec Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:25:52 +0100 Subject: PCI: Only allow WC mmap on prefetchable resources The /proc/bus/pci mmap interface allows the user to specify whether they want WC or not. Don't let them do so on non-prefetchable BARs. Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org --- drivers/pci/proc.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index 62a0c3e788d3..dc8912e2d4a1 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -231,7 +231,7 @@ 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, res_bit; + int i, ret, write_combine = 0, res_bit; if (!capable(CAP_SYS_RAWIO)) return -EPERM; @@ -251,10 +251,13 @@ static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma) if (i >= PCI_ROM_RESOURCE) return -ENODEV; - if (fpriv->mmap_state == pci_mmap_mem) - write_combine = fpriv->write_combine; - else - write_combine = 0; + 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, vma, fpriv->mmap_state, write_combine); if (ret < 0) -- cgit v1.2.3 From 03a064b431eb5cb0a91012699ac1e4d6302b327d Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:25:53 +0100 Subject: xtensa/PCI: Do not mmap PCI BARs to userspace as write-through These should be uncached, not write-through. Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas --- arch/xtensa/kernel/pci.c | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c index b848cc3dc913..5b73fc2f076c 100644 --- a/arch/xtensa/kernel/pci.c +++ b/arch/xtensa/kernel/pci.c @@ -333,25 +333,6 @@ __pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vma, return -EINVAL; } -/* - * Set vm_page_prot of VMA, as appropriate for this architecture, for a pci - * device mapping. - */ -static __inline__ void -__pci_mmap_set_pgprot(struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine) -{ - int prot = pgprot_val(vma->vm_page_prot); - - /* Set to write-through */ - prot = (prot & _PAGE_CA_MASK) | _PAGE_CA_WT; -#if 0 - if (!write_combine) - prot |= _PAGE_WRITETHRU; -#endif - vma->vm_page_prot = __pgprot(prot); -} - /* * Perform the actual remap of the pages for a PCI device mapping, as * appropriate for this architecture. The region in the process to map @@ -372,7 +353,7 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, if (ret < 0) return ret; - __pci_mmap_set_pgprot(dev, vma, mmap_state, write_combine); + vma->vm_page_prot = pgprot_device(vma->vm_page_prot); ret = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, vma->vm_end - vma->vm_start,vma->vm_page_prot); -- cgit v1.2.3 From ae749c7ab475de2c9c427058db19921c91846e89 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:25:54 +0100 Subject: PCI: Add arch_can_pci_mmap_wc() macro Most of the almost-identical versions of pci_mmap_page_range() silently ignore the 'write_combine' argument and give uncached mappings. Yet we allow the PCIIOC_WRITE_COMBINE ioctl in /proc/bus/pci, expose the 'resourceX_wc' file in sysfs, and allow an attempted mapping to apparently succeed. To fix this, introduce a macro arch_can_pci_mmap_wc() which indicates whether the platform can do a write-combining mapping. On x86 this ends up being pat_enabled(), while the few other platforms that support it can just set it to a literal '1'. Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas --- Documentation/filesystems/sysfs-pci.txt | 4 ++++ arch/ia64/include/asm/pci.h | 2 ++ arch/powerpc/include/asm/pci.h | 5 +++-- arch/x86/include/asm/pci.h | 2 ++ drivers/pci/pci-sysfs.c | 4 ++-- drivers/pci/proc.c | 15 ++++++++------- include/linux/pci.h | 4 ++++ 7 files changed, 25 insertions(+), 11 deletions(-) diff --git a/Documentation/filesystems/sysfs-pci.txt b/Documentation/filesystems/sysfs-pci.txt index 6ea1ceda6f52..25b7f1c1b868 100644 --- a/Documentation/filesystems/sysfs-pci.txt +++ b/Documentation/filesystems/sysfs-pci.txt @@ -117,6 +117,10 @@ code must define HAVE_PCI_MMAP and provide a pci_mmap_page_range function. Platforms are free to only support subsets of the mmap functionality, but useful return codes should be provided. +Platforms which support write-combining maps of PCI resources must define +arch_can_pci_mmap_wc() which shall evaluate to non-zero at runtime when +write-combining is permitted. + Legacy resources are protected by the HAVE_PCI_LEGACY define. Platforms wishing to support legacy functionality should define it and provide pci_legacy_read, pci_legacy_write and pci_mmap_legacy_page_range functions. diff --git a/arch/ia64/include/asm/pci.h b/arch/ia64/include/asm/pci.h index c0835b0dc722..6283758474ad 100644 --- a/arch/ia64/include/asm/pci.h +++ b/arch/ia64/include/asm/pci.h @@ -51,6 +51,8 @@ extern unsigned long ia64_max_iommu_merge_mask; #define PCI_DMA_BUS_IS_PHYS (ia64_max_iommu_merge_mask == ~0UL) #define HAVE_PCI_MMAP +#define arch_can_pci_mmap_wc() 1 + extern int pci_mmap_page_range (struct pci_dev *dev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine); #define HAVE_PCI_LEGACY diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index 93eded8d3843..b5b68c6a10b1 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h @@ -81,8 +81,9 @@ struct vm_area_struct; int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine); -/* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */ -#define HAVE_PCI_MMAP 1 +/* Tell drivers/pci/proc.c that we have pci_mmap_page_range() and it does WC */ +#define HAVE_PCI_MMAP 1 +#define arch_can_pci_mmap_wc() 1 extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, size_t count); diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index 1411dbed5e5e..f6e22c271efa 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #ifdef __KERNEL__ @@ -102,6 +103,7 @@ int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq); #define HAVE_PCI_MMAP +#define arch_can_pci_mmap_wc() pat_enabled() extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 7ac258fd3c5c..7d494bd66a97 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1211,9 +1211,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; diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index dc8912e2d4a1..a2aa58a8fb96 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -210,14 +210,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; diff --git a/include/linux/pci.h b/include/linux/pci.h index eb3da1a04e6c..e614fb42d8bb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1626,6 +1626,10 @@ static inline int pci_get_new_domain_nr(void) { return -ENOSYS; } #include +#ifndef arch_can_pci_mmap_wc +#define arch_can_pci_mmap_wc() 0 +#endif + #ifndef pci_root_bus_fwnode #define pci_root_bus_fwnode(bus) NULL #endif -- cgit v1.2.3 From 11df19546fe4a6135cdae62e96a1e25b3fabf6ea Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:25:55 +0100 Subject: PCI: Move multiple declarations of pci_mmap_page_range() to We can declare it even on platforms where it isn't going to be defined. There's no need to have it littered through the various files. Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas --- arch/arm/include/asm/pci.h | 2 -- arch/cris/include/asm/pci.h | 3 --- arch/ia64/include/asm/pci.h | 2 -- arch/microblaze/include/asm/pci.h | 3 --- arch/mips/include/asm/pci.h | 3 --- arch/mn10300/include/asm/pci.h | 3 --- arch/parisc/include/asm/pci.h | 3 --- arch/powerpc/include/asm/pci.h | 3 --- arch/sh/include/asm/pci.h | 3 +-- arch/sparc/include/asm/pci_64.h | 4 ---- arch/unicore32/include/asm/pci.h | 2 -- arch/x86/include/asm/pci.h | 4 ---- arch/xtensa/include/asm/pci.h | 4 ---- include/linux/pci.h | 7 +++++++ 14 files changed, 8 insertions(+), 38 deletions(-) diff --git a/arch/arm/include/asm/pci.h b/arch/arm/include/asm/pci.h index 057d381f4e57..51118a0cabbc 100644 --- a/arch/arm/include/asm/pci.h +++ b/arch/arm/include/asm/pci.h @@ -29,8 +29,6 @@ static inline int pci_proc_domain(struct pci_bus *bus) #define PCI_DMA_BUS_IS_PHYS (1) #define HAVE_PCI_MMAP -extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine); static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) { diff --git a/arch/cris/include/asm/pci.h b/arch/cris/include/asm/pci.h index b1b289df04c7..65198cb46c85 100644 --- a/arch/cris/include/asm/pci.h +++ b/arch/cris/include/asm/pci.h @@ -42,9 +42,6 @@ struct pci_dev; #define PCI_DMA_BUS_IS_PHYS (1) #define HAVE_PCI_MMAP -extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine); - #endif /* __KERNEL__ */ diff --git a/arch/ia64/include/asm/pci.h b/arch/ia64/include/asm/pci.h index 6283758474ad..fc60b3d139f4 100644 --- a/arch/ia64/include/asm/pci.h +++ b/arch/ia64/include/asm/pci.h @@ -53,8 +53,6 @@ extern unsigned long ia64_max_iommu_merge_mask; #define HAVE_PCI_MMAP #define arch_can_pci_mmap_wc() 1 -extern int pci_mmap_page_range (struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine); #define HAVE_PCI_LEGACY extern int pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma, diff --git a/arch/microblaze/include/asm/pci.h b/arch/microblaze/include/asm/pci.h index 2a120bb70e54..ab381d283fb9 100644 --- a/arch/microblaze/include/asm/pci.h +++ b/arch/microblaze/include/asm/pci.h @@ -46,9 +46,6 @@ extern int pci_domain_nr(struct pci_bus *bus); extern int pci_proc_domain(struct pci_bus *bus); struct vm_area_struct; -/* Map a range of PCI memory or I/O space for a device into user space */ -int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine); /* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */ #define HAVE_PCI_MMAP 1 diff --git a/arch/mips/include/asm/pci.h b/arch/mips/include/asm/pci.h index 30d1129d8624..3141e2a8113a 100644 --- a/arch/mips/include/asm/pci.h +++ b/arch/mips/include/asm/pci.h @@ -111,9 +111,6 @@ extern void pcibios_set_master(struct pci_dev *dev); #define HAVE_PCI_MMAP -extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine); - #define HAVE_ARCH_PCI_RESOURCE_TO_USER /* diff --git a/arch/mn10300/include/asm/pci.h b/arch/mn10300/include/asm/pci.h index 51159fff025a..082b6de90936 100644 --- a/arch/mn10300/include/asm/pci.h +++ b/arch/mn10300/include/asm/pci.h @@ -74,9 +74,6 @@ static inline int pci_controller_num(struct pci_dev *dev) } #define HAVE_PCI_MMAP -extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, - int write_combine); #endif /* __KERNEL__ */ diff --git a/arch/parisc/include/asm/pci.h b/arch/parisc/include/asm/pci.h index defebd956585..bb9ea9003e08 100644 --- a/arch/parisc/include/asm/pci.h +++ b/arch/parisc/include/asm/pci.h @@ -201,7 +201,4 @@ static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) #define HAVE_PCI_MMAP -extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine); - #endif /* __ASM_PARISC_PCI_H */ diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index b5b68c6a10b1..55887d171205 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h @@ -77,9 +77,6 @@ extern int pci_domain_nr(struct pci_bus *bus); extern int pci_proc_domain(struct pci_bus *bus); struct vm_area_struct; -/* Map a range of PCI memory or I/O space for a device into user space */ -int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine); /* Tell drivers/pci/proc.c that we have pci_mmap_page_range() and it does WC */ #define HAVE_PCI_MMAP 1 diff --git a/arch/sh/include/asm/pci.h b/arch/sh/include/asm/pci.h index 644314f2b1ef..46abbc9983c9 100644 --- a/arch/sh/include/asm/pci.h +++ b/arch/sh/include/asm/pci.h @@ -66,8 +66,7 @@ extern unsigned long PCIBIOS_MIN_IO, PCIBIOS_MIN_MEM; struct pci_dev; #define HAVE_PCI_MMAP -extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine); + extern void pcibios_set_master(struct pci_dev *dev); /* Dynamic DMA mapping stuff. diff --git a/arch/sparc/include/asm/pci_64.h b/arch/sparc/include/asm/pci_64.h index 2303635158f5..516fda70a1bb 100644 --- a/arch/sparc/include/asm/pci_64.h +++ b/arch/sparc/include/asm/pci_64.h @@ -45,10 +45,6 @@ static inline int pci_proc_domain(struct pci_bus *bus) #define HAVE_ARCH_PCI_GET_UNMAPPED_AREA #define get_pci_unmapped_area get_fb_unmapped_area -int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, - int write_combine); - static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) { return PCI_IRQ_NONE; diff --git a/arch/unicore32/include/asm/pci.h b/arch/unicore32/include/asm/pci.h index 37e55d018de5..a51290862acd 100644 --- a/arch/unicore32/include/asm/pci.h +++ b/arch/unicore32/include/asm/pci.h @@ -17,8 +17,6 @@ #include /* for PCIBIOS_MIN_* */ #define HAVE_PCI_MMAP -extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine); #endif /* __KERNEL__ */ #endif diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index f6e22c271efa..734cc9411b3a 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -104,10 +104,6 @@ int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq); #define HAVE_PCI_MMAP #define arch_can_pci_mmap_wc() pat_enabled() -extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, - int write_combine); - #ifdef CONFIG_PCI extern void early_quirks(void); diff --git a/arch/xtensa/include/asm/pci.h b/arch/xtensa/include/asm/pci.h index 5d6bd932ba4e..bb5510b28754 100644 --- a/arch/xtensa/include/asm/pci.h +++ b/arch/xtensa/include/asm/pci.h @@ -46,10 +46,6 @@ struct pci_dev; #define PCI_DMA_BUS_IS_PHYS (1) -/* Map a range of PCI memory or I/O space for a device into user space */ -int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine); - /* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */ #define HAVE_PCI_MMAP 1 diff --git a/include/linux/pci.h b/include/linux/pci.h index e614fb42d8bb..e7bb4b62cc97 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1626,6 +1626,13 @@ static inline int pci_get_new_domain_nr(void) { return -ENOSYS; } #include +/* Map a range of PCI memory or I/O space for a device into user space. + * Architectures provide this function if they set HAVE_PCI_MMAP, and + * it accepts the 'write_combine' argument when arch_can_pci_mmap_wc() + * evaluates to nonzero. */ +int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine); + #ifndef arch_can_pci_mmap_wc #define arch_can_pci_mmap_wc() 0 #endif -- cgit v1.2.3 From e854d8b2a82ef76521ad2bed68211fde0511d417 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:25:56 +0100 Subject: PCI: Add arch_can_pci_mmap_io() on architectures which can mmap() I/O space This is relatively esoteric, and knowing that we don't have it makes life easier in some cases rather than just an eventual -EINVAL from pci_mmap_page_range(). Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas --- Documentation/filesystems/sysfs-pci.txt | 3 ++- arch/microblaze/include/asm/pci.h | 3 ++- arch/powerpc/include/asm/pci.h | 1 + arch/sparc/include/asm/pci_64.h | 1 + arch/xtensa/include/asm/pci.h | 3 ++- drivers/pci/pci-sysfs.c | 13 ++++++++----- drivers/pci/proc.c | 11 +++++++---- include/linux/pci.h | 3 +++ 8 files changed, 26 insertions(+), 12 deletions(-) diff --git a/Documentation/filesystems/sysfs-pci.txt b/Documentation/filesystems/sysfs-pci.txt index 25b7f1c1b868..46b95d82c4fd 100644 --- a/Documentation/filesystems/sysfs-pci.txt +++ b/Documentation/filesystems/sysfs-pci.txt @@ -119,7 +119,8 @@ useful return codes should be provided. Platforms which support write-combining maps of PCI resources must define arch_can_pci_mmap_wc() which shall evaluate to non-zero at runtime when -write-combining is permitted. +write-combining is permitted. Platforms which support maps of I/O resources +define arch_can_pci_mmap_io() similarly. Legacy resources are protected by the HAVE_PCI_LEGACY define. Platforms wishing to support legacy functionality should define it and provide diff --git a/arch/microblaze/include/asm/pci.h b/arch/microblaze/include/asm/pci.h index ab381d283fb9..efd4983cb697 100644 --- a/arch/microblaze/include/asm/pci.h +++ b/arch/microblaze/include/asm/pci.h @@ -48,7 +48,8 @@ extern int pci_proc_domain(struct pci_bus *bus); struct vm_area_struct; /* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */ -#define HAVE_PCI_MMAP 1 +#define HAVE_PCI_MMAP 1 +#define arch_can_pci_mmap_io() 1 extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, size_t count); diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index 55887d171205..c8975dac535f 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h @@ -80,6 +80,7 @@ struct vm_area_struct; /* Tell drivers/pci/proc.c that we have pci_mmap_page_range() and it does WC */ #define HAVE_PCI_MMAP 1 +#define arch_can_pci_mmap_io() 1 #define arch_can_pci_mmap_wc() 1 extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, diff --git a/arch/sparc/include/asm/pci_64.h b/arch/sparc/include/asm/pci_64.h index 516fda70a1bb..b957ca5527a3 100644 --- a/arch/sparc/include/asm/pci_64.h +++ b/arch/sparc/include/asm/pci_64.h @@ -42,6 +42,7 @@ static inline int pci_proc_domain(struct pci_bus *bus) /* Platform support for /proc/bus/pci/X/Y mmap()s. */ #define HAVE_PCI_MMAP +#define arch_can_pci_mmap_io() 1 #define HAVE_ARCH_PCI_GET_UNMAPPED_AREA #define get_pci_unmapped_area get_fb_unmapped_area diff --git a/arch/xtensa/include/asm/pci.h b/arch/xtensa/include/asm/pci.h index bb5510b28754..e4f366a488d3 100644 --- a/arch/xtensa/include/asm/pci.h +++ b/arch/xtensa/include/asm/pci.h @@ -47,7 +47,8 @@ struct pci_dev; #define PCI_DMA_BUS_IS_PHYS (1) /* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */ -#define HAVE_PCI_MMAP 1 +#define HAVE_PCI_MMAP 1 +#define arch_can_pci_mmap_io() 1 #endif /* __KERNEL__ */ diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 7d494bd66a97..cb04bc29943f 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1174,11 +1174,14 @@ 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; diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index a2aa58a8fb96..45e5cf7e9193 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -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; @@ -232,15 +234,16 @@ 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 = 0, res_bit; + 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 (fpriv->mmap_state == pci_mmap_io) { + if (!arch_can_pci_mmap_io()) + return -EINVAL; res_bit = IORESOURCE_IO; - else - res_bit = IORESOURCE_MEM; + } /* Make sure the caller is mapping a real resource for this device */ for (i = 0; i < PCI_ROM_RESOURCE; i++) { diff --git a/include/linux/pci.h b/include/linux/pci.h index e7bb4b62cc97..590cfcf6acf5 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1636,6 +1636,9 @@ int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, #ifndef arch_can_pci_mmap_wc #define arch_can_pci_mmap_wc() 0 #endif +#ifndef arch_can_pci_mmap_io +#define arch_can_pci_mmap_io() 0 +#endif #ifndef pci_root_bus_fwnode #define pci_root_bus_fwnode(bus) NULL -- cgit v1.2.3 From dca40b186b757c7f9a05f870f69e0dfaeca59d7b Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:25:57 +0100 Subject: PCI: Use BAR index in sysfs attr->private instead of resource pointer We store the pointer, and then on *every* use of it we loop over the device's resources to find out the index. That's kind of silly. Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-sysfs.c | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index cb04bc29943f..534844df8bf5 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1017,26 +1017,20 @@ 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; } @@ -1044,7 +1038,7 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, * 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); + pci_resource_to_user(pdev, bar, 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); @@ -1069,22 +1063,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) { @@ -1186,7 +1176,7 @@ static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine) 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); -- cgit v1.2.3 From f66e225828c1b046c7db1db65b0dd2d135f6a2da Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:25:58 +0100 Subject: PCI: Add BAR index argument to pci_mmap_page_range() In all cases we know which BAR it is. Passing it in means that arch code (or generic code; watch this space) won't have to go looking for it again. Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas --- arch/arm/kernel/bios32.c | 3 ++- arch/cris/arch-v32/drivers/pci/bios.c | 3 ++- arch/ia64/pci/pci.c | 3 ++- arch/microblaze/pci/pci-common.c | 2 +- arch/mips/pci/pci.c | 3 ++- arch/mn10300/unit-asb2305/pci-asb2305.c | 3 ++- arch/parisc/kernel/pci.c | 3 ++- arch/powerpc/kernel/pci-common.c | 3 ++- arch/sh/drivers/pci/pci.c | 3 ++- arch/sparc/kernel/pci.c | 6 +++--- arch/unicore32/kernel/pci.c | 3 ++- arch/x86/pci/i386.c | 3 ++- arch/xtensa/kernel/pci.c | 3 ++- drivers/pci/pci-sysfs.c | 2 +- drivers/pci/proc.c | 2 +- include/linux/pci.h | 3 ++- 16 files changed, 30 insertions(+), 18 deletions(-) diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 2f0e07735d1d..a4fc3f46eeae 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -597,7 +597,8 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, return start; } -int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, +int pci_mmap_page_range(struct pci_dev *dev, int bar, + struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine) { if (mmap_state == pci_mmap_io) diff --git a/arch/cris/arch-v32/drivers/pci/bios.c b/arch/cris/arch-v32/drivers/pci/bios.c index 212266a2c5d9..a589686d5448 100644 --- a/arch/cris/arch-v32/drivers/pci/bios.c +++ b/arch/cris/arch-v32/drivers/pci/bios.c @@ -14,7 +14,8 @@ void pcibios_set_master(struct pci_dev *dev) pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); } -int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, +int pci_mmap_page_range(struct pci_dev *dev, int bar, + struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine) { unsigned long prot; diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 8f6ac2f8ae4c..053c688b15a5 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -419,7 +419,8 @@ pcibios_align_resource (void *data, const struct resource *res, } int -pci_mmap_page_range (struct pci_dev *dev, struct vm_area_struct *vma, +pci_mmap_page_range (struct pci_dev *dev, int bar, + struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine) { unsigned long size = vma->vm_end - vma->vm_start; diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c index 13bc93242c0c..404fb38d06b7 100644 --- a/arch/microblaze/pci/pci-common.c +++ b/arch/microblaze/pci/pci-common.c @@ -278,7 +278,7 @@ pgprot_t pci_phys_mem_access_prot(struct file *file, * * Returns a negative error code on failure, zero on success. */ -int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, +int pci_mmap_page_range(struct pci_dev *dev, int bar, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine) { resource_size_t offset = diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c index f6325fa657fb..f189502041a6 100644 --- a/arch/mips/pci/pci.c +++ b/arch/mips/pci/pci.c @@ -58,7 +58,8 @@ void pci_resource_to_user(const struct pci_dev *dev, int bar, *end = rsrc->start + size; } -int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, +int pci_mmap_page_range(struct pci_dev *dev, int bar, + struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine) { unsigned long prot; diff --git a/arch/mn10300/unit-asb2305/pci-asb2305.c b/arch/mn10300/unit-asb2305/pci-asb2305.c index b7ab8378964c..4abbbd54ac7d 100644 --- a/arch/mn10300/unit-asb2305/pci-asb2305.c +++ b/arch/mn10300/unit-asb2305/pci-asb2305.c @@ -211,7 +211,8 @@ void __init pcibios_resource_survey(void) pcibios_allocate_resources(1); } -int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, +int pci_mmap_page_range(struct pci_dev *dev, int bar, + struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine) { unsigned long prot; diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c index 0903c6abd7a4..653877553acd 100644 --- a/arch/parisc/kernel/pci.c +++ b/arch/parisc/kernel/pci.c @@ -228,7 +228,8 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, } -int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, +int pci_mmap_page_range(struct pci_dev *dev, int bar, + struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine) { unsigned long prot; diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index ffda24a38dda..6dda4a20de6e 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -513,7 +513,8 @@ pgprot_t pci_phys_mem_access_prot(struct file *file, * * Returns a negative error code on failure, zero on success. */ -int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, +int pci_mmap_page_range(struct pci_dev *dev, int bar, + struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine) { resource_size_t offset = diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c index 84563e39a5b8..c8b36b7ceb98 100644 --- a/arch/sh/drivers/pci/pci.c +++ b/arch/sh/drivers/pci/pci.c @@ -269,7 +269,8 @@ void __ref pcibios_report_status(unsigned int status_mask, int warn) } } -int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, +int pci_mmap_page_range(struct pci_dev *dev, int bar, + struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine) { /* diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c index 015e55a7495d..7eceaa10836f 100644 --- a/arch/sparc/kernel/pci.c +++ b/arch/sparc/kernel/pci.c @@ -862,9 +862,9 @@ static void __pci_mmap_set_pgprot(struct pci_dev *dev, struct vm_area_struct *vm * * Returns a negative error code on failure, zero on success. */ -int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, - int write_combine) +int pci_mmap_page_range(struct pci_dev *dev, int bar, + struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine) { int ret; diff --git a/arch/unicore32/kernel/pci.c b/arch/unicore32/kernel/pci.c index 62137d13c6f9..1b438857e91f 100644 --- a/arch/unicore32/kernel/pci.c +++ b/arch/unicore32/kernel/pci.c @@ -357,7 +357,8 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) return 0; } -int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, +int pci_mmap_page_range(struct pci_dev *dev, int bar, + struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine) { unsigned long phys; diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 0a9f2caf358f..8ca5e5d9f251 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -411,7 +411,8 @@ static const struct vm_operations_struct pci_mmap_ops = { .access = generic_access_phys, }; -int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, +int pci_mmap_page_range(struct pci_dev *dev, int bar, + struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine) { unsigned long prot; diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c index 5b73fc2f076c..903963ee495d 100644 --- a/arch/xtensa/kernel/pci.c +++ b/arch/xtensa/kernel/pci.c @@ -343,7 +343,8 @@ __pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vma, * * Returns a negative error code on failure, zero on success. */ -int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, +int pci_mmap_page_range(struct pci_dev *dev, int bar, + struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine) { diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 534844df8bf5..bfd9efe637c4 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1041,7 +1041,7 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, pci_resource_to_user(pdev, bar, 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_page_range(pdev, bar, vma, mmap_type, write_combine); } static int pci_mmap_resource_uc(struct file *filp, struct kobject *kobj, diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index 45e5cf7e9193..098360d7ff81 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -262,7 +262,7 @@ static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma) else return -EINVAL; } - ret = pci_mmap_page_range(dev, vma, + ret = pci_mmap_page_range(dev, i, vma, fpriv->mmap_state, write_combine); if (ret < 0) return ret; diff --git a/include/linux/pci.h b/include/linux/pci.h index 590cfcf6acf5..7173a677d6dd 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1630,7 +1630,8 @@ static inline int pci_get_new_domain_nr(void) { return -ENOSYS; } * Architectures provide this function if they set HAVE_PCI_MMAP, and * it accepts the 'write_combine' argument when arch_can_pci_mmap_wc() * evaluates to nonzero. */ -int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, +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); #ifndef arch_can_pci_mmap_wc -- cgit v1.2.3 From f719582435afe9c7985206e42d804ea6aa315d33 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:25:59 +0100 Subject: PCI: Add pci_mmap_resource_range() and use it for ARM64 Starting to leave behind the legacy of the pci_mmap_page_range() interface which takes "user-visible" BAR addresses. This takes just the resource and offset. For now, both APIs coexist and depending on the platform, one is implemented as a wrapper around the other. Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas --- Documentation/filesystems/sysfs-pci.txt | 10 ++-- arch/arm64/include/asm/pci.h | 2 + drivers/pci/Makefile | 2 +- drivers/pci/mmap.c | 95 +++++++++++++++++++++++++++++++++ drivers/pci/pci-sysfs.c | 13 ++--- drivers/pci/pci.h | 4 +- include/linux/pci.h | 19 +++++-- 7 files changed, 125 insertions(+), 20 deletions(-) create mode 100644 drivers/pci/mmap.c diff --git a/Documentation/filesystems/sysfs-pci.txt b/Documentation/filesystems/sysfs-pci.txt index 46b95d82c4fd..06f1d64c6f70 100644 --- a/Documentation/filesystems/sysfs-pci.txt +++ b/Documentation/filesystems/sysfs-pci.txt @@ -113,9 +113,13 @@ Supporting PCI access on new platforms -------------------------------------- In order to support PCI resource mapping as described above, Linux platform -code must define HAVE_PCI_MMAP and provide a pci_mmap_page_range function. -Platforms are free to only support subsets of the mmap functionality, but -useful return codes should be provided. +code should ideally define ARCH_GENERIC_PCI_MMAP_RESOURCE and use the generic +implementation of that functionality. To support the historical interface of +mmap() through files in /proc/bus/pci, platforms may also set HAVE_PCI_MMAP. + +Alternatively, platforms which set HAVE_PCI_MMAP may provide their own +implementation of pci_mmap_page_range() instead of defining +ARCH_GENERIC_PCI_MMAP_RESOURCE. Platforms which support write-combining maps of PCI resources must define arch_can_pci_mmap_wc() which shall evaluate to non-zero at runtime when diff --git a/arch/arm64/include/asm/pci.h b/arch/arm64/include/asm/pci.h index b9a7ba9ca44c..1fc19744ffe9 100644 --- a/arch/arm64/include/asm/pci.h +++ b/arch/arm64/include/asm/pci.h @@ -22,6 +22,8 @@ */ #define PCI_DMA_BUS_IS_PHYS (0) +#define ARCH_GENERIC_PCI_MMAP_RESOURCE 1 + extern int isa_dma_bridge_buggy; #ifdef CONFIG_PCI diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 8db5079f09a7..3d40e415a171 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -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 diff --git a/drivers/pci/mmap.c b/drivers/pci/mmap.c new file mode 100644 index 000000000000..b7aca9c89c31 --- /dev/null +++ b/drivers/pci/mmap.c @@ -0,0 +1,95 @@ +/* + * mmap.c — generic PCI resource mmap helper + * + * Copyright © 2017 Amazon.com, Inc. or its affiliates. + * + * Author: David Woodhouse + * + * 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 +#include +#include + +#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; + + if (mmap_state == pci_mmap_io) + return -EINVAL; + + 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); + + 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 diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index bfd9efe637c4..10feb98a2b1d 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -980,7 +980,7 @@ 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) @@ -1019,7 +1019,6 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); int bar = (unsigned long)attr->private; enum pci_mmap_state mmap_type; - resource_size_t start, end; struct resource *res = &pdev->resource[bar]; if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start)) @@ -1033,15 +1032,9 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, (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, bar, 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, bar, 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, diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 8dd38e69d6f2..8e5ca2dec7e7 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -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//resource */ PCI_MMAP_PROCFS /* mmap on /proc/bus/pci/ */ }; 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); /** diff --git a/include/linux/pci.h b/include/linux/pci.h index 7173a677d6dd..98a72abcf361 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1626,10 +1626,21 @@ static inline int pci_get_new_domain_nr(void) { return -ENOSYS; } #include -/* Map a range of PCI memory or I/O space for a device into user space. - * Architectures provide this function if they set HAVE_PCI_MMAP, and - * it accepts the 'write_combine' argument when arch_can_pci_mmap_wc() - * evaluates to nonzero. */ +/* These two functions provide almost identical functionality. Depennding + * on the architecture, one will be implemented as a wrapper around the + * other (in drivers/pci/mmap.c). + * + * pci_mmap_resource_range() maps a specific BAR, and vm->vm_pgoff + * is expected to be an offset within that region. + * + * pci_mmap_page_range() is the legacy architecture-specific interface, + * which accepts a "user visible" resource address converted by + * pci_resource_to_user(), as used in the legacy mmap() interface in + * /proc/bus/pci/. + */ +int pci_mmap_resource_range(struct pci_dev *dev, int bar, + struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine); 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); -- cgit v1.2.3 From 00d2904ffeac067d7fe29c04edcfa0216102b4c2 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:26:00 +0100 Subject: ARM/PCI: Use generic pci_mmap_resource_range() Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas --- arch/arm/include/asm/pci.h | 1 + arch/arm/kernel/bios32.c | 20 -------------------- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/arch/arm/include/asm/pci.h b/arch/arm/include/asm/pci.h index 51118a0cabbc..396c92bcc0cf 100644 --- a/arch/arm/include/asm/pci.h +++ b/arch/arm/include/asm/pci.h @@ -29,6 +29,7 @@ static inline int pci_proc_domain(struct pci_bus *bus) #define PCI_DMA_BUS_IS_PHYS (1) #define HAVE_PCI_MMAP +#define ARCH_GENERIC_PCI_MMAP_RESOURCE static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) { diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index a4fc3f46eeae..b259956365a0 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -597,26 +597,6 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, return start; } -int pci_mmap_page_range(struct pci_dev *dev, int bar, - struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine) -{ - if (mmap_state == pci_mmap_io) - return -EINVAL; - - /* - * Mark this as IO - */ - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - - if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, - vma->vm_end - vma->vm_start, - vma->vm_page_prot)) - return -EAGAIN; - - return 0; -} - void __init pci_map_io_early(unsigned long pfn) { struct map_desc pci_io_desc = { -- cgit v1.2.3 From 3efdd5fe0a363e308ed61d020f14371dfb5aab4a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:26:01 +0100 Subject: cris/PCI: Use generic pci_mmap_resource_range() Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas Acked-by: Jesper Nilsson --- arch/cris/arch-v32/drivers/pci/bios.c | 23 ----------------------- arch/cris/include/asm/pci.h | 1 + 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/arch/cris/arch-v32/drivers/pci/bios.c b/arch/cris/arch-v32/drivers/pci/bios.c index a589686d5448..394c2a73d5e2 100644 --- a/arch/cris/arch-v32/drivers/pci/bios.c +++ b/arch/cris/arch-v32/drivers/pci/bios.c @@ -14,29 +14,6 @@ void pcibios_set_master(struct pci_dev *dev) pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); } -int pci_mmap_page_range(struct pci_dev *dev, int bar, - struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine) -{ - unsigned long prot; - - /* Leave vm_pgoff as-is, the PCI space address is the physical - * address on this platform. - */ - prot = pgprot_val(vma->vm_page_prot); - vma->vm_page_prot = __pgprot(prot); - - /* Write-combine setting is ignored, it is changed via the mtrr - * interfaces on this platform. - */ - if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, - vma->vm_end - vma->vm_start, - vma->vm_page_prot)) - return -EAGAIN; - - return 0; -} - resource_size_t pcibios_align_resource(void *data, const struct resource *res, resource_size_t size, resource_size_t align) diff --git a/arch/cris/include/asm/pci.h b/arch/cris/include/asm/pci.h index 65198cb46c85..6e505332b3e3 100644 --- a/arch/cris/include/asm/pci.h +++ b/arch/cris/include/asm/pci.h @@ -42,6 +42,7 @@ struct pci_dev; #define PCI_DMA_BUS_IS_PHYS (1) #define HAVE_PCI_MMAP +#define ARCH_GENERIC_PCI_MMAP_RESOURCE #endif /* __KERNEL__ */ -- cgit v1.2.3 From 35368f80403d32a993587c7f26cd3bcede3ed202 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:26:02 +0100 Subject: MIPS: PCI: Use generic pci_mmap_resource_range() Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas --- arch/mips/include/asm/pci.h | 2 +- arch/mips/pci/pci.c | 25 ------------------------- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/arch/mips/include/asm/pci.h b/arch/mips/include/asm/pci.h index 3141e2a8113a..1000c1b4c875 100644 --- a/arch/mips/include/asm/pci.h +++ b/arch/mips/include/asm/pci.h @@ -110,7 +110,7 @@ extern unsigned long PCIBIOS_MIN_MEM; extern void pcibios_set_master(struct pci_dev *dev); #define HAVE_PCI_MMAP - +#define ARCH_GENERIC_PCI_MMAP_RESOURCE #define HAVE_ARCH_PCI_RESOURCE_TO_USER /* diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c index f189502041a6..bd67ac74fe2d 100644 --- a/arch/mips/pci/pci.c +++ b/arch/mips/pci/pci.c @@ -57,28 +57,3 @@ void pci_resource_to_user(const struct pci_dev *dev, int bar, *start = fixup_bigphys_addr(rsrc->start, size); *end = rsrc->start + size; } - -int pci_mmap_page_range(struct pci_dev *dev, int bar, - struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine) -{ - unsigned long prot; - - /* - * I/O space can be accessed via normal processor loads and stores on - * this platform but for now we elect not to do this and portable - * drivers should not do this anyway. - */ - if (mmap_state == pci_mmap_io) - return -EINVAL; - - /* - * Ignore write-combine; for now only return uncached mappings. - */ - prot = pgprot_val(vma->vm_page_prot); - prot = (prot & ~_CACHE_MASK) | _CACHE_UNCACHED; - vma->vm_page_prot = __pgprot(prot); - - return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, - vma->vm_end - vma->vm_start, vma->vm_page_prot); -} -- cgit v1.2.3 From 903bdbe271217f656d13767d8a3e74b8aabc54c2 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:26:03 +0100 Subject: mn10300/PCI: Use generic pci_mmap_resource_range() This was setting vma->vm_flags |= VM_LOCKED. Not sure why... Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas Reviewed-by: David Howells --- arch/mn10300/include/asm/pci.h | 1 + arch/mn10300/unit-asb2305/pci-asb2305.c | 24 ------------------------ 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/arch/mn10300/include/asm/pci.h b/arch/mn10300/include/asm/pci.h index 082b6de90936..d27654902f28 100644 --- a/arch/mn10300/include/asm/pci.h +++ b/arch/mn10300/include/asm/pci.h @@ -74,6 +74,7 @@ static inline int pci_controller_num(struct pci_dev *dev) } #define HAVE_PCI_MMAP +#define ARCH_GENERIC_PCI_MMAP_RESOURCE #endif /* __KERNEL__ */ diff --git a/arch/mn10300/unit-asb2305/pci-asb2305.c b/arch/mn10300/unit-asb2305/pci-asb2305.c index 4abbbd54ac7d..e0f4617c0c7a 100644 --- a/arch/mn10300/unit-asb2305/pci-asb2305.c +++ b/arch/mn10300/unit-asb2305/pci-asb2305.c @@ -210,27 +210,3 @@ void __init pcibios_resource_survey(void) pcibios_allocate_resources(0); pcibios_allocate_resources(1); } - -int pci_mmap_page_range(struct pci_dev *dev, int bar, - struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine) -{ - unsigned long prot; - - /* Leave vm_pgoff as-is, the PCI space address is the physical - * address on this platform. - */ - vma->vm_flags |= VM_LOCKED; - - prot = pgprot_val(vma->vm_page_prot); - prot &= ~_PAGE_CACHE; - vma->vm_page_prot = __pgprot(prot); - - /* Write-combine setting is ignored */ - if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, - vma->vm_end - vma->vm_start, - vma->vm_page_prot)) - return -EAGAIN; - - return 0; -} -- cgit v1.2.3 From 6a94ca14c389b00515b026cd35bcd074dec6f7ff Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:26:04 +0100 Subject: parisc: Use generic pci_mmap_resource_range() Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas --- arch/parisc/include/asm/pci.h | 1 + arch/parisc/kernel/pci.c | 29 ----------------------------- 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/arch/parisc/include/asm/pci.h b/arch/parisc/include/asm/pci.h index bb9ea9003e08..1de1a3f412ec 100644 --- a/arch/parisc/include/asm/pci.h +++ b/arch/parisc/include/asm/pci.h @@ -200,5 +200,6 @@ static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) } #define HAVE_PCI_MMAP +#define ARCH_GENERIC_PCI_MMAP_RESOURCE #endif /* __ASM_PARISC_PCI_H */ diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c index 653877553acd..13ee3569959a 100644 --- a/arch/parisc/kernel/pci.c +++ b/arch/parisc/kernel/pci.c @@ -227,35 +227,6 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, return start; } - -int pci_mmap_page_range(struct pci_dev *dev, int bar, - struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine) -{ - unsigned long prot; - - /* - * I/O space can be accessed via normal processor loads and stores on - * this platform but for now we elect not to do this and portable - * drivers should not do this anyway. - */ - if (mmap_state == pci_mmap_io) - return -EINVAL; - - if (write_combine) - return -EINVAL; - - /* - * Ignore write-combine; for now only return uncached mappings. - */ - prot = pgprot_val(vma->vm_page_prot); - prot |= _PAGE_NO_CACHE; - vma->vm_page_prot = __pgprot(prot); - - return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, - vma->vm_end - vma->vm_start, vma->vm_page_prot); -} - /* * A driver is enabling the device. We make sure that all the appropriate * bits are set to allow the device to operate as the driver is expecting. -- cgit v1.2.3 From adf7bde63f049ab0c3284a67898055bb11614c15 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:26:05 +0100 Subject: sh/PCI: Use generic pci_mmap_resource_range() Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas --- arch/sh/drivers/pci/pci.c | 22 ---------------------- arch/sh/include/asm/pci.h | 1 + 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c index c8b36b7ceb98..c99ee286b69f 100644 --- a/arch/sh/drivers/pci/pci.c +++ b/arch/sh/drivers/pci/pci.c @@ -269,28 +269,6 @@ void __ref pcibios_report_status(unsigned int status_mask, int warn) } } -int pci_mmap_page_range(struct pci_dev *dev, int bar, - struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine) -{ - /* - * I/O space can be accessed via normal processor loads and stores on - * this platform but for now we elect not to do this and portable - * drivers should not do this anyway. - */ - if (mmap_state == pci_mmap_io) - return -EINVAL; - - /* - * Ignore write-combine; for now only return uncached mappings. - */ - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - - return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, - vma->vm_end - vma->vm_start, - vma->vm_page_prot); -} - #ifndef CONFIG_GENERIC_IOMAP void __iomem *__pci_ioport_map(struct pci_dev *dev, diff --git a/arch/sh/include/asm/pci.h b/arch/sh/include/asm/pci.h index 46abbc9983c9..17fa69bc814d 100644 --- a/arch/sh/include/asm/pci.h +++ b/arch/sh/include/asm/pci.h @@ -66,6 +66,7 @@ extern unsigned long PCIBIOS_MIN_IO, PCIBIOS_MIN_MEM; struct pci_dev; #define HAVE_PCI_MMAP +#define ARCH_GENERIC_PCI_MMAP_RESOURCE extern void pcibios_set_master(struct pci_dev *dev); -- cgit v1.2.3 From 69c701ec1f97315dc0f482699f370887448e150e Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:26:06 +0100 Subject: unicore32/PCI: Use generic pci_mmap_resource_range() Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas --- arch/unicore32/include/asm/pci.h | 1 + arch/unicore32/kernel/pci.c | 24 ------------------------ 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/arch/unicore32/include/asm/pci.h b/arch/unicore32/include/asm/pci.h index a51290862acd..ac5acdf4c4d0 100644 --- a/arch/unicore32/include/asm/pci.h +++ b/arch/unicore32/include/asm/pci.h @@ -17,6 +17,7 @@ #include /* for PCIBIOS_MIN_* */ #define HAVE_PCI_MMAP +#define ARCH_GENERIC_PCI_MMAP_RESOURCE #endif /* __KERNEL__ */ #endif diff --git a/arch/unicore32/kernel/pci.c b/arch/unicore32/kernel/pci.c index 1b438857e91f..1053bca1f8aa 100644 --- a/arch/unicore32/kernel/pci.c +++ b/arch/unicore32/kernel/pci.c @@ -356,27 +356,3 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) } return 0; } - -int pci_mmap_page_range(struct pci_dev *dev, int bar, - struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine) -{ - unsigned long phys; - - if (mmap_state == pci_mmap_io) - return -EINVAL; - - phys = vma->vm_pgoff; - - /* - * Mark this as IO - */ - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - - if (remap_pfn_range(vma, vma->vm_start, phys, - vma->vm_end - vma->vm_start, - vma->vm_page_prot)) - return -EAGAIN; - - return 0; -} -- cgit v1.2.3 From 5c2d5ce2ab4bd716b2125f15b2e0879143ac0563 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:26:07 +0100 Subject: x86/PCI: Use generic pci_mmap_resource_range() Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas --- arch/x86/include/asm/pci.h | 1 + arch/x86/pci/i386.c | 48 ---------------------------------------------- 2 files changed, 1 insertion(+), 48 deletions(-) diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index 734cc9411b3a..f513cc231151 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -104,6 +104,7 @@ int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq); #define HAVE_PCI_MMAP #define arch_can_pci_mmap_wc() pat_enabled() +#define ARCH_GENERIC_PCI_MMAP_RESOURCE #ifdef CONFIG_PCI extern void early_quirks(void); diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 8ca5e5d9f251..68499cf82ccf 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -406,51 +406,3 @@ void __init pcibios_resource_survey(void) */ ioapic_insert_resources(); } - -static const struct vm_operations_struct pci_mmap_ops = { - .access = generic_access_phys, -}; - -int pci_mmap_page_range(struct pci_dev *dev, int bar, - struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine) -{ - unsigned long prot; - - /* I/O space cannot be accessed via normal processor loads and - * stores on this platform. - */ - if (mmap_state == pci_mmap_io) - return -EINVAL; - - prot = pgprot_val(vma->vm_page_prot); - - /* - * Return error if pat is not enabled and write_combine is requested. - * Caller can followup with UC MINUS request and add a WC mtrr if there - * is a free mtrr slot. - */ - if (!pat_enabled() && write_combine) - return -EINVAL; - - if (pat_enabled() && write_combine) - prot |= cachemode2protval(_PAGE_CACHE_MODE_WC); - else if (pat_enabled() || boot_cpu_data.x86 > 3) - /* - * ioremap() and ioremap_nocache() defaults to UC MINUS for now. - * To avoid attribute conflicts, request UC MINUS here - * as well. - */ - prot |= cachemode2protval(_PAGE_CACHE_MODE_UC_MINUS); - - vma->vm_page_prot = __pgprot(prot); - - if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, - vma->vm_end - vma->vm_start, - vma->vm_page_prot)) - return -EAGAIN; - - vma->vm_ops = &pci_mmap_ops; - - return 0; -} -- cgit v1.2.3 From 2bea36fd1af440e1853e431459a0bf929266cd52 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 12 Apr 2017 13:26:08 +0100 Subject: PCI: Add I/O BAR support to generic pci_mmap_resource_range() This will need to call into an arch-provided pci_iobar_pfn() function. Signed-off-by: David Woodhouse Signed-off-by: Bjorn Helgaas --- drivers/pci/mmap.c | 12 ++++++++---- include/linux/pci.h | 4 ++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/pci/mmap.c b/drivers/pci/mmap.c index b7aca9c89c31..9a5e5a9055eb 100644 --- a/drivers/pci/mmap.c +++ b/drivers/pci/mmap.c @@ -48,9 +48,7 @@ int pci_mmap_resource_range(struct pci_dev *pdev, int bar, enum pci_mmap_state mmap_state, int write_combine) { unsigned long size; - - if (mmap_state == pci_mmap_io) - return -EINVAL; + int ret; size = ((pci_resource_len(pdev, bar) - 1) >> PAGE_SHIFT) + 1; if (vma->vm_pgoff + vma_pages(vma) > size) @@ -61,7 +59,13 @@ int pci_mmap_resource_range(struct pci_dev *pdev, int bar, else vma->vm_page_prot = pgprot_device(vma->vm_page_prot); - vma->vm_pgoff += (pci_resource_start(pdev, bar) >> PAGE_SHIFT); + 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, diff --git a/include/linux/pci.h b/include/linux/pci.h index 98a72abcf361..9f302444d4ac 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1648,8 +1648,12 @@ int pci_mmap_page_range(struct pci_dev *pdev, int bar, #ifndef arch_can_pci_mmap_wc #define arch_can_pci_mmap_wc() 0 #endif + #ifndef arch_can_pci_mmap_io #define arch_can_pci_mmap_io() 0 +#define pci_iobar_pfn(pdev, bar, vma) (-EINVAL) +#else +int pci_iobar_pfn(struct pci_dev *pdev, int bar, struct vm_area_struct *vma); #endif #ifndef pci_root_bus_fwnode -- cgit v1.2.3 From 61eee41ae135517392d05cbf1177523a02f12727 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 7 Apr 2017 11:22:42 +0200 Subject: ia64: Remove redundant valid_mmap_phys_addr_range() from pci_mmap_page_range() We know we are within a valid MMIO BAR by the time this function gets called; there's no need to check. Signed-off-by: David Woodhouse Tested-by: Tony Luck --- arch/ia64/pci/pci.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 053c688b15a5..27020f30caa6 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -439,9 +439,6 @@ pci_mmap_page_range (struct pci_dev *dev, int bar, */ return -EINVAL; - if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size)) - return -EINVAL; - prot = phys_mem_access_prot(NULL, vma->vm_pgoff, size, vma->vm_page_prot); -- cgit v1.2.3 From fcdb10d6b179fde41ca94d032afda8f4ed796b8e Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 7 Apr 2017 12:01:00 +0200 Subject: ia64: Remove redundant checks for WC in pci_mmap_page_range() For a PCI MMIO BAR, phys_mem_access_prot() should always return UC or WC. And while a mixture of cached and uncached mappings is forbidden, we were already mixing WC and UC, which is OK. Just do as we're asked. Signed-off-by: David Woodhouse Tested-by: Tony Luck --- arch/ia64/pci/pci.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 27020f30caa6..7438e8c84cdd 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -423,9 +423,6 @@ pci_mmap_page_range (struct pci_dev *dev, int bar, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine) { - unsigned long size = vma->vm_end - vma->vm_start; - pgprot_t prot; - /* * I/O space cannot be accessed via normal processor loads and * stores on this platform. @@ -439,21 +436,10 @@ pci_mmap_page_range (struct pci_dev *dev, int bar, */ return -EINVAL; - prot = phys_mem_access_prot(NULL, vma->vm_pgoff, size, - vma->vm_page_prot); - - /* - * If the user requested WC, the kernel uses UC or WC for this region, - * and the chipset supports WC, we can use WC. Otherwise, we have to - * use the same attribute the kernel uses. - */ - if (write_combine && - ((pgprot_val(prot) & _PAGE_MA_MASK) == _PAGE_MA_UC || - (pgprot_val(prot) & _PAGE_MA_MASK) == _PAGE_MA_WC) && - efi_range_is_wc(vma->vm_start, vma->vm_end - vma->vm_start)) + if (write_combine) vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); else - vma->vm_page_prot = prot; + vma->vm_page_prot = pgprot_device(vma->vm_page_prot); if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, vma->vm_end - vma->vm_start, vma->vm_page_prot)) -- cgit v1.2.3 From d9c102de2caa733c3e718e1b032cd154a9994326 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 7 Apr 2017 12:10:53 +0200 Subject: ia64: Use generic pci_mmap_resource_range() Now that we eliminated the different behaviour in separately-reviewable commits, we can switch IA64 to the generic implementation. Signed-off-by: David Woodhouse Tested-by: Tony Luck --- arch/ia64/include/asm/pci.h | 1 + arch/ia64/pci/pci.c | 30 ------------------------------ 2 files changed, 1 insertion(+), 30 deletions(-) diff --git a/arch/ia64/include/asm/pci.h b/arch/ia64/include/asm/pci.h index fc60b3d139f4..6459f2d46200 100644 --- a/arch/ia64/include/asm/pci.h +++ b/arch/ia64/include/asm/pci.h @@ -51,6 +51,7 @@ extern unsigned long ia64_max_iommu_merge_mask; #define PCI_DMA_BUS_IS_PHYS (ia64_max_iommu_merge_mask == ~0UL) #define HAVE_PCI_MMAP +#define ARCH_GENERIC_PCI_MMAP_RESOURCE #define arch_can_pci_mmap_wc() 1 #define HAVE_PCI_LEGACY diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 7438e8c84cdd..4068bde623dc 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -418,36 +418,6 @@ pcibios_align_resource (void *data, const struct resource *res, return res->start; } -int -pci_mmap_page_range (struct pci_dev *dev, int bar, - struct vm_area_struct *vma, - enum pci_mmap_state mmap_state, int write_combine) -{ - /* - * I/O space cannot be accessed via normal processor loads and - * stores on this platform. - */ - if (mmap_state == pci_mmap_io) - /* - * XXX we could relax this for I/O spaces for which ACPI - * indicates that the space is 1-to-1 mapped. But at the - * moment, we don't support multiple PCI address spaces and - * the legacy I/O space is not 1-to-1 mapped, so this is moot. - */ - 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 (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, - vma->vm_end - vma->vm_start, vma->vm_page_prot)) - return -EAGAIN; - - return 0; -} - /** * ia64_pci_get_legacy_mem - generic legacy mem routine * @bus: bus to get legacy memory base address for -- cgit v1.2.3