1/* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 2009, Wind River Systems Inc 7 * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com 8 */ 9 10#include <linux/export.h> 11#include <linux/sched.h> 12#include <linux/mm.h> 13#include <linux/fs.h> 14 15#include <asm/cacheflush.h> 16#include <asm/cpuinfo.h> 17 18static void __flush_dcache(unsigned long start, unsigned long end) 19{ 20 unsigned long addr; 21 22 start &= ~(cpuinfo.dcache_line_size - 1); 23 end += (cpuinfo.dcache_line_size - 1); 24 end &= ~(cpuinfo.dcache_line_size - 1); 25 26 for (addr = start; addr < end; addr += cpuinfo.dcache_line_size) { 27 __asm__ __volatile__ (" flushda 0(%0)\n" 28 : /* Outputs */ 29 : /* Inputs */ "r"(addr) 30 /* : No clobber */); 31 } 32} 33 34static void __flush_dcache_all(unsigned long start, unsigned long end) 35{ 36 unsigned long addr; 37 38 start &= ~(cpuinfo.dcache_line_size - 1); 39 end += (cpuinfo.dcache_line_size - 1); 40 end &= ~(cpuinfo.dcache_line_size - 1); 41 42 if (end > start + cpuinfo.dcache_size) 43 end = start + cpuinfo.dcache_size; 44 45 for (addr = start; addr < end; addr += cpuinfo.dcache_line_size) { 46 __asm__ __volatile__ (" flushd 0(%0)\n" 47 : /* Outputs */ 48 : /* Inputs */ "r"(addr) 49 /* : No clobber */); 50 } 51} 52 53static void __invalidate_dcache(unsigned long start, unsigned long end) 54{ 55 unsigned long addr; 56 57 start &= ~(cpuinfo.dcache_line_size - 1); 58 end += (cpuinfo.dcache_line_size - 1); 59 end &= ~(cpuinfo.dcache_line_size - 1); 60 61 for (addr = start; addr < end; addr += cpuinfo.dcache_line_size) { 62 __asm__ __volatile__ (" initda 0(%0)\n" 63 : /* Outputs */ 64 : /* Inputs */ "r"(addr) 65 /* : No clobber */); 66 } 67} 68 69static void __flush_icache(unsigned long start, unsigned long end) 70{ 71 unsigned long addr; 72 73 start &= ~(cpuinfo.icache_line_size - 1); 74 end += (cpuinfo.icache_line_size - 1); 75 end &= ~(cpuinfo.icache_line_size - 1); 76 77 if (end > start + cpuinfo.icache_size) 78 end = start + cpuinfo.icache_size; 79 80 for (addr = start; addr < end; addr += cpuinfo.icache_line_size) { 81 __asm__ __volatile__ (" flushi %0\n" 82 : /* Outputs */ 83 : /* Inputs */ "r"(addr) 84 /* : No clobber */); 85 } 86 __asm__ __volatile(" flushp\n"); 87} 88 89static void flush_aliases(struct address_space *mapping, struct page *page) 90{ 91 struct mm_struct *mm = current->active_mm; 92 struct vm_area_struct *mpnt; 93 pgoff_t pgoff; 94 95 pgoff = page->index; 96 97 flush_dcache_mmap_lock(mapping); 98 vma_interval_tree_foreach(mpnt, &mapping->i_mmap, pgoff, pgoff) { 99 unsigned long offset; 100 101 if (mpnt->vm_mm != mm) 102 continue; 103 if (!(mpnt->vm_flags & VM_MAYSHARE)) 104 continue; 105 106 offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT; 107 flush_cache_page(mpnt, mpnt->vm_start + offset, 108 page_to_pfn(page)); 109 } 110 flush_dcache_mmap_unlock(mapping); 111} 112 113void flush_cache_all(void) 114{ 115 __flush_dcache_all(0, cpuinfo.dcache_size); 116 __flush_icache(0, cpuinfo.icache_size); 117} 118 119void flush_cache_mm(struct mm_struct *mm) 120{ 121 flush_cache_all(); 122} 123 124void flush_cache_dup_mm(struct mm_struct *mm) 125{ 126 flush_cache_all(); 127} 128 129void flush_icache_range(unsigned long start, unsigned long end) 130{ 131 __flush_dcache(start, end); 132 __flush_icache(start, end); 133} 134 135void flush_dcache_range(unsigned long start, unsigned long end) 136{ 137 __flush_dcache(start, end); 138 __flush_icache(start, end); 139} 140EXPORT_SYMBOL(flush_dcache_range); 141 142void invalidate_dcache_range(unsigned long start, unsigned long end) 143{ 144 __invalidate_dcache(start, end); 145} 146EXPORT_SYMBOL(invalidate_dcache_range); 147 148void flush_cache_range(struct vm_area_struct *vma, unsigned long start, 149 unsigned long end) 150{ 151 __flush_dcache(start, end); 152 if (vma == NULL || (vma->vm_flags & VM_EXEC)) 153 __flush_icache(start, end); 154} 155 156void flush_icache_page(struct vm_area_struct *vma, struct page *page) 157{ 158 unsigned long start = (unsigned long) page_address(page); 159 unsigned long end = start + PAGE_SIZE; 160 161 __flush_dcache(start, end); 162 __flush_icache(start, end); 163} 164 165void flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, 166 unsigned long pfn) 167{ 168 unsigned long start = vmaddr; 169 unsigned long end = start + PAGE_SIZE; 170 171 __flush_dcache(start, end); 172 if (vma->vm_flags & VM_EXEC) 173 __flush_icache(start, end); 174} 175 176void __flush_dcache_page(struct address_space *mapping, struct page *page) 177{ 178 /* 179 * Writeback any data associated with the kernel mapping of this 180 * page. This ensures that data in the physical page is mutually 181 * coherent with the kernels mapping. 182 */ 183 unsigned long start = (unsigned long)page_address(page); 184 185 __flush_dcache_all(start, start + PAGE_SIZE); 186} 187 188void flush_dcache_page(struct page *page) 189{ 190 struct address_space *mapping; 191 192 /* 193 * The zero page is never written to, so never has any dirty 194 * cache lines, and therefore never needs to be flushed. 195 */ 196 if (page == ZERO_PAGE(0)) 197 return; 198 199 mapping = page_mapping(page); 200 201 /* Flush this page if there are aliases. */ 202 if (mapping && !mapping_mapped(mapping)) { 203 clear_bit(PG_dcache_clean, &page->flags); 204 } else { 205 __flush_dcache_page(mapping, page); 206 if (mapping) { 207 unsigned long start = (unsigned long)page_address(page); 208 flush_aliases(mapping, page); 209 flush_icache_range(start, start + PAGE_SIZE); 210 } 211 set_bit(PG_dcache_clean, &page->flags); 212 } 213} 214EXPORT_SYMBOL(flush_dcache_page); 215 216void update_mmu_cache(struct vm_area_struct *vma, 217 unsigned long address, pte_t *pte) 218{ 219 unsigned long pfn = pte_pfn(*pte); 220 struct page *page; 221 struct address_space *mapping; 222 223 if (!pfn_valid(pfn)) 224 return; 225 226 /* 227 * The zero page is never written to, so never has any dirty 228 * cache lines, and therefore never needs to be flushed. 229 */ 230 page = pfn_to_page(pfn); 231 if (page == ZERO_PAGE(0)) 232 return; 233 234 mapping = page_mapping(page); 235 if (!test_and_set_bit(PG_dcache_clean, &page->flags)) 236 __flush_dcache_page(mapping, page); 237 238 if(mapping) 239 { 240 flush_aliases(mapping, page); 241 if (vma->vm_flags & VM_EXEC) 242 flush_icache_page(vma, page); 243 } 244} 245 246void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, 247 struct page *to) 248{ 249 __flush_dcache(vaddr, vaddr + PAGE_SIZE); 250 __flush_icache(vaddr, vaddr + PAGE_SIZE); 251 copy_page(vto, vfrom); 252 __flush_dcache((unsigned long)vto, (unsigned long)vto + PAGE_SIZE); 253 __flush_icache((unsigned long)vto, (unsigned long)vto + PAGE_SIZE); 254} 255 256void clear_user_page(void *addr, unsigned long vaddr, struct page *page) 257{ 258 __flush_dcache(vaddr, vaddr + PAGE_SIZE); 259 __flush_icache(vaddr, vaddr + PAGE_SIZE); 260 clear_page(addr); 261 __flush_dcache((unsigned long)addr, (unsigned long)addr + PAGE_SIZE); 262 __flush_icache((unsigned long)addr, (unsigned long)addr + PAGE_SIZE); 263} 264 265void copy_from_user_page(struct vm_area_struct *vma, struct page *page, 266 unsigned long user_vaddr, 267 void *dst, void *src, int len) 268{ 269 flush_cache_page(vma, user_vaddr, page_to_pfn(page)); 270 memcpy(dst, src, len); 271 __flush_dcache_all((unsigned long)src, (unsigned long)src + len); 272 if (vma->vm_flags & VM_EXEC) 273 __flush_icache((unsigned long)src, (unsigned long)src + len); 274} 275 276void copy_to_user_page(struct vm_area_struct *vma, struct page *page, 277 unsigned long user_vaddr, 278 void *dst, void *src, int len) 279{ 280 flush_cache_page(vma, user_vaddr, page_to_pfn(page)); 281 memcpy(dst, src, len); 282 __flush_dcache_all((unsigned long)dst, (unsigned long)dst + len); 283 if (vma->vm_flags & VM_EXEC) 284 __flush_icache((unsigned long)dst, (unsigned long)dst + len); 285} 286