From 2eaa7ec286db54cc1a864565ed9367966743bcbd Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Mon, 11 Feb 2008 14:51:40 +0000 Subject: [PATCH] [MIPS] Handle I-cache coherency in flush_cache_range() So far flush_cache_range() did't consider the I-cache largely because it did rarely ever matter to real world code. This was working primarily because normally code and data are don't share the same pages - with the exception of MIPS16 code which uses address constants embedded between the code. The following sequence of events may break the code: o MIPS16 executable being loaded o dynamic linker relocates the address constants embedded into the code: o Uses mprotect(2) to make code pages PROT_READ|PROT_WRITE o Performs the actual relocations by writing to the pages which likely are COW. Because no PROT_EXEC is set I-cache coherence will not be considered. o Uses mprotect(2) to switch code pages back to PROT_READ|PROT_EXEC. This results in a call to flush_cache_range() which also does not consider I-caches. o => executing the page just having been relocated may now result in the I-cache getting refilled with stale data from memory. Signed-off-by: Ralf Baechle --- arch/mips/mm/c-r4k.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c index 53ec05267a98..2c4f7e11f0d5 100644 --- a/arch/mips/mm/c-r4k.c +++ b/arch/mips/mm/c-r4k.c @@ -364,20 +364,23 @@ static inline int has_valid_asid(const struct mm_struct *mm) static inline void local_r4k_flush_cache_range(void * args) { struct vm_area_struct *vma = args; + int exec = vma->vm_flags & VM_EXEC; if (!(has_valid_asid(vma->vm_mm))) return; r4k_blast_dcache(); + if (exec) + r4k_blast_icache(); } static void r4k_flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { - if (!cpu_has_dc_aliases) - return; + int exec = vma->vm_flags & VM_EXEC; - r4k_on_each_cpu(local_r4k_flush_cache_range, vma, 1, 1); + if (cpu_has_dc_aliases || (exec && !cpu_has_ic_fills_f_dc)) + r4k_on_each_cpu(local_r4k_flush_cache_range, vma, 1, 1); } static inline void local_r4k_flush_cache_mm(void * args)