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 100