diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2017-04-28 11:34:34 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2017-04-28 11:34:34 -0400 |
commit | 889e4dd916a1f4dc7f9e6220fed26d811e39ca71 (patch) | |
tree | ec3905e25ae15a064a51ec74c00443a5474f223f | |
parent | 78f098383a7abb1eb9dfd45346773fe80a65f05a (diff) | |
parent | d9c102de2caa733c3e718e1b032cd154a9994326 (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
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 | ||
115 | In order to support PCI resource mapping as described above, Linux platform | 115 | In order to support PCI resource mapping as described above, Linux platform |
116 | code must define HAVE_PCI_MMAP and provide a pci_mmap_page_range function. | 116 | code should ideally define ARCH_GENERIC_PCI_MMAP_RESOURCE and use the generic |
117 | Platforms are free to only support subsets of the mmap functionality, but | 117 | implementation of that functionality. To support the historical interface of |
118 | useful return codes should be provided. | 118 | mmap() through files in /proc/bus/pci, platforms may also set HAVE_PCI_MMAP. |
119 | |||
120 | Alternatively, platforms which set HAVE_PCI_MMAP may provide their own | ||
121 | implementation of pci_mmap_page_range() instead of defining | ||
122 | ARCH_GENERIC_PCI_MMAP_RESOURCE. | ||
123 | |||
124 | Platforms which support write-combining maps of PCI resources must define | ||
125 | arch_can_pci_mmap_wc() which shall evaluate to non-zero at runtime when | ||
126 | write-combining is permitted. Platforms which support maps of I/O resources | ||
127 | define arch_can_pci_mmap_io() similarly. | ||
119 | 128 | ||
120 | Legacy resources are protected by the HAVE_PCI_LEGACY define. Platforms | 129 | Legacy resources are protected by the HAVE_PCI_LEGACY define. Platforms |
121 | wishing to support legacy functionality should define it and provide | 130 | wishing 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 |
32 | extern 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 | ||
35 | static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) | 34 | static 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 | ||
600 | int 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 | |||
619 | void __init pci_map_io_early(unsigned long pfn) | 600 | void __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 | |||
25 | extern int isa_dma_bridge_buggy; | 27 | extern 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 | ||
17 | int 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 | |||
39 | resource_size_t | 17 | resource_size_t |
40 | pcibios_align_resource(void *data, const struct resource *res, | 18 | pcibios_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 |
45 | extern 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 |
54 | extern 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 |
57 | extern int pci_mmap_legacy_page_range(struct pci_bus *bus, | 58 | extern 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 | ||
421 | int | ||
422 | pci_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); | |||
46 | extern int pci_proc_domain(struct pci_bus *bus); | 46 | extern int pci_proc_domain(struct pci_bus *bus); |
47 | 47 | ||
48 | struct vm_area_struct; | 48 | struct vm_area_struct; |
49 | /* Map a range of PCI memory or I/O space for a device into user space */ | ||
50 | int 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 | ||
56 | extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, | 54 | extern 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 | */ |
281 | int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, | 281 | int 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; | |||
110 | extern void pcibios_set_master(struct pci_dev *dev); | 110 | extern 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 | |
114 | extern 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 | |||
61 | int 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 |
77 | extern 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 | |||
214 | int 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 | |
204 | extern 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 | |||
231 | int 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); | |||
77 | extern int pci_proc_domain(struct pci_bus *bus); | 77 | extern int pci_proc_domain(struct pci_bus *bus); |
78 | 78 | ||
79 | struct vm_area_struct; | 79 | struct vm_area_struct; |
80 | /* Map a range of PCI memory or I/O space for a device into user space */ | ||
81 | int 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 | ||
87 | extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, | 86 | extern 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 | */ |
524 | int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, | 524 | int 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 | ||
272 | int 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 | ||
295 | void __iomem *__pci_ioport_map(struct pci_dev *dev, | 274 | 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 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; | |||
66 | struct pci_dev; | 66 | struct pci_dev; |
67 | 67 | ||
68 | #define HAVE_PCI_MMAP | 68 | #define HAVE_PCI_MMAP |
69 | extern 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 | |
71 | extern void pcibios_set_master(struct pci_dev *dev); | 71 | extern 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 | ||
48 | int 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 | |||
52 | static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) | 49 | static 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 | */ |
865 | int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, | 865 | int 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 |
20 | extern 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 | |||
360 | int 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 |
105 | extern 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 |
111 | extern void early_quirks(void); | 110 | extern 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 | |||
410 | static const struct vm_operations_struct pci_mmap_ops = { | ||
411 | .access = generic_access_phys, | ||
412 | }; | ||
413 | |||
414 | int 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 */ | ||
50 | int 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 | */ | ||
340 | static __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 | */ |
365 | int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, | 346 | int 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 | ||
5 | obj-y += access.o bus.o probe.o host-bridge.o remove.o pci.o \ | 5 | obj-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 |
8 | obj-$(CONFIG_PROC_FS) += proc.o | 8 | obj-$(CONFIG_PROC_FS) += proc.o |
9 | obj-$(CONFIG_SYSFS) += slot.o | 9 | obj-$(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 | ||
25 | int 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 | |||
40 | static const struct vm_operations_struct pci_phys_vm_ops = { | ||
41 | #ifdef CONFIG_HAVE_IOREMAP_PROT | ||
42 | .access = generic_access_phys, | ||
43 | #endif | ||
44 | }; | ||
45 | |||
46 | int 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 | |||
84 | int 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 | ||
985 | int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma, | 985 | int 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 | ||
1049 | static int pci_mmap_resource_uc(struct file *filp, struct kobject *kobj, | 1040 | static 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); | |||
21 | void pci_remove_firmware_label_files(struct pci_dev *pdev); | 21 | void pci_remove_firmware_label_files(struct pci_dev *pdev); |
22 | #endif | 22 | #endif |
23 | void pci_cleanup_rom(struct pci_dev *dev); | 23 | void pci_cleanup_rom(struct pci_dev *dev); |
24 | #ifdef HAVE_PCI_MMAP | 24 | |
25 | enum pci_mmap_api { | 25 | enum 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 | }; |
29 | int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai, | 29 | int 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 | |
32 | int pci_probe_reset_function(struct pci_dev *dev); | 32 | int 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 | */ | ||
1632 | int 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); | ||
1635 | int 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 | ||
1647 | int 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 |