diff options
author | George G. Davis <davis_g@mvista.com> | 2006-09-02 13:43:20 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2006-09-02 13:43:20 -0400 |
commit | a188ad2bc7dbfa16ccdcaa8d43ade185b969baff (patch) | |
tree | 7938fff8dded204e92bd7c3149875abb8d7060aa | |
parent | 57bcdafcb1e0782e7ae13471d9223c69e3a6cba2 (diff) |
[ARM] 3762/1: Fix ptrace cache coherency bug for ARM1136 VIPT nonaliasing Harvard caches
Patch from George G. Davis
Resolve ARM1136 VIPT non-aliasing cache coherency issues observed when
using ptrace to set breakpoints and cleanup copy_{to,from}_user_page()
while we're here as requested by Russell King because "it's also far
too heavy on non-v6 CPUs".
NOTES:
1. Only access_process_vm() calls copy_{to,from}_user_page().
2. access_process_vm() calls get_user_pages() to pin down the "page".
3. get_user_pages() calls flush_dcache_page(page) which ensures cache
coherency between kernel and userspace mappings of "page". However
flush_dcache_page(page) may not invalidate I-Cache over this range
for all cases, specifically, I-Cache is not invalidated for the VIPT
non-aliasing case. So memory is consistent between kernel and user
space mappings of "page" but I-Cache may still be hot over this
range. IOW, we don't have to worry about flush_cache_page() before
memcpy().
4. Now, for the copy_to_user_page() case, after memcpy(), we must flush
the caches so memory is consistent with kernel cache entries and
invalidate the I-Cache if this mm region is executable. We don't
need to do anything after memcpy() for the copy_from_user_page()
case since kernel cache entries will be invalidated via the same
process above if we access "page" again. The flush_ptrace_access()
function (borrowed from SPARC64 implementation) is added to handle
cache flushing after memcpy() for the copy_to_user_page() case.
Signed-off-by: George G. Davis <gdavis@mvista.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r-- | arch/arm/mm/flush.c | 26 | ||||
-rw-r--r-- | include/asm-arm/cacheflush.h | 18 |
2 files changed, 41 insertions, 3 deletions
diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index b103e56806bd..d438ce41cdd5 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c | |||
@@ -87,6 +87,32 @@ void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsig | |||
87 | if (cache_is_vipt_aliasing()) | 87 | if (cache_is_vipt_aliasing()) |
88 | flush_pfn_alias(pfn, user_addr); | 88 | flush_pfn_alias(pfn, user_addr); |
89 | } | 89 | } |
90 | |||
91 | void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, | ||
92 | unsigned long uaddr, void *kaddr, | ||
93 | unsigned long len, int write) | ||
94 | { | ||
95 | if (cache_is_vivt()) { | ||
96 | if (cpu_isset(smp_processor_id(), vma->vm_mm->cpu_vm_mask)) { | ||
97 | unsigned long addr = (unsigned long)kaddr; | ||
98 | __cpuc_coherent_kern_range(addr, addr + len); | ||
99 | } | ||
100 | return; | ||
101 | } | ||
102 | |||
103 | if (cache_is_vipt_aliasing()) { | ||
104 | flush_pfn_alias(page_to_pfn(page), uaddr); | ||
105 | return; | ||
106 | } | ||
107 | |||
108 | /* VIPT non-aliasing cache */ | ||
109 | if (cpu_isset(smp_processor_id(), vma->vm_mm->cpu_vm_mask) && | ||
110 | vma->vm_flags | VM_EXEC) { | ||
111 | unsigned long addr = (unsigned long)kaddr; | ||
112 | /* only flushing the kernel mapping on non-aliasing VIPT */ | ||
113 | __cpuc_coherent_kern_range(addr, addr + len); | ||
114 | } | ||
115 | } | ||
90 | #else | 116 | #else |
91 | #define flush_pfn_alias(pfn,vaddr) do { } while (0) | 117 | #define flush_pfn_alias(pfn,vaddr) do { } while (0) |
92 | #endif | 118 | #endif |
diff --git a/include/asm-arm/cacheflush.h b/include/asm-arm/cacheflush.h index fe0c744e0266..e4a2569c636c 100644 --- a/include/asm-arm/cacheflush.h +++ b/include/asm-arm/cacheflush.h | |||
@@ -247,14 +247,12 @@ extern void dmac_flush_range(unsigned long, unsigned long); | |||
247 | */ | 247 | */ |
248 | #define copy_to_user_page(vma, page, vaddr, dst, src, len) \ | 248 | #define copy_to_user_page(vma, page, vaddr, dst, src, len) \ |
249 | do { \ | 249 | do { \ |
250 | flush_cache_page(vma, vaddr, page_to_pfn(page));\ | ||
251 | memcpy(dst, src, len); \ | 250 | memcpy(dst, src, len); \ |
252 | flush_dcache_page(page); \ | 251 | flush_ptrace_access(vma, page, vaddr, dst, len, 1);\ |
253 | } while (0) | 252 | } while (0) |
254 | 253 | ||
255 | #define copy_from_user_page(vma, page, vaddr, dst, src, len) \ | 254 | #define copy_from_user_page(vma, page, vaddr, dst, src, len) \ |
256 | do { \ | 255 | do { \ |
257 | flush_cache_page(vma, vaddr, page_to_pfn(page));\ | ||
258 | memcpy(dst, src, len); \ | 256 | memcpy(dst, src, len); \ |
259 | } while (0) | 257 | } while (0) |
260 | 258 | ||
@@ -285,10 +283,24 @@ flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsigned l | |||
285 | __cpuc_flush_user_range(addr, addr + PAGE_SIZE, vma->vm_flags); | 283 | __cpuc_flush_user_range(addr, addr + PAGE_SIZE, vma->vm_flags); |
286 | } | 284 | } |
287 | } | 285 | } |
286 | |||
287 | static inline void | ||
288 | flush_ptrace_access(struct vm_area_struct *vma, struct page *page, | ||
289 | unsigned long uaddr, void *kaddr, | ||
290 | unsigned long len, int write) | ||
291 | { | ||
292 | if (cpu_isset(smp_processor_id(), vma->vm_mm->cpu_vm_mask)) { | ||
293 | unsigned long addr = (unsigned long)kaddr; | ||
294 | __cpuc_coherent_kern_range(addr, addr + len); | ||
295 | } | ||
296 | } | ||
288 | #else | 297 | #else |
289 | extern void flush_cache_mm(struct mm_struct *mm); | 298 | extern void flush_cache_mm(struct mm_struct *mm); |
290 | extern void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end); | 299 | extern void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end); |
291 | extern void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsigned long pfn); | 300 | extern void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsigned long pfn); |
301 | extern void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, | ||
302 | unsigned long uaddr, void *kaddr, | ||
303 | unsigned long len, int write); | ||
292 | #endif | 304 | #endif |
293 | 305 | ||
294 | /* | 306 | /* |