11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * This file is subject to the terms and conditions of the GNU General Public 31da177e4SLinus Torvalds * License. See the file "COPYING" in the main directory of this archive 41da177e4SLinus Torvalds * for more details. 51da177e4SLinus Torvalds * 6641e97f3SRalf Baechle * Copyright (C) 1994 - 2003, 06, 07 by Ralf Baechle (ralf@linux-mips.org) 77575a49fSRalf Baechle * Copyright (C) 2007 MIPS Technologies, Inc. 81da177e4SLinus Torvalds */ 924e9d0b9SRalf Baechle #include <linux/fs.h> 1024e9d0b9SRalf Baechle #include <linux/fcntl.h> 111da177e4SLinus Torvalds #include <linux/kernel.h> 12641e97f3SRalf Baechle #include <linux/linkage.h> 13d9ba5778SPaul Gortmaker #include <linux/export.h> 141da177e4SLinus Torvalds #include <linux/sched.h> 15dbda6ac0SRalf Baechle #include <linux/syscalls.h> 161da177e4SLinus Torvalds #include <linux/mm.h> 1778b6d91eSIra Weiny #include <linux/highmem.h> 18842ca547SMatthew Wilcox (Oracle) #include <linux/pagemap.h> 191da177e4SLinus Torvalds 201da177e4SLinus Torvalds #include <asm/cacheflush.h> 211da177e4SLinus Torvalds #include <asm/processor.h> 221da177e4SLinus Torvalds #include <asm/cpu.h> 231da177e4SLinus Torvalds #include <asm/cpu-features.h> 2469939424SPaul Burton #include <asm/setup.h> 25a2fa4cedSYanteng Si #include <asm/pgtable.h> 261da177e4SLinus Torvalds 271da177e4SLinus Torvalds /* Cache operations. */ 281da177e4SLinus Torvalds void (*flush_cache_all)(void); 291da177e4SLinus Torvalds void (*__flush_cache_all)(void); 304fa9de5aSJames Hogan EXPORT_SYMBOL_GPL(__flush_cache_all); 311da177e4SLinus Torvalds void (*flush_cache_mm)(struct mm_struct *mm); 321da177e4SLinus Torvalds void (*flush_cache_range)(struct vm_area_struct *vma, unsigned long start, 331da177e4SLinus Torvalds unsigned long end); 3453de0d47SRalf Baechle void (*flush_cache_page)(struct vm_area_struct *vma, unsigned long page, 3553de0d47SRalf Baechle unsigned long pfn); 36d4264f18SAtsushi Nemoto void (*flush_icache_range)(unsigned long start, unsigned long end); 378229f1a0SKees Cook EXPORT_SYMBOL_GPL(flush_icache_range); 38e0cee3eeSThomas Bogendoerfer void (*local_flush_icache_range)(unsigned long start, unsigned long end); 3990f91356SJames Hogan EXPORT_SYMBOL_GPL(local_flush_icache_range); 4001882b4dSJames Hogan void (*__flush_icache_user_range)(unsigned long start, unsigned long end); 4101882b4dSJames Hogan void (*__local_flush_icache_user_range)(unsigned long start, unsigned long end); 4201882b4dSJames Hogan EXPORT_SYMBOL_GPL(__local_flush_icache_user_range); 431da177e4SLinus Torvalds 449c5a3d72SRalf Baechle void (*__flush_cache_vmap)(void); 459c5a3d72SRalf Baechle void (*__flush_cache_vunmap)(void); 469c5a3d72SRalf Baechle 47d9cdc901SRalf Baechle void (*__flush_kernel_vmap_range)(unsigned long vaddr, int size); 48d9cdc901SRalf Baechle EXPORT_SYMBOL_GPL(__flush_kernel_vmap_range); 49d9cdc901SRalf Baechle 501da177e4SLinus Torvalds /* MIPS specific cache operations */ 517e3bfc7cSRalf Baechle void (*local_flush_data_cache_page)(void * addr); 521da177e4SLinus Torvalds void (*flush_data_cache_page)(unsigned long addr); 531da177e4SLinus Torvalds void (*flush_icache_all)(void); 541da177e4SLinus Torvalds 559202f325SRalf Baechle EXPORT_SYMBOL_GPL(local_flush_data_cache_page); 569ff77c46SRalf Baechle EXPORT_SYMBOL(flush_data_cache_page); 57f2e3656dSSanjay Lal EXPORT_SYMBOL(flush_icache_all); 589ff77c46SRalf Baechle 59972dc3b7SChristoph Hellwig #ifdef CONFIG_DMA_NONCOHERENT 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds /* DMA cache operations. */ 621da177e4SLinus Torvalds void (*_dma_cache_wback_inv)(unsigned long start, unsigned long size); 631da177e4SLinus Torvalds void (*_dma_cache_wback)(unsigned long start, unsigned long size); 641da177e4SLinus Torvalds void (*_dma_cache_inv)(unsigned long start, unsigned long size); 651da177e4SLinus Torvalds 66972dc3b7SChristoph Hellwig #endif /* CONFIG_DMA_NONCOHERENT */ 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds /* 691da177e4SLinus Torvalds * We could optimize the case where the cache argument is not BCACHE but 701da177e4SLinus Torvalds * that seems very atypical use ... 711da177e4SLinus Torvalds */ 72dbda6ac0SRalf Baechle SYSCALL_DEFINE3(cacheflush, unsigned long, addr, unsigned long, bytes, 73dbda6ac0SRalf Baechle unsigned int, cache) 741da177e4SLinus Torvalds { 75750ccf68SAtsushi Nemoto if (bytes == 0) 76750ccf68SAtsushi Nemoto return 0; 7796d4f267SLinus Torvalds if (!access_ok((void __user *) addr, bytes)) 781da177e4SLinus Torvalds return -EFAULT; 791da177e4SLinus Torvalds 808e3a9f4cSJames Hogan __flush_icache_user_range(addr, addr + bytes); 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds return 0; 831da177e4SLinus Torvalds } 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds void __flush_dcache_page(struct page *page) 861da177e4SLinus Torvalds { 87cb9f753aSHuang Ying struct address_space *mapping = page_mapping_file(page); 881da177e4SLinus Torvalds unsigned long addr; 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds if (mapping && !mapping_mapped(mapping)) { 911da177e4SLinus Torvalds SetPageDcacheDirty(page); 921da177e4SLinus Torvalds return; 931da177e4SLinus Torvalds } 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds /* 961da177e4SLinus Torvalds * We could delay the flush for the !page_mapping case too. But that 971da177e4SLinus Torvalds * case is for exec env/arg pages and those are %99 certainly going to 981da177e4SLinus Torvalds * get faulted into the tlb (and thus flushed) anyways. 991da177e4SLinus Torvalds */ 100234859e4SPaul Burton if (PageHighMem(page)) 101234859e4SPaul Burton addr = (unsigned long)kmap_atomic(page); 102234859e4SPaul Burton else 1031da177e4SLinus Torvalds addr = (unsigned long)page_address(page); 104234859e4SPaul Burton 1051da177e4SLinus Torvalds flush_data_cache_page(addr); 106234859e4SPaul Burton 107234859e4SPaul Burton if (PageHighMem(page)) 108abca2500SIra Weiny kunmap_atomic((void *)addr); 1091da177e4SLinus Torvalds } 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds EXPORT_SYMBOL(__flush_dcache_page); 1121da177e4SLinus Torvalds 1137575a49fSRalf Baechle void __flush_anon_page(struct page *page, unsigned long vmaddr) 1147575a49fSRalf Baechle { 1159a74b3ebSRalf Baechle unsigned long addr = (unsigned long) page_address(page); 1169a74b3ebSRalf Baechle 1179a74b3ebSRalf Baechle if (pages_do_alias(addr, vmaddr)) { 118e1534ae9SKirill A. Shutemov if (page_mapcount(page) && !Page_dcache_dirty(page)) { 1197575a49fSRalf Baechle void *kaddr; 1207575a49fSRalf Baechle 1217575a49fSRalf Baechle kaddr = kmap_coherent(page, vmaddr); 1227575a49fSRalf Baechle flush_data_cache_page((unsigned long)kaddr); 123eacb9d61SRalf Baechle kunmap_coherent(); 1249a74b3ebSRalf Baechle } else 1259a74b3ebSRalf Baechle flush_data_cache_page(addr); 1267575a49fSRalf Baechle } 1277575a49fSRalf Baechle } 1287575a49fSRalf Baechle 1297575a49fSRalf Baechle EXPORT_SYMBOL(__flush_anon_page); 1307575a49fSRalf Baechle 13137d22a0dSPaul Burton void __update_cache(unsigned long address, pte_t pte) 1321da177e4SLinus Torvalds { 1331da177e4SLinus Torvalds struct page *page; 1345b9593f3SLars Persson unsigned long pfn, addr; 13537d22a0dSPaul Burton int exec = !pte_no_exec(pte) && !cpu_has_ic_fills_f_dc; 1361da177e4SLinus Torvalds 1375b9593f3SLars Persson pfn = pte_pfn(pte); 138585fa724SRalf Baechle if (unlikely(!pfn_valid(pfn))) 139585fa724SRalf Baechle return; 140585fa724SRalf Baechle page = pfn_to_page(pfn); 14137d22a0dSPaul Burton if (Page_dcache_dirty(page)) { 142f4281bbaSPaul Burton if (PageHighMem(page)) 143f4281bbaSPaul Burton addr = (unsigned long)kmap_atomic(page); 144f4281bbaSPaul Burton else 1455b9593f3SLars Persson addr = (unsigned long)page_address(page); 146f4281bbaSPaul Burton 1475b9593f3SLars Persson if (exec || pages_do_alias(addr, address & PAGE_MASK)) 1485b9593f3SLars Persson flush_data_cache_page(addr); 149f4281bbaSPaul Burton 150f4281bbaSPaul Burton if (PageHighMem(page)) 151abca2500SIra Weiny kunmap_atomic((void *)addr); 152f4281bbaSPaul Burton 1531da177e4SLinus Torvalds ClearPageDcacheDirty(page); 1541da177e4SLinus Torvalds } 1551da177e4SLinus Torvalds } 1561da177e4SLinus Torvalds 15735133692SChris Dearman unsigned long _page_cachable_default; 1587b3e543dSAnton Altaparmakov EXPORT_SYMBOL(_page_cachable_default); 15935133692SChris Dearman 160ed2adb74SThomas Bogendoerfer #define PM(p) __pgprot(_page_cachable_default | (p)) 161ed2adb74SThomas Bogendoerfer 162*499c1dd9SAnshuman Khandual static pgprot_t protection_map[16] __ro_after_init; 163*499c1dd9SAnshuman Khandual DECLARE_VM_GET_PAGE_PROT 164*499c1dd9SAnshuman Khandual 16535133692SChris Dearman static inline void setup_protection_map(void) 16635133692SChris Dearman { 167ed2adb74SThomas Bogendoerfer protection_map[0] = PM(_PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_NO_READ); 16850c25ee9SThomas Bogendoerfer protection_map[1] = PM(_PAGE_PRESENT | _PAGE_NO_EXEC); 16950c25ee9SThomas Bogendoerfer protection_map[2] = PM(_PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_NO_READ); 17050c25ee9SThomas Bogendoerfer protection_map[3] = PM(_PAGE_PRESENT | _PAGE_NO_EXEC); 17150c25ee9SThomas Bogendoerfer protection_map[4] = PM(_PAGE_PRESENT); 17250c25ee9SThomas Bogendoerfer protection_map[5] = PM(_PAGE_PRESENT); 17350c25ee9SThomas Bogendoerfer protection_map[6] = PM(_PAGE_PRESENT); 17450c25ee9SThomas Bogendoerfer protection_map[7] = PM(_PAGE_PRESENT); 1756dd9344cSDavid Daney 176ed2adb74SThomas Bogendoerfer protection_map[8] = PM(_PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_NO_READ); 17750c25ee9SThomas Bogendoerfer protection_map[9] = PM(_PAGE_PRESENT | _PAGE_NO_EXEC); 17850c25ee9SThomas Bogendoerfer protection_map[10] = PM(_PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_WRITE | 1790df162e1SThomas Bogendoerfer _PAGE_NO_READ); 18050c25ee9SThomas Bogendoerfer protection_map[11] = PM(_PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_WRITE); 18150c25ee9SThomas Bogendoerfer protection_map[12] = PM(_PAGE_PRESENT); 18250c25ee9SThomas Bogendoerfer protection_map[13] = PM(_PAGE_PRESENT); 18350c25ee9SThomas Bogendoerfer protection_map[14] = PM(_PAGE_PRESENT | _PAGE_WRITE); 18450c25ee9SThomas Bogendoerfer protection_map[15] = PM(_PAGE_PRESENT | _PAGE_WRITE); 1856dd9344cSDavid Daney } 1861da177e4SLinus Torvalds 187ed2adb74SThomas Bogendoerfer #undef PM 188ed2adb74SThomas Bogendoerfer 189078a55fcSPaul Gortmaker void cpu_cache_init(void) 1901da177e4SLinus Torvalds { 19102cf2119SRalf Baechle if (cpu_has_3k_cache) { 19202cf2119SRalf Baechle extern void __weak r3k_cache_init(void); 1931da177e4SLinus Torvalds 19402cf2119SRalf Baechle r3k_cache_init(); 1951da177e4SLinus Torvalds } 19602cf2119SRalf Baechle if (cpu_has_4k_cache) { 19702cf2119SRalf Baechle extern void __weak r4k_cache_init(void); 19802cf2119SRalf Baechle 19902cf2119SRalf Baechle r4k_cache_init(); 20002cf2119SRalf Baechle } 20102cf2119SRalf Baechle 20247d979ecSDavid Daney if (cpu_has_octeon_cache) { 20347d979ecSDavid Daney extern void __weak octeon_cache_init(void); 20447d979ecSDavid Daney 20547d979ecSDavid Daney octeon_cache_init(); 20647d979ecSDavid Daney } 20747d979ecSDavid Daney 20835133692SChris Dearman setup_protection_map(); 2091da177e4SLinus Torvalds } 210