aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2017-04-28 11:34:34 -0400
committerBjorn Helgaas <bhelgaas@google.com>2017-04-28 11:34:34 -0400
commit889e4dd916a1f4dc7f9e6220fed26d811e39ca71 (patch)
treeec3905e25ae15a064a51ec74c00443a5474f223f
parent78f098383a7abb1eb9dfd45346773fe80a65f05a (diff)
parentd9c102de2caa733c3e718e1b032cd154a9994326 (diff)
Merge branch 'pci/resource-mmap' into next
* pci/resource-mmap: ia64: Use generic pci_mmap_resource_range() ia64: Remove redundant checks for WC in pci_mmap_page_range() ia64: Remove redundant valid_mmap_phys_addr_range() from pci_mmap_page_range() PCI: Add I/O BAR support to generic pci_mmap_resource_range() x86/PCI: Use generic pci_mmap_resource_range() unicore32/PCI: Use generic pci_mmap_resource_range() sh/PCI: Use generic pci_mmap_resource_range() parisc: Use generic pci_mmap_resource_range() mn10300/PCI: Use generic pci_mmap_resource_range() MIPS: PCI: Use generic pci_mmap_resource_range() cris/PCI: Use generic pci_mmap_resource_range() ARM/PCI: Use generic pci_mmap_resource_range() PCI: Add pci_mmap_resource_range() and use it for ARM64 PCI: Add BAR index argument to pci_mmap_page_range() PCI: Use BAR index in sysfs attr->private instead of resource pointer PCI: Add arch_can_pci_mmap_io() on architectures which can mmap() I/O space PCI: Move multiple declarations of pci_mmap_page_range() to <linux/pci.h> PCI: Add arch_can_pci_mmap_wc() macro xtensa/PCI: Do not mmap PCI BARs to userspace as write-through PCI: Only allow WC mmap on prefetchable resources PCI: Fix another sanity check bug in /proc/pci mmap PCI: Fix pci_mmap_fits() for HAVE_PCI_RESOURCE_TO_USER platforms
-rw-r--r--Documentation/filesystems/sysfs-pci.txt15
-rw-r--r--arch/arm/include/asm/pci.h3
-rw-r--r--arch/arm/kernel/bios32.c19
-rw-r--r--arch/arm64/include/asm/pci.h2
-rw-r--r--arch/cris/arch-v32/drivers/pci/bios.c22
-rw-r--r--arch/cris/include/asm/pci.h4
-rw-r--r--arch/ia64/include/asm/pci.h5
-rw-r--r--arch/ia64/pci/pci.c46
-rw-r--r--arch/microblaze/include/asm/pci.h6
-rw-r--r--arch/microblaze/pci/pci-common.c2
-rw-r--r--arch/mips/include/asm/pci.h5
-rw-r--r--arch/mips/pci/pci.c24
-rw-r--r--arch/mn10300/include/asm/pci.h4
-rw-r--r--arch/mn10300/unit-asb2305/pci-asb2305.c23
-rw-r--r--arch/parisc/include/asm/pci.h4
-rw-r--r--arch/parisc/kernel/pci.c28
-rw-r--r--arch/powerpc/include/asm/pci.h9
-rw-r--r--arch/powerpc/kernel/pci-common.c3
-rw-r--r--arch/sh/drivers/pci/pci.c21
-rw-r--r--arch/sh/include/asm/pci.h4
-rw-r--r--arch/sparc/include/asm/pci_64.h5
-rw-r--r--arch/sparc/kernel/pci.c6
-rw-r--r--arch/unicore32/include/asm/pci.h3
-rw-r--r--arch/unicore32/kernel/pci.c23
-rw-r--r--arch/x86/include/asm/pci.h7
-rw-r--r--arch/x86/pci/i386.c47
-rw-r--r--arch/xtensa/include/asm/pci.h7
-rw-r--r--arch/xtensa/kernel/pci.c24
-rw-r--r--drivers/pci/Makefile2
-rw-r--r--drivers/pci/mmap.c99
-rw-r--r--drivers/pci/pci-sysfs.c76
-rw-r--r--drivers/pci/pci.h4
-rw-r--r--drivers/pci/proc.c41
-rw-r--r--include/linux/pci.h30
34 files changed, 238 insertions, 385 deletions
diff --git a/Documentation/filesystems/sysfs-pci.txt b/Documentation/filesystems/sysfs-pci.txt
index 6ea1ceda6f52..06f1d64c6f70 100644
--- a/Documentation/filesystems/sysfs-pci.txt
+++ b/Documentation/filesystems/sysfs-pci.txt
@@ -113,9 +113,18 @@ Supporting PCI access on new platforms
113-------------------------------------- 113--------------------------------------
114 114
115In order to support PCI resource mapping as described above, Linux platform 115In order to support PCI resource mapping as described above, Linux platform
116code must define HAVE_PCI_MMAP and provide a pci_mmap_page_range function. 116code should ideally define ARCH_GENERIC_PCI_MMAP_RESOURCE and use the generic
117Platforms are free to only support subsets of the mmap functionality, but 117implementation of that functionality. To support the historical interface of
118useful return codes should be provided. 118mmap() through files in /proc/bus/pci, platforms may also set HAVE_PCI_MMAP.
119
120Alternatively, platforms which set HAVE_PCI_MMAP may provide their own
121implementation of pci_mmap_page_range() instead of defining
122ARCH_GENERIC_PCI_MMAP_RESOURCE.
123
124Platforms which support write-combining maps of PCI resources must define
125arch_can_pci_mmap_wc() which shall evaluate to non-zero at runtime when
126write-combining is permitted. Platforms which support maps of I/O resources
127define arch_can_pci_mmap_io() similarly.
119 128
120Legacy resources are protected by the HAVE_PCI_LEGACY define. Platforms 129Legacy resources are protected by the HAVE_PCI_LEGACY define. Platforms
121wishing to support legacy functionality should define it and provide 130wishing to support legacy functionality should define it and provide
diff --git a/arch/arm/include/asm/pci.h b/arch/arm/include/asm/pci.h
index 057d381f4e57..396c92bcc0cf 100644
--- a/arch/arm/include/asm/pci.h
+++ b/arch/arm/include/asm/pci.h
@@ -29,8 +29,7 @@ static inline int pci_proc_domain(struct pci_bus *bus)
29#define PCI_DMA_BUS_IS_PHYS (1) 29#define PCI_DMA_BUS_IS_PHYS (1)
30 30
31#define HAVE_PCI_MMAP 31#define HAVE_PCI_MMAP
32extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, 32#define ARCH_GENERIC_PCI_MMAP_RESOURCE
33 enum pci_mmap_state mmap_state, int write_combine);
34 33
35static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) 34static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
36{ 35{
diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c
index 2f0e07735d1d..b259956365a0 100644
--- a/arch/arm/kernel/bios32.c
+++ b/arch/arm/kernel/bios32.c
@@ -597,25 +597,6 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res,
597 return start; 597 return start;
598} 598}
599 599
600int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
601 enum pci_mmap_state mmap_state, int write_combine)
602{
603 if (mmap_state == pci_mmap_io)
604 return -EINVAL;
605
606 /*
607 * Mark this as IO
608 */
609 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
610
611 if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
612 vma->vm_end - vma->vm_start,
613 vma->vm_page_prot))
614 return -EAGAIN;
615
616 return 0;
617}
618
619void __init pci_map_io_early(unsigned long pfn) 600void __init pci_map_io_early(unsigned long pfn)
620{ 601{
621 struct map_desc pci_io_desc = { 602 struct map_desc pci_io_desc = {
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 @@
22 */ 22 */
23#define PCI_DMA_BUS_IS_PHYS (0) 23#define PCI_DMA_BUS_IS_PHYS (0)
24 24
25#define ARCH_GENERIC_PCI_MMAP_RESOURCE 1
26
25extern int isa_dma_bridge_buggy; 27extern int isa_dma_bridge_buggy;
26 28
27#ifdef CONFIG_PCI 29#ifdef CONFIG_PCI
diff --git a/arch/cris/arch-v32/drivers/pci/bios.c b/arch/cris/arch-v32/drivers/pci/bios.c
index 212266a2c5d9..394c2a73d5e2 100644
--- a/arch/cris/arch-v32/drivers/pci/bios.c
+++ b/arch/cris/arch-v32/drivers/pci/bios.c
@@ -14,28 +14,6 @@ void pcibios_set_master(struct pci_dev *dev)
14 pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); 14 pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
15} 15}
16 16
17int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
18 enum pci_mmap_state mmap_state, int write_combine)
19{
20 unsigned long prot;
21
22 /* Leave vm_pgoff as-is, the PCI space address is the physical
23 * address on this platform.
24 */
25 prot = pgprot_val(vma->vm_page_prot);
26 vma->vm_page_prot = __pgprot(prot);
27
28 /* Write-combine setting is ignored, it is changed via the mtrr
29 * interfaces on this platform.
30 */
31 if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
32 vma->vm_end - vma->vm_start,
33 vma->vm_page_prot))
34 return -EAGAIN;
35
36 return 0;
37}
38
39resource_size_t 17resource_size_t
40pcibios_align_resource(void *data, const struct resource *res, 18pcibios_align_resource(void *data, const struct resource *res,
41 resource_size_t size, resource_size_t align) 19 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 b1b289df04c7..6e505332b3e3 100644
--- a/arch/cris/include/asm/pci.h
+++ b/arch/cris/include/asm/pci.h
@@ -42,9 +42,7 @@ struct pci_dev;
42#define PCI_DMA_BUS_IS_PHYS (1) 42#define PCI_DMA_BUS_IS_PHYS (1)
43 43
44#define HAVE_PCI_MMAP 44#define HAVE_PCI_MMAP
45extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, 45#define ARCH_GENERIC_PCI_MMAP_RESOURCE
46 enum pci_mmap_state mmap_state, int write_combine);
47
48 46
49#endif /* __KERNEL__ */ 47#endif /* __KERNEL__ */
50 48
diff --git a/arch/ia64/include/asm/pci.h b/arch/ia64/include/asm/pci.h
index c0835b0dc722..6459f2d46200 100644
--- a/arch/ia64/include/asm/pci.h
+++ b/arch/ia64/include/asm/pci.h
@@ -51,8 +51,9 @@ extern unsigned long ia64_max_iommu_merge_mask;
51#define PCI_DMA_BUS_IS_PHYS (ia64_max_iommu_merge_mask == ~0UL) 51#define PCI_DMA_BUS_IS_PHYS (ia64_max_iommu_merge_mask == ~0UL)
52 52
53#define HAVE_PCI_MMAP 53#define HAVE_PCI_MMAP
54extern int pci_mmap_page_range (struct pci_dev *dev, struct vm_area_struct *vma, 54#define ARCH_GENERIC_PCI_MMAP_RESOURCE
55 enum pci_mmap_state mmap_state, int write_combine); 55#define arch_can_pci_mmap_wc() 1
56
56#define HAVE_PCI_LEGACY 57#define HAVE_PCI_LEGACY
57extern int pci_mmap_legacy_page_range(struct pci_bus *bus, 58extern int pci_mmap_legacy_page_range(struct pci_bus *bus,
58 struct vm_area_struct *vma, 59 struct vm_area_struct *vma,
diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c
index 8f6ac2f8ae4c..4068bde623dc 100644
--- a/arch/ia64/pci/pci.c
+++ b/arch/ia64/pci/pci.c
@@ -418,52 +418,6 @@ pcibios_align_resource (void *data, const struct resource *res,
418 return res->start; 418 return res->start;
419} 419}
420 420
421int
422pci_mmap_page_range (struct pci_dev *dev, struct vm_area_struct *vma,
423 enum pci_mmap_state mmap_state, int write_combine)
424{
425 unsigned long size = vma->vm_end - vma->vm_start;
426 pgprot_t prot;
427
428 /*
429 * I/O space cannot be accessed via normal processor loads and
430 * stores on this platform.
431 */
432 if (mmap_state == pci_mmap_io)
433 /*
434 * XXX we could relax this for I/O spaces for which ACPI
435 * indicates that the space is 1-to-1 mapped. But at the
436 * moment, we don't support multiple PCI address spaces and
437 * the legacy I/O space is not 1-to-1 mapped, so this is moot.
438 */
439 return -EINVAL;
440
441 if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
442 return -EINVAL;
443
444 prot = phys_mem_access_prot(NULL, vma->vm_pgoff, size,
445 vma->vm_page_prot);
446
447 /*
448 * If the user requested WC, the kernel uses UC or WC for this region,
449 * and the chipset supports WC, we can use WC. Otherwise, we have to
450 * use the same attribute the kernel uses.
451 */
452 if (write_combine &&
453 ((pgprot_val(prot) & _PAGE_MA_MASK) == _PAGE_MA_UC ||
454 (pgprot_val(prot) & _PAGE_MA_MASK) == _PAGE_MA_WC) &&
455 efi_range_is_wc(vma->vm_start, vma->vm_end - vma->vm_start))
456 vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
457 else
458 vma->vm_page_prot = prot;
459
460 if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
461 vma->vm_end - vma->vm_start, vma->vm_page_prot))
462 return -EAGAIN;
463
464 return 0;
465}
466
467/** 421/**
468 * ia64_pci_get_legacy_mem - generic legacy mem routine 422 * ia64_pci_get_legacy_mem - generic legacy mem routine
469 * @bus: bus to get legacy memory base address for 423 * @bus: bus to get legacy memory base address for
diff --git a/arch/microblaze/include/asm/pci.h b/arch/microblaze/include/asm/pci.h
index 2a120bb70e54..efd4983cb697 100644
--- a/arch/microblaze/include/asm/pci.h
+++ b/arch/microblaze/include/asm/pci.h
@@ -46,12 +46,10 @@ extern int pci_domain_nr(struct pci_bus *bus);
46extern int pci_proc_domain(struct pci_bus *bus); 46extern int pci_proc_domain(struct pci_bus *bus);
47 47
48struct vm_area_struct; 48struct vm_area_struct;
49/* Map a range of PCI memory or I/O space for a device into user space */
50int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma,
51 enum pci_mmap_state mmap_state, int write_combine);
52 49
53/* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */ 50/* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */
54#define HAVE_PCI_MMAP 1 51#define HAVE_PCI_MMAP 1
52#define arch_can_pci_mmap_io() 1
55 53
56extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, 54extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val,
57 size_t count); 55 size_t count);
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,
278 * 278 *
279 * Returns a negative error code on failure, zero on success. 279 * Returns a negative error code on failure, zero on success.
280 */ 280 */
281int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, 281int pci_mmap_page_range(struct pci_dev *dev, int bar, struct vm_area_struct *vma,
282 enum pci_mmap_state mmap_state, int write_combine) 282 enum pci_mmap_state mmap_state, int write_combine)
283{ 283{
284 resource_size_t offset = 284 resource_size_t offset =
diff --git a/arch/mips/include/asm/pci.h b/arch/mips/include/asm/pci.h
index 30d1129d8624..1000c1b4c875 100644
--- a/arch/mips/include/asm/pci.h
+++ b/arch/mips/include/asm/pci.h
@@ -110,10 +110,7 @@ extern unsigned long PCIBIOS_MIN_MEM;
110extern void pcibios_set_master(struct pci_dev *dev); 110extern void pcibios_set_master(struct pci_dev *dev);
111 111
112#define HAVE_PCI_MMAP 112#define HAVE_PCI_MMAP
113 113#define ARCH_GENERIC_PCI_MMAP_RESOURCE
114extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
115 enum pci_mmap_state mmap_state, int write_combine);
116
117#define HAVE_ARCH_PCI_RESOURCE_TO_USER 114#define HAVE_ARCH_PCI_RESOURCE_TO_USER
118 115
119/* 116/*
diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c
index f6325fa657fb..bd67ac74fe2d 100644
--- a/arch/mips/pci/pci.c
+++ b/arch/mips/pci/pci.c
@@ -57,27 +57,3 @@ void pci_resource_to_user(const struct pci_dev *dev, int bar,
57 *start = fixup_bigphys_addr(rsrc->start, size); 57 *start = fixup_bigphys_addr(rsrc->start, size);
58 *end = rsrc->start + size; 58 *end = rsrc->start + size;
59} 59}
60
61int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
62 enum pci_mmap_state mmap_state, int write_combine)
63{
64 unsigned long prot;
65
66 /*
67 * I/O space can be accessed via normal processor loads and stores on
68 * this platform but for now we elect not to do this and portable
69 * drivers should not do this anyway.
70 */
71 if (mmap_state == pci_mmap_io)
72 return -EINVAL;
73
74 /*
75 * Ignore write-combine; for now only return uncached mappings.
76 */
77 prot = pgprot_val(vma->vm_page_prot);
78 prot = (prot & ~_CACHE_MASK) | _CACHE_UNCACHED;
79 vma->vm_page_prot = __pgprot(prot);
80
81 return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
82 vma->vm_end - vma->vm_start, vma->vm_page_prot);
83}
diff --git a/arch/mn10300/include/asm/pci.h b/arch/mn10300/include/asm/pci.h
index 51159fff025a..d27654902f28 100644
--- a/arch/mn10300/include/asm/pci.h
+++ b/arch/mn10300/include/asm/pci.h
@@ -74,9 +74,7 @@ static inline int pci_controller_num(struct pci_dev *dev)
74} 74}
75 75
76#define HAVE_PCI_MMAP 76#define HAVE_PCI_MMAP
77extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, 77#define ARCH_GENERIC_PCI_MMAP_RESOURCE
78 enum pci_mmap_state mmap_state,
79 int write_combine);
80 78
81#endif /* __KERNEL__ */ 79#endif /* __KERNEL__ */
82 80
diff --git a/arch/mn10300/unit-asb2305/pci-asb2305.c b/arch/mn10300/unit-asb2305/pci-asb2305.c
index b7ab8378964c..e0f4617c0c7a 100644
--- a/arch/mn10300/unit-asb2305/pci-asb2305.c
+++ b/arch/mn10300/unit-asb2305/pci-asb2305.c
@@ -210,26 +210,3 @@ void __init pcibios_resource_survey(void)
210 pcibios_allocate_resources(0); 210 pcibios_allocate_resources(0);
211 pcibios_allocate_resources(1); 211 pcibios_allocate_resources(1);
212} 212}
213
214int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
215 enum pci_mmap_state mmap_state, int write_combine)
216{
217 unsigned long prot;
218
219 /* Leave vm_pgoff as-is, the PCI space address is the physical
220 * address on this platform.
221 */
222 vma->vm_flags |= VM_LOCKED;
223
224 prot = pgprot_val(vma->vm_page_prot);
225 prot &= ~_PAGE_CACHE;
226 vma->vm_page_prot = __pgprot(prot);
227
228 /* Write-combine setting is ignored */
229 if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
230 vma->vm_end - vma->vm_start,
231 vma->vm_page_prot))
232 return -EAGAIN;
233
234 return 0;
235}
diff --git a/arch/parisc/include/asm/pci.h b/arch/parisc/include/asm/pci.h
index defebd956585..1de1a3f412ec 100644
--- a/arch/parisc/include/asm/pci.h
+++ b/arch/parisc/include/asm/pci.h
@@ -200,8 +200,6 @@ static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
200} 200}
201 201
202#define HAVE_PCI_MMAP 202#define HAVE_PCI_MMAP
203 203#define ARCH_GENERIC_PCI_MMAP_RESOURCE
204extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
205 enum pci_mmap_state mmap_state, int write_combine);
206 204
207#endif /* __ASM_PARISC_PCI_H */ 205#endif /* __ASM_PARISC_PCI_H */
diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c
index 0903c6abd7a4..13ee3569959a 100644
--- a/arch/parisc/kernel/pci.c
+++ b/arch/parisc/kernel/pci.c
@@ -227,34 +227,6 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res,
227 return start; 227 return start;
228} 228}
229 229
230
231int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
232 enum pci_mmap_state mmap_state, int write_combine)
233{
234 unsigned long prot;
235
236 /*
237 * I/O space can be accessed via normal processor loads and stores on
238 * this platform but for now we elect not to do this and portable
239 * drivers should not do this anyway.
240 */
241 if (mmap_state == pci_mmap_io)
242 return -EINVAL;
243
244 if (write_combine)
245 return -EINVAL;
246
247 /*
248 * Ignore write-combine; for now only return uncached mappings.
249 */
250 prot = pgprot_val(vma->vm_page_prot);
251 prot |= _PAGE_NO_CACHE;
252 vma->vm_page_prot = __pgprot(prot);
253
254 return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
255 vma->vm_end - vma->vm_start, vma->vm_page_prot);
256}
257
258/* 230/*
259 * A driver is enabling the device. We make sure that all the appropriate 231 * A driver is enabling the device. We make sure that all the appropriate
260 * bits are set to allow the device to operate as the driver is expecting. 232 * bits are set to allow the device to operate as the driver is expecting.
diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h
index 93eded8d3843..c8975dac535f 100644
--- a/arch/powerpc/include/asm/pci.h
+++ b/arch/powerpc/include/asm/pci.h
@@ -77,12 +77,11 @@ extern int pci_domain_nr(struct pci_bus *bus);
77extern int pci_proc_domain(struct pci_bus *bus); 77extern int pci_proc_domain(struct pci_bus *bus);
78 78
79struct vm_area_struct; 79struct vm_area_struct;
80/* Map a range of PCI memory or I/O space for a device into user space */
81int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma,
82 enum pci_mmap_state mmap_state, int write_combine);
83 80
84/* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */ 81/* Tell drivers/pci/proc.c that we have pci_mmap_page_range() and it does WC */
85#define HAVE_PCI_MMAP 1 82#define HAVE_PCI_MMAP 1
83#define arch_can_pci_mmap_io() 1
84#define arch_can_pci_mmap_wc() 1
86 85
87extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, 86extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val,
88 size_t count); 87 size_t count);
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index 03a7c9f58126..341a7469cab8 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -521,7 +521,8 @@ pgprot_t pci_phys_mem_access_prot(struct file *file,
521 * 521 *
522 * Returns a negative error code on failure, zero on success. 522 * Returns a negative error code on failure, zero on success.
523 */ 523 */
524int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, 524int pci_mmap_page_range(struct pci_dev *dev, int bar,
525 struct vm_area_struct *vma,
525 enum pci_mmap_state mmap_state, int write_combine) 526 enum pci_mmap_state mmap_state, int write_combine)
526{ 527{
527 resource_size_t offset = 528 resource_size_t offset =
diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c
index 84563e39a5b8..c99ee286b69f 100644
--- a/arch/sh/drivers/pci/pci.c
+++ b/arch/sh/drivers/pci/pci.c
@@ -269,27 +269,6 @@ void __ref pcibios_report_status(unsigned int status_mask, int warn)
269 } 269 }
270} 270}
271 271
272int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
273 enum pci_mmap_state mmap_state, int write_combine)
274{
275 /*
276 * I/O space can be accessed via normal processor loads and stores on
277 * this platform but for now we elect not to do this and portable
278 * drivers should not do this anyway.
279 */
280 if (mmap_state == pci_mmap_io)
281 return -EINVAL;
282
283 /*
284 * Ignore write-combine; for now only return uncached mappings.
285 */
286 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
287
288 return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
289 vma->vm_end - vma->vm_start,
290 vma->vm_page_prot);
291}
292
293#ifndef CONFIG_GENERIC_IOMAP 272#ifndef CONFIG_GENERIC_IOMAP
294 273
295void __iomem *__pci_ioport_map(struct pci_dev *dev, 274void __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 644314f2b1ef..17fa69bc814d 100644
--- a/arch/sh/include/asm/pci.h
+++ b/arch/sh/include/asm/pci.h
@@ -66,8 +66,8 @@ extern unsigned long PCIBIOS_MIN_IO, PCIBIOS_MIN_MEM;
66struct pci_dev; 66struct pci_dev;
67 67
68#define HAVE_PCI_MMAP 68#define HAVE_PCI_MMAP
69extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, 69#define ARCH_GENERIC_PCI_MMAP_RESOURCE
70 enum pci_mmap_state mmap_state, int write_combine); 70
71extern void pcibios_set_master(struct pci_dev *dev); 71extern void pcibios_set_master(struct pci_dev *dev);
72 72
73/* Dynamic DMA mapping stuff. 73/* Dynamic DMA mapping stuff.
diff --git a/arch/sparc/include/asm/pci_64.h b/arch/sparc/include/asm/pci_64.h
index 2303635158f5..b957ca5527a3 100644
--- a/arch/sparc/include/asm/pci_64.h
+++ b/arch/sparc/include/asm/pci_64.h
@@ -42,13 +42,10 @@ static inline int pci_proc_domain(struct pci_bus *bus)
42/* Platform support for /proc/bus/pci/X/Y mmap()s. */ 42/* Platform support for /proc/bus/pci/X/Y mmap()s. */
43 43
44#define HAVE_PCI_MMAP 44#define HAVE_PCI_MMAP
45#define arch_can_pci_mmap_io() 1
45#define HAVE_ARCH_PCI_GET_UNMAPPED_AREA 46#define HAVE_ARCH_PCI_GET_UNMAPPED_AREA
46#define get_pci_unmapped_area get_fb_unmapped_area 47#define get_pci_unmapped_area get_fb_unmapped_area
47 48
48int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
49 enum pci_mmap_state mmap_state,
50 int write_combine);
51
52static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) 49static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
53{ 50{
54 return PCI_IRQ_NONE; 51 return PCI_IRQ_NONE;
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
862 * 862 *
863 * Returns a negative error code on failure, zero on success. 863 * Returns a negative error code on failure, zero on success.
864 */ 864 */
865int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, 865int pci_mmap_page_range(struct pci_dev *dev, int bar,
866 enum pci_mmap_state mmap_state, 866 struct vm_area_struct *vma,
867 int write_combine) 867 enum pci_mmap_state mmap_state, int write_combine)
868{ 868{
869 int ret; 869 int ret;
870 870
diff --git a/arch/unicore32/include/asm/pci.h b/arch/unicore32/include/asm/pci.h
index 37e55d018de5..ac5acdf4c4d0 100644
--- a/arch/unicore32/include/asm/pci.h
+++ b/arch/unicore32/include/asm/pci.h
@@ -17,8 +17,7 @@
17#include <mach/hardware.h> /* for PCIBIOS_MIN_* */ 17#include <mach/hardware.h> /* for PCIBIOS_MIN_* */
18 18
19#define HAVE_PCI_MMAP 19#define HAVE_PCI_MMAP
20extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, 20#define ARCH_GENERIC_PCI_MMAP_RESOURCE
21 enum pci_mmap_state mmap_state, int write_combine);
22 21
23#endif /* __KERNEL__ */ 22#endif /* __KERNEL__ */
24#endif 23#endif
diff --git a/arch/unicore32/kernel/pci.c b/arch/unicore32/kernel/pci.c
index 62137d13c6f9..1053bca1f8aa 100644
--- a/arch/unicore32/kernel/pci.c
+++ b/arch/unicore32/kernel/pci.c
@@ -356,26 +356,3 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
356 } 356 }
357 return 0; 357 return 0;
358} 358}
359
360int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
361 enum pci_mmap_state mmap_state, int write_combine)
362{
363 unsigned long phys;
364
365 if (mmap_state == pci_mmap_io)
366 return -EINVAL;
367
368 phys = vma->vm_pgoff;
369
370 /*
371 * Mark this as IO
372 */
373 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
374
375 if (remap_pfn_range(vma, vma->vm_start, phys,
376 vma->vm_end - vma->vm_start,
377 vma->vm_page_prot))
378 return -EAGAIN;
379
380 return 0;
381}
diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h
index 1411dbed5e5e..f513cc231151 100644
--- a/arch/x86/include/asm/pci.h
+++ b/arch/x86/include/asm/pci.h
@@ -7,6 +7,7 @@
7#include <linux/string.h> 7#include <linux/string.h>
8#include <linux/scatterlist.h> 8#include <linux/scatterlist.h>
9#include <asm/io.h> 9#include <asm/io.h>
10#include <asm/pat.h>
10#include <asm/x86_init.h> 11#include <asm/x86_init.h>
11 12
12#ifdef __KERNEL__ 13#ifdef __KERNEL__
@@ -102,10 +103,8 @@ int pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq);
102 103
103 104
104#define HAVE_PCI_MMAP 105#define HAVE_PCI_MMAP
105extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, 106#define arch_can_pci_mmap_wc() pat_enabled()
106 enum pci_mmap_state mmap_state, 107#define ARCH_GENERIC_PCI_MMAP_RESOURCE
107 int write_combine);
108
109 108
110#ifdef CONFIG_PCI 109#ifdef CONFIG_PCI
111extern void early_quirks(void); 110extern void early_quirks(void);
diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c
index 0a9f2caf358f..68499cf82ccf 100644
--- a/arch/x86/pci/i386.c
+++ b/arch/x86/pci/i386.c
@@ -406,50 +406,3 @@ void __init pcibios_resource_survey(void)
406 */ 406 */
407 ioapic_insert_resources(); 407 ioapic_insert_resources();
408} 408}
409
410static const struct vm_operations_struct pci_mmap_ops = {
411 .access = generic_access_phys,
412};
413
414int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
415 enum pci_mmap_state mmap_state, int write_combine)
416{
417 unsigned long prot;
418
419 /* I/O space cannot be accessed via normal processor loads and
420 * stores on this platform.
421 */
422 if (mmap_state == pci_mmap_io)
423 return -EINVAL;
424
425 prot = pgprot_val(vma->vm_page_prot);
426
427 /*
428 * Return error if pat is not enabled and write_combine is requested.
429 * Caller can followup with UC MINUS request and add a WC mtrr if there
430 * is a free mtrr slot.
431 */
432 if (!pat_enabled() && write_combine)
433 return -EINVAL;
434
435 if (pat_enabled() && write_combine)
436 prot |= cachemode2protval(_PAGE_CACHE_MODE_WC);
437 else if (pat_enabled() || boot_cpu_data.x86 > 3)
438 /*
439 * ioremap() and ioremap_nocache() defaults to UC MINUS for now.
440 * To avoid attribute conflicts, request UC MINUS here
441 * as well.
442 */
443 prot |= cachemode2protval(_PAGE_CACHE_MODE_UC_MINUS);
444
445 vma->vm_page_prot = __pgprot(prot);
446
447 if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
448 vma->vm_end - vma->vm_start,
449 vma->vm_page_prot))
450 return -EAGAIN;
451
452 vma->vm_ops = &pci_mmap_ops;
453
454 return 0;
455}
diff --git a/arch/xtensa/include/asm/pci.h b/arch/xtensa/include/asm/pci.h
index 5d6bd932ba4e..e4f366a488d3 100644
--- a/arch/xtensa/include/asm/pci.h
+++ b/arch/xtensa/include/asm/pci.h
@@ -46,12 +46,9 @@ struct pci_dev;
46 46
47#define PCI_DMA_BUS_IS_PHYS (1) 47#define PCI_DMA_BUS_IS_PHYS (1)
48 48
49/* Map a range of PCI memory or I/O space for a device into user space */
50int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma,
51 enum pci_mmap_state mmap_state, int write_combine);
52
53/* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */ 49/* Tell drivers/pci/proc.c that we have pci_mmap_page_range() */
54#define HAVE_PCI_MMAP 1 50#define HAVE_PCI_MMAP 1
51#define arch_can_pci_mmap_io() 1
55 52
56#endif /* __KERNEL__ */ 53#endif /* __KERNEL__ */
57 54
diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c
index b848cc3dc913..903963ee495d 100644
--- a/arch/xtensa/kernel/pci.c
+++ b/arch/xtensa/kernel/pci.c
@@ -334,25 +334,6 @@ __pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vma,
334} 334}
335 335
336/* 336/*
337 * Set vm_page_prot of VMA, as appropriate for this architecture, for a pci
338 * device mapping.
339 */
340static __inline__ void
341__pci_mmap_set_pgprot(struct pci_dev *dev, struct vm_area_struct *vma,
342 enum pci_mmap_state mmap_state, int write_combine)
343{
344 int prot = pgprot_val(vma->vm_page_prot);
345
346 /* Set to write-through */
347 prot = (prot & _PAGE_CA_MASK) | _PAGE_CA_WT;
348#if 0
349 if (!write_combine)
350 prot |= _PAGE_WRITETHRU;
351#endif
352 vma->vm_page_prot = __pgprot(prot);
353}
354
355/*
356 * Perform the actual remap of the pages for a PCI device mapping, as 337 * Perform the actual remap of the pages for a PCI device mapping, as
357 * appropriate for this architecture. The region in the process to map 338 * appropriate for this architecture. The region in the process to map
358 * is described by vm_start and vm_end members of VMA, the base physical 339 * is described by vm_start and vm_end members of VMA, the base physical
@@ -362,7 +343,8 @@ __pci_mmap_set_pgprot(struct pci_dev *dev, struct vm_area_struct *vma,
362 * 343 *
363 * Returns a negative error code on failure, zero on success. 344 * Returns a negative error code on failure, zero on success.
364 */ 345 */
365int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, 346int pci_mmap_page_range(struct pci_dev *dev, int bar,
347 struct vm_area_struct *vma,
366 enum pci_mmap_state mmap_state, 348 enum pci_mmap_state mmap_state,
367 int write_combine) 349 int write_combine)
368{ 350{
@@ -372,7 +354,7 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
372 if (ret < 0) 354 if (ret < 0)
373 return ret; 355 return ret;
374 356
375 __pci_mmap_set_pgprot(dev, vma, mmap_state, write_combine); 357 vma->vm_page_prot = pgprot_device(vma->vm_page_prot);
376 358
377 ret = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, 359 ret = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
378 vma->vm_end - vma->vm_start,vma->vm_page_prot); 360 vma->vm_end - vma->vm_start,vma->vm_page_prot);
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 15b46dd5074b..462c1f5f5546 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -4,7 +4,7 @@
4 4
5obj-y += access.o bus.o probe.o host-bridge.o remove.o pci.o \ 5obj-y += access.o bus.o probe.o host-bridge.o remove.o pci.o \
6 pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \ 6 pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \
7 irq.o vpd.o setup-bus.o vc.o 7 irq.o vpd.o setup-bus.o vc.o mmap.o
8obj-$(CONFIG_PROC_FS) += proc.o 8obj-$(CONFIG_PROC_FS) += proc.o
9obj-$(CONFIG_SYSFS) += slot.o 9obj-$(CONFIG_SYSFS) += slot.o
10 10
diff --git a/drivers/pci/mmap.c b/drivers/pci/mmap.c
new file mode 100644
index 000000000000..9a5e5a9055eb
--- /dev/null
+++ b/drivers/pci/mmap.c
@@ -0,0 +1,99 @@
1/*
2 * mmap.c — generic PCI resource mmap helper
3 *
4 * Copyright © 2017 Amazon.com, Inc. or its affiliates.
5 *
6 * Author: David Woodhouse <dwmw2@infradead.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/kernel.h>
14#include <linux/mm.h>
15#include <linux/pci.h>
16
17#ifdef ARCH_GENERIC_PCI_MMAP_RESOURCE
18
19/*
20 * Modern setup: generic pci_mmap_resource_range(), and implement the legacy
21 * pci_mmap_page_range() (if needed) as a wrapper round it.
22 */
23
24#ifdef HAVE_PCI_MMAP
25int pci_mmap_page_range(struct pci_dev *pdev, int bar,
26 struct vm_area_struct *vma,
27 enum pci_mmap_state mmap_state, int write_combine)
28{
29 resource_size_t start, end;
30
31 pci_resource_to_user(pdev, bar, &pdev->resource[bar], &start, &end);
32
33 /* Adjust vm_pgoff to be the offset within the resource */
34 vma->vm_pgoff -= start >> PAGE_SHIFT;
35 return pci_mmap_resource_range(pdev, bar, vma, mmap_state,
36 write_combine);
37}
38#endif
39
40static const struct vm_operations_struct pci_phys_vm_ops = {
41#ifdef CONFIG_HAVE_IOREMAP_PROT
42 .access = generic_access_phys,
43#endif
44};
45
46int pci_mmap_resource_range(struct pci_dev *pdev, int bar,
47 struct vm_area_struct *vma,
48 enum pci_mmap_state mmap_state, int write_combine)
49{
50 unsigned long size;
51 int ret;
52
53 size = ((pci_resource_len(pdev, bar) - 1) >> PAGE_SHIFT) + 1;
54 if (vma->vm_pgoff + vma_pages(vma) > size)
55 return -EINVAL;
56
57 if (write_combine)
58 vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
59 else
60 vma->vm_page_prot = pgprot_device(vma->vm_page_prot);
61
62 if (mmap_state == pci_mmap_io) {
63 ret = pci_iobar_pfn(pdev, bar, vma);
64 if (ret)
65 return ret;
66 } else
67 vma->vm_pgoff += (pci_resource_start(pdev, bar) >> PAGE_SHIFT);
68
69 vma->vm_ops = &pci_phys_vm_ops;
70
71 return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
72 vma->vm_end - vma->vm_start,
73 vma->vm_page_prot);
74}
75
76#elif defined(HAVE_PCI_MMAP) /* && !ARCH_GENERIC_PCI_MMAP_RESOURCE */
77
78/*
79 * Legacy setup: Impement pci_mmap_resource_range() as a wrapper around
80 * the architecture's pci_mmap_page_range(), converting to "user visible"
81 * addresses as necessary.
82 */
83
84int pci_mmap_resource_range(struct pci_dev *pdev, int bar,
85 struct vm_area_struct *vma,
86 enum pci_mmap_state mmap_state, int write_combine)
87{
88 resource_size_t start, end;
89
90 /*
91 * pci_mmap_page_range() expects the same kind of entry as coming
92 * from /proc/bus/pci/ which is a "user visible" value. If this is
93 * different from the resource itself, arch will do necessary fixup.
94 */
95 pci_resource_to_user(pdev, bar, &pdev->resource[bar], &start, &end);
96 vma->vm_pgoff += start >> PAGE_SHIFT;
97 return pci_mmap_page_range(pdev, bar, vma, mmap_state, write_combine);
98}
99#endif
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 25d010d449a3..10feb98a2b1d 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -980,20 +980,24 @@ void pci_remove_legacy_files(struct pci_bus *b)
980} 980}
981#endif /* HAVE_PCI_LEGACY */ 981#endif /* HAVE_PCI_LEGACY */
982 982
983#ifdef HAVE_PCI_MMAP 983#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)
984 984
985int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma, 985int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma,
986 enum pci_mmap_api mmap_api) 986 enum pci_mmap_api mmap_api)
987{ 987{
988 unsigned long nr, start, size, pci_start; 988 unsigned long nr, start, size;
989 resource_size_t pci_start = 0, pci_end;
989 990
990 if (pci_resource_len(pdev, resno) == 0) 991 if (pci_resource_len(pdev, resno) == 0)
991 return 0; 992 return 0;
992 nr = vma_pages(vma); 993 nr = vma_pages(vma);
993 start = vma->vm_pgoff; 994 start = vma->vm_pgoff;
994 size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1; 995 size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1;
995 pci_start = (mmap_api == PCI_MMAP_PROCFS) ? 996 if (mmap_api == PCI_MMAP_PROCFS) {
996 pci_resource_start(pdev, resno) >> PAGE_SHIFT : 0; 997 pci_resource_to_user(pdev, resno, &pdev->resource[resno],
998 &pci_start, &pci_end);
999 pci_start >>= PAGE_SHIFT;
1000 }
997 if (start >= pci_start && start < pci_start + size && 1001 if (start >= pci_start && start < pci_start + size &&
998 start + nr <= pci_start + size) 1002 start + nr <= pci_start + size)
999 return 1; 1003 return 1;
@@ -1013,37 +1017,24 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
1013 struct vm_area_struct *vma, int write_combine) 1017 struct vm_area_struct *vma, int write_combine)
1014{ 1018{
1015 struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); 1019 struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
1016 struct resource *res = attr->private; 1020 int bar = (unsigned long)attr->private;
1017 enum pci_mmap_state mmap_type; 1021 enum pci_mmap_state mmap_type;
1018 resource_size_t start, end; 1022 struct resource *res = &pdev->resource[bar];
1019 int i;
1020
1021 for (i = 0; i < PCI_ROM_RESOURCE; i++)
1022 if (res == &pdev->resource[i])
1023 break;
1024 if (i >= PCI_ROM_RESOURCE)
1025 return -ENODEV;
1026 1023
1027 if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start)) 1024 if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start))
1028 return -EINVAL; 1025 return -EINVAL;
1029 1026
1030 if (!pci_mmap_fits(pdev, i, vma, PCI_MMAP_SYSFS)) { 1027 if (!pci_mmap_fits(pdev, bar, vma, PCI_MMAP_SYSFS)) {
1031 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", 1028 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",
1032 current->comm, vma->vm_end-vma->vm_start, vma->vm_pgoff, 1029 current->comm, vma->vm_end-vma->vm_start, vma->vm_pgoff,
1033 pci_name(pdev), i, 1030 pci_name(pdev), bar,
1034 (u64)pci_resource_start(pdev, i), 1031 (u64)pci_resource_start(pdev, bar),
1035 (u64)pci_resource_len(pdev, i)); 1032 (u64)pci_resource_len(pdev, bar));
1036 return -EINVAL; 1033 return -EINVAL;
1037 } 1034 }
1038
1039 /* pci_mmap_page_range() expects the same kind of entry as coming
1040 * from /proc/bus/pci/ which is a "user visible" value. If this is
1041 * different from the resource itself, arch will do necessary fixup.
1042 */
1043 pci_resource_to_user(pdev, i, res, &start, &end);
1044 vma->vm_pgoff += start >> PAGE_SHIFT;
1045 mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; 1035 mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
1046 return pci_mmap_page_range(pdev, vma, mmap_type, write_combine); 1036
1037 return pci_mmap_resource_range(pdev, bar, vma, mmap_type, write_combine);
1047} 1038}
1048 1039
1049static int pci_mmap_resource_uc(struct file *filp, struct kobject *kobj, 1040static int pci_mmap_resource_uc(struct file *filp, struct kobject *kobj,
@@ -1065,22 +1056,18 @@ static ssize_t pci_resource_io(struct file *filp, struct kobject *kobj,
1065 loff_t off, size_t count, bool write) 1056 loff_t off, size_t count, bool write)
1066{ 1057{
1067 struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); 1058 struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
1068 struct resource *res = attr->private; 1059 int bar = (unsigned long)attr->private;
1060 struct resource *res;
1069 unsigned long port = off; 1061 unsigned long port = off;
1070 int i;
1071 1062
1072 for (i = 0; i < PCI_ROM_RESOURCE; i++) 1063 res = &pdev->resource[bar];
1073 if (res == &pdev->resource[i])
1074 break;
1075 if (i >= PCI_ROM_RESOURCE)
1076 return -ENODEV;
1077 1064
1078 port += pci_resource_start(pdev, i); 1065 port += pci_resource_start(pdev, bar);
1079 1066
1080 if (port > pci_resource_end(pdev, i)) 1067 if (port > pci_resource_end(pdev, bar))
1081 return 0; 1068 return 0;
1082 1069
1083 if (port + count - 1 > pci_resource_end(pdev, i)) 1070 if (port + count - 1 > pci_resource_end(pdev, bar))
1084 return -EINVAL; 1071 return -EINVAL;
1085 1072
1086 switch (count) { 1073 switch (count) {
@@ -1170,16 +1157,19 @@ static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine)
1170 } else { 1157 } else {
1171 pdev->res_attr[num] = res_attr; 1158 pdev->res_attr[num] = res_attr;
1172 sprintf(res_attr_name, "resource%d", num); 1159 sprintf(res_attr_name, "resource%d", num);
1173 res_attr->mmap = pci_mmap_resource_uc; 1160 if (pci_resource_flags(pdev, num) & IORESOURCE_IO) {
1174 } 1161 res_attr->read = pci_read_resource_io;
1175 if (pci_resource_flags(pdev, num) & IORESOURCE_IO) { 1162 res_attr->write = pci_write_resource_io;
1176 res_attr->read = pci_read_resource_io; 1163 if (arch_can_pci_mmap_io())
1177 res_attr->write = pci_write_resource_io; 1164 res_attr->mmap = pci_mmap_resource_uc;
1165 } else {
1166 res_attr->mmap = pci_mmap_resource_uc;
1167 }
1178 } 1168 }
1179 res_attr->attr.name = res_attr_name; 1169 res_attr->attr.name = res_attr_name;
1180 res_attr->attr.mode = S_IRUSR | S_IWUSR; 1170 res_attr->attr.mode = S_IRUSR | S_IWUSR;
1181 res_attr->size = pci_resource_len(pdev, num); 1171 res_attr->size = pci_resource_len(pdev, num);
1182 res_attr->private = &pdev->resource[num]; 1172 res_attr->private = (void *)(unsigned long)num;
1183 retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr); 1173 retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
1184 if (retval) 1174 if (retval)
1185 kfree(res_attr); 1175 kfree(res_attr);
@@ -1207,9 +1197,9 @@ static int pci_create_resource_files(struct pci_dev *pdev)
1207 1197
1208 retval = pci_create_attr(pdev, i, 0); 1198 retval = pci_create_attr(pdev, i, 0);
1209 /* for prefetchable resources, create a WC mappable file */ 1199 /* for prefetchable resources, create a WC mappable file */
1210 if (!retval && pdev->resource[i].flags & IORESOURCE_PREFETCH) 1200 if (!retval && arch_can_pci_mmap_wc() &&
1201 pdev->resource[i].flags & IORESOURCE_PREFETCH)
1211 retval = pci_create_attr(pdev, i, 1); 1202 retval = pci_create_attr(pdev, i, 1);
1212
1213 if (retval) { 1203 if (retval) {
1214 pci_remove_resource_files(pdev); 1204 pci_remove_resource_files(pdev);
1215 return retval; 1205 return retval;
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 245719c3e409..71fa82359b5b 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);
21void pci_remove_firmware_label_files(struct pci_dev *pdev); 21void pci_remove_firmware_label_files(struct pci_dev *pdev);
22#endif 22#endif
23void pci_cleanup_rom(struct pci_dev *dev); 23void pci_cleanup_rom(struct pci_dev *dev);
24#ifdef HAVE_PCI_MMAP 24
25enum pci_mmap_api { 25enum pci_mmap_api {
26 PCI_MMAP_SYSFS, /* mmap on /sys/bus/pci/devices/<BDF>/resource<N> */ 26 PCI_MMAP_SYSFS, /* mmap on /sys/bus/pci/devices/<BDF>/resource<N> */
27 PCI_MMAP_PROCFS /* mmap on /proc/bus/pci/<BDF> */ 27 PCI_MMAP_PROCFS /* mmap on /proc/bus/pci/<BDF> */
28}; 28};
29int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai, 29int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai,
30 enum pci_mmap_api mmap_api); 30 enum pci_mmap_api mmap_api);
31#endif 31
32int pci_probe_reset_function(struct pci_dev *dev); 32int pci_probe_reset_function(struct pci_dev *dev);
33 33
34/** 34/**
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index f82710a8694d..098360d7ff81 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,
202 202
203#ifdef HAVE_PCI_MMAP 203#ifdef HAVE_PCI_MMAP
204 case PCIIOC_MMAP_IS_IO: 204 case PCIIOC_MMAP_IS_IO:
205 if (!arch_can_pci_mmap_io())
206 return -EINVAL;
205 fpriv->mmap_state = pci_mmap_io; 207 fpriv->mmap_state = pci_mmap_io;
206 break; 208 break;
207 209
@@ -210,14 +212,15 @@ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd,
210 break; 212 break;
211 213
212 case PCIIOC_WRITE_COMBINE: 214 case PCIIOC_WRITE_COMBINE:
213 if (arg) 215 if (arch_can_pci_mmap_wc()) {
214 fpriv->write_combine = 1; 216 if (arg)
215 else 217 fpriv->write_combine = 1;
216 fpriv->write_combine = 0; 218 else
217 break; 219 fpriv->write_combine = 0;
218 220 break;
221 }
222 /* If arch decided it can't, fall through... */
219#endif /* HAVE_PCI_MMAP */ 223#endif /* HAVE_PCI_MMAP */
220
221 default: 224 default:
222 ret = -EINVAL; 225 ret = -EINVAL;
223 break; 226 break;
@@ -231,25 +234,35 @@ static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma)
231{ 234{
232 struct pci_dev *dev = PDE_DATA(file_inode(file)); 235 struct pci_dev *dev = PDE_DATA(file_inode(file));
233 struct pci_filp_private *fpriv = file->private_data; 236 struct pci_filp_private *fpriv = file->private_data;
234 int i, ret, write_combine; 237 int i, ret, write_combine = 0, res_bit = IORESOURCE_MEM;
235 238
236 if (!capable(CAP_SYS_RAWIO)) 239 if (!capable(CAP_SYS_RAWIO))
237 return -EPERM; 240 return -EPERM;
238 241
242 if (fpriv->mmap_state == pci_mmap_io) {
243 if (!arch_can_pci_mmap_io())
244 return -EINVAL;
245 res_bit = IORESOURCE_IO;
246 }
247
239 /* Make sure the caller is mapping a real resource for this device */ 248 /* Make sure the caller is mapping a real resource for this device */
240 for (i = 0; i < PCI_ROM_RESOURCE; i++) { 249 for (i = 0; i < PCI_ROM_RESOURCE; i++) {
241 if (pci_mmap_fits(dev, i, vma, PCI_MMAP_PROCFS)) 250 if (dev->resource[i].flags & res_bit &&
251 pci_mmap_fits(dev, i, vma, PCI_MMAP_PROCFS))
242 break; 252 break;
243 } 253 }
244 254
245 if (i >= PCI_ROM_RESOURCE) 255 if (i >= PCI_ROM_RESOURCE)
246 return -ENODEV; 256 return -ENODEV;
247 257
248 if (fpriv->mmap_state == pci_mmap_mem) 258 if (fpriv->mmap_state == pci_mmap_mem &&
249 write_combine = fpriv->write_combine; 259 fpriv->write_combine) {
250 else 260 if (dev->resource[i].flags & IORESOURCE_PREFETCH)
251 write_combine = 0; 261 write_combine = 1;
252 ret = pci_mmap_page_range(dev, vma, 262 else
263 return -EINVAL;
264 }
265 ret = pci_mmap_page_range(dev, i, vma,
253 fpriv->mmap_state, write_combine); 266 fpriv->mmap_state, write_combine);
254 if (ret < 0) 267 if (ret < 0)
255 return ret; 268 return ret;
diff --git a/include/linux/pci.h b/include/linux/pci.h
index d8ffed9cb1a5..bbd17d49c947 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1617,6 +1617,36 @@ static inline int pci_get_new_domain_nr(void) { return -ENOSYS; }
1617 1617
1618#include <asm/pci.h> 1618#include <asm/pci.h>
1619 1619
1620/* These two functions provide almost identical functionality. Depennding
1621 * on the architecture, one will be implemented as a wrapper around the
1622 * other (in drivers/pci/mmap.c).
1623 *
1624 * pci_mmap_resource_range() maps a specific BAR, and vm->vm_pgoff
1625 * is expected to be an offset within that region.
1626 *
1627 * pci_mmap_page_range() is the legacy architecture-specific interface,
1628 * which accepts a "user visible" resource address converted by
1629 * pci_resource_to_user(), as used in the legacy mmap() interface in
1630 * /proc/bus/pci/.
1631 */
1632int pci_mmap_resource_range(struct pci_dev *dev, int bar,
1633 struct vm_area_struct *vma,
1634 enum pci_mmap_state mmap_state, int write_combine);
1635int pci_mmap_page_range(struct pci_dev *pdev, int bar,
1636 struct vm_area_struct *vma,
1637 enum pci_mmap_state mmap_state, int write_combine);
1638
1639#ifndef arch_can_pci_mmap_wc
1640#define arch_can_pci_mmap_wc() 0
1641#endif
1642
1643#ifndef arch_can_pci_mmap_io
1644#define arch_can_pci_mmap_io() 0
1645#define pci_iobar_pfn(pdev, bar, vma) (-EINVAL)
1646#else
1647int pci_iobar_pfn(struct pci_dev *pdev, int bar, struct vm_area_struct *vma);
1648#endif
1649
1620#ifndef pci_root_bus_fwnode 1650#ifndef pci_root_bus_fwnode
1621#define pci_root_bus_fwnode(bus) NULL 1651#define pci_root_bus_fwnode(bus) NULL
1622#endif 1652#endif