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 /arch | |
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>
Diffstat (limited to 'arch')
-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); |