diff options
| author | Will Deacon <will.deacon@arm.com> | 2010-09-13 11:19:41 -0400 |
|---|---|---|
| committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2010-10-04 15:57:10 -0400 |
| commit | c4e259c859538e94007d1f04a488540375189551 (patch) | |
| tree | df5485bd1184c562a11a8588915539d136e8de29 | |
| parent | 8925ec4c530094b878e7e28a1fd78e7122afd973 (diff) | |
ARM: 6386/1: flush_ptrace_access: invalidate correct I-cache alias
copy_to_user_page can be used by access_process_vm to write to an
executable page of a process using a mapping acquired by kmap.
For systems with I-cache aliasing, flushing the I-cache using the
Kernel mapping may leave stale data in the I-cache if the user
mapping is of a different colour.
This patch introduces a flush_icache_alias function to flush.c,
which calls flush_icache_range with a mapping of the specified
colour. flush_ptrace_access is then modified to call this new
function instead of coherent_kern_range in the case of an aliasing
I-cache and a non-aliasing D-cache.
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
| -rw-r--r-- | arch/arm/mm/flush.c | 23 |
1 files changed, 20 insertions, 3 deletions
diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index 2332b774c6b9..391ffae75098 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c | |||
| @@ -40,6 +40,18 @@ static void flush_pfn_alias(unsigned long pfn, unsigned long vaddr) | |||
| 40 | : "cc"); | 40 | : "cc"); |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | static void flush_icache_alias(unsigned long pfn, unsigned long vaddr, unsigned long len) | ||
| 44 | { | ||
| 45 | unsigned long colour = CACHE_COLOUR(vaddr); | ||
| 46 | unsigned long offset = vaddr & (PAGE_SIZE - 1); | ||
| 47 | unsigned long to; | ||
| 48 | |||
| 49 | set_pte_ext(TOP_PTE(ALIAS_FLUSH_START) + colour, pfn_pte(pfn, PAGE_KERNEL), 0); | ||
| 50 | to = ALIAS_FLUSH_START + (colour << PAGE_SHIFT) + offset; | ||
| 51 | flush_tlb_kernel_page(to); | ||
| 52 | flush_icache_range(to, to + len); | ||
| 53 | } | ||
| 54 | |||
| 43 | void flush_cache_mm(struct mm_struct *mm) | 55 | void flush_cache_mm(struct mm_struct *mm) |
| 44 | { | 56 | { |
| 45 | if (cache_is_vivt()) { | 57 | if (cache_is_vivt()) { |
| @@ -90,8 +102,10 @@ void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsig | |||
| 90 | if (vma->vm_flags & VM_EXEC && icache_is_vivt_asid_tagged()) | 102 | if (vma->vm_flags & VM_EXEC && icache_is_vivt_asid_tagged()) |
| 91 | __flush_icache_all(); | 103 | __flush_icache_all(); |
| 92 | } | 104 | } |
| 105 | |||
| 93 | #else | 106 | #else |
| 94 | #define flush_pfn_alias(pfn,vaddr) do { } while (0) | 107 | #define flush_pfn_alias(pfn,vaddr) do { } while (0) |
| 108 | #define flush_icache_alias(pfn,vaddr,len) do { } while (0) | ||
| 95 | #endif | 109 | #endif |
| 96 | 110 | ||
| 97 | static void flush_ptrace_access_other(void *args) | 111 | static void flush_ptrace_access_other(void *args) |
| @@ -117,10 +131,13 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, | |||
| 117 | return; | 131 | return; |
| 118 | } | 132 | } |
| 119 | 133 | ||
| 120 | /* VIPT non-aliasing cache */ | 134 | /* VIPT non-aliasing D-cache */ |
| 121 | if (vma->vm_flags & VM_EXEC) { | 135 | if (vma->vm_flags & VM_EXEC) { |
| 122 | unsigned long addr = (unsigned long)kaddr; | 136 | unsigned long addr = (unsigned long)kaddr; |
| 123 | __cpuc_coherent_kern_range(addr, addr + len); | 137 | if (icache_is_vipt_aliasing()) |
| 138 | flush_icache_alias(page_to_pfn(page), uaddr, len); | ||
| 139 | else | ||
| 140 | __cpuc_coherent_kern_range(addr, addr + len); | ||
| 124 | if (cache_ops_need_broadcast()) | 141 | if (cache_ops_need_broadcast()) |
| 125 | smp_call_function(flush_ptrace_access_other, | 142 | smp_call_function(flush_ptrace_access_other, |
| 126 | NULL, 1); | 143 | NULL, 1); |
