diff options
-rw-r--r-- | include/linux/mm.h | 2 | ||||
-rw-r--r-- | mm/memory.c | 31 |
2 files changed, 16 insertions, 17 deletions
diff --git a/include/linux/mm.h b/include/linux/mm.h index 35f811b0cd69..2f6e2f886d4b 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h | |||
@@ -804,6 +804,8 @@ int copy_page_range(struct mm_struct *dst, struct mm_struct *src, | |||
804 | struct vm_area_struct *vma); | 804 | struct vm_area_struct *vma); |
805 | void unmap_mapping_range(struct address_space *mapping, | 805 | void unmap_mapping_range(struct address_space *mapping, |
806 | loff_t const holebegin, loff_t const holelen, int even_cows); | 806 | loff_t const holebegin, loff_t const holelen, int even_cows); |
807 | int follow_phys(struct vm_area_struct *vma, unsigned long address, | ||
808 | unsigned int flags, unsigned long *prot, resource_size_t *phys); | ||
807 | int generic_access_phys(struct vm_area_struct *vma, unsigned long addr, | 809 | int generic_access_phys(struct vm_area_struct *vma, unsigned long addr, |
808 | void *buf, int len, int write); | 810 | void *buf, int len, int write); |
809 | 811 | ||
diff --git a/mm/memory.c b/mm/memory.c index 1e8f0d347c0e..79f28e35d4fc 100644 --- a/mm/memory.c +++ b/mm/memory.c | |||
@@ -2981,9 +2981,9 @@ int in_gate_area_no_task(unsigned long addr) | |||
2981 | #endif /* __HAVE_ARCH_GATE_AREA */ | 2981 | #endif /* __HAVE_ARCH_GATE_AREA */ |
2982 | 2982 | ||
2983 | #ifdef CONFIG_HAVE_IOREMAP_PROT | 2983 | #ifdef CONFIG_HAVE_IOREMAP_PROT |
2984 | static resource_size_t follow_phys(struct vm_area_struct *vma, | 2984 | int follow_phys(struct vm_area_struct *vma, |
2985 | unsigned long address, unsigned int flags, | 2985 | unsigned long address, unsigned int flags, |
2986 | unsigned long *prot) | 2986 | unsigned long *prot, resource_size_t *phys) |
2987 | { | 2987 | { |
2988 | pgd_t *pgd; | 2988 | pgd_t *pgd; |
2989 | pud_t *pud; | 2989 | pud_t *pud; |
@@ -2992,24 +2992,26 @@ static resource_size_t follow_phys(struct vm_area_struct *vma, | |||
2992 | spinlock_t *ptl; | 2992 | spinlock_t *ptl; |
2993 | resource_size_t phys_addr = 0; | 2993 | resource_size_t phys_addr = 0; |
2994 | struct mm_struct *mm = vma->vm_mm; | 2994 | struct mm_struct *mm = vma->vm_mm; |
2995 | int ret = -EINVAL; | ||
2995 | 2996 | ||
2996 | VM_BUG_ON(!(vma->vm_flags & (VM_IO | VM_PFNMAP))); | 2997 | if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) |
2998 | goto out; | ||
2997 | 2999 | ||
2998 | pgd = pgd_offset(mm, address); | 3000 | pgd = pgd_offset(mm, address); |
2999 | if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) | 3001 | if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) |
3000 | goto no_page_table; | 3002 | goto out; |
3001 | 3003 | ||
3002 | pud = pud_offset(pgd, address); | 3004 | pud = pud_offset(pgd, address); |
3003 | if (pud_none(*pud) || unlikely(pud_bad(*pud))) | 3005 | if (pud_none(*pud) || unlikely(pud_bad(*pud))) |
3004 | goto no_page_table; | 3006 | goto out; |
3005 | 3007 | ||
3006 | pmd = pmd_offset(pud, address); | 3008 | pmd = pmd_offset(pud, address); |
3007 | if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) | 3009 | if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) |
3008 | goto no_page_table; | 3010 | goto out; |
3009 | 3011 | ||
3010 | /* We cannot handle huge page PFN maps. Luckily they don't exist. */ | 3012 | /* We cannot handle huge page PFN maps. Luckily they don't exist. */ |
3011 | if (pmd_huge(*pmd)) | 3013 | if (pmd_huge(*pmd)) |
3012 | goto no_page_table; | 3014 | goto out; |
3013 | 3015 | ||
3014 | ptep = pte_offset_map_lock(mm, pmd, address, &ptl); | 3016 | ptep = pte_offset_map_lock(mm, pmd, address, &ptl); |
3015 | if (!ptep) | 3017 | if (!ptep) |
@@ -3024,13 +3026,13 @@ static resource_size_t follow_phys(struct vm_area_struct *vma, | |||
3024 | phys_addr <<= PAGE_SHIFT; /* Shift here to avoid overflow on PAE */ | 3026 | phys_addr <<= PAGE_SHIFT; /* Shift here to avoid overflow on PAE */ |
3025 | 3027 | ||
3026 | *prot = pgprot_val(pte_pgprot(pte)); | 3028 | *prot = pgprot_val(pte_pgprot(pte)); |
3029 | *phys = phys_addr; | ||
3030 | ret = 0; | ||
3027 | 3031 | ||
3028 | unlock: | 3032 | unlock: |
3029 | pte_unmap_unlock(ptep, ptl); | 3033 | pte_unmap_unlock(ptep, ptl); |
3030 | out: | 3034 | out: |
3031 | return phys_addr; | 3035 | return ret; |
3032 | no_page_table: | ||
3033 | return 0; | ||
3034 | } | 3036 | } |
3035 | 3037 | ||
3036 | int generic_access_phys(struct vm_area_struct *vma, unsigned long addr, | 3038 | int generic_access_phys(struct vm_area_struct *vma, unsigned long addr, |
@@ -3041,12 +3043,7 @@ int generic_access_phys(struct vm_area_struct *vma, unsigned long addr, | |||
3041 | void *maddr; | 3043 | void *maddr; |
3042 | int offset = addr & (PAGE_SIZE-1); | 3044 | int offset = addr & (PAGE_SIZE-1); |
3043 | 3045 | ||
3044 | if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) | 3046 | if (follow_phys(vma, addr, write, &prot, &phys_addr)) |
3045 | return -EINVAL; | ||
3046 | |||
3047 | phys_addr = follow_phys(vma, addr, write, &prot); | ||
3048 | |||
3049 | if (!phys_addr) | ||
3050 | return -EINVAL; | 3047 | return -EINVAL; |
3051 | 3048 | ||
3052 | maddr = ioremap_prot(phys_addr, PAGE_SIZE, prot); | 3049 | maddr = ioremap_prot(phys_addr, PAGE_SIZE, prot); |