18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/arch/arm/mm/flush.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1995-2002 Russell King 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/mm.h> 98c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 108c2ecf20Sopenharmony_ci#include <linux/highmem.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 138c2ecf20Sopenharmony_ci#include <asm/cachetype.h> 148c2ecf20Sopenharmony_ci#include <asm/highmem.h> 158c2ecf20Sopenharmony_ci#include <asm/smp_plat.h> 168c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 178c2ecf20Sopenharmony_ci#include <linux/hugetlb.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "mm.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_HEAVY_MB 228c2ecf20Sopenharmony_civoid (*soc_mb)(void); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_civoid arm_heavy_mb(void) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci#ifdef CONFIG_OUTER_CACHE_SYNC 278c2ecf20Sopenharmony_ci if (outer_cache.sync) 288c2ecf20Sopenharmony_ci outer_cache.sync(); 298c2ecf20Sopenharmony_ci#endif 308c2ecf20Sopenharmony_ci if (soc_mb) 318c2ecf20Sopenharmony_ci soc_mb(); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(arm_heavy_mb); 348c2ecf20Sopenharmony_ci#endif 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_CACHE_VIPT 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic void flush_pfn_alias(unsigned long pfn, unsigned long vaddr) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci unsigned long to = FLUSH_ALIAS_START + (CACHE_COLOUR(vaddr) << PAGE_SHIFT); 418c2ecf20Sopenharmony_ci const int zero = 0; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci set_top_pte(to, pfn_pte(pfn, PAGE_KERNEL)); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci asm( "mcrr p15, 0, %1, %0, c14\n" 468c2ecf20Sopenharmony_ci " mcr p15, 0, %2, c7, c10, 4" 478c2ecf20Sopenharmony_ci : 488c2ecf20Sopenharmony_ci : "r" (to), "r" (to + PAGE_SIZE - 1), "r" (zero) 498c2ecf20Sopenharmony_ci : "cc"); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void flush_icache_alias(unsigned long pfn, unsigned long vaddr, unsigned long len) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci unsigned long va = FLUSH_ALIAS_START + (CACHE_COLOUR(vaddr) << PAGE_SHIFT); 558c2ecf20Sopenharmony_ci unsigned long offset = vaddr & (PAGE_SIZE - 1); 568c2ecf20Sopenharmony_ci unsigned long to; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci set_top_pte(va, pfn_pte(pfn, PAGE_KERNEL)); 598c2ecf20Sopenharmony_ci to = va + offset; 608c2ecf20Sopenharmony_ci flush_icache_range(to, to + len); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_civoid flush_cache_mm(struct mm_struct *mm) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci if (cache_is_vivt()) { 668c2ecf20Sopenharmony_ci vivt_flush_cache_mm(mm); 678c2ecf20Sopenharmony_ci return; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (cache_is_vipt_aliasing()) { 718c2ecf20Sopenharmony_ci asm( "mcr p15, 0, %0, c7, c14, 0\n" 728c2ecf20Sopenharmony_ci " mcr p15, 0, %0, c7, c10, 4" 738c2ecf20Sopenharmony_ci : 748c2ecf20Sopenharmony_ci : "r" (0) 758c2ecf20Sopenharmony_ci : "cc"); 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_civoid flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci if (cache_is_vivt()) { 828c2ecf20Sopenharmony_ci vivt_flush_cache_range(vma, start, end); 838c2ecf20Sopenharmony_ci return; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (cache_is_vipt_aliasing()) { 878c2ecf20Sopenharmony_ci asm( "mcr p15, 0, %0, c7, c14, 0\n" 888c2ecf20Sopenharmony_ci " mcr p15, 0, %0, c7, c10, 4" 898c2ecf20Sopenharmony_ci : 908c2ecf20Sopenharmony_ci : "r" (0) 918c2ecf20Sopenharmony_ci : "cc"); 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_EXEC) 958c2ecf20Sopenharmony_ci __flush_icache_all(); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_civoid flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsigned long pfn) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci if (cache_is_vivt()) { 1018c2ecf20Sopenharmony_ci vivt_flush_cache_page(vma, user_addr, pfn); 1028c2ecf20Sopenharmony_ci return; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (cache_is_vipt_aliasing()) { 1068c2ecf20Sopenharmony_ci flush_pfn_alias(pfn, user_addr); 1078c2ecf20Sopenharmony_ci __flush_icache_all(); 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_EXEC && icache_is_vivt_asid_tagged()) 1118c2ecf20Sopenharmony_ci __flush_icache_all(); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#else 1158c2ecf20Sopenharmony_ci#define flush_pfn_alias(pfn,vaddr) do { } while (0) 1168c2ecf20Sopenharmony_ci#define flush_icache_alias(pfn,vaddr,len) do { } while (0) 1178c2ecf20Sopenharmony_ci#endif 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci#define FLAG_PA_IS_EXEC 1 1208c2ecf20Sopenharmony_ci#define FLAG_PA_CORE_IN_MM 2 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void flush_ptrace_access_other(void *args) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci __flush_icache_all(); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic inline 1288c2ecf20Sopenharmony_civoid __flush_ptrace_access(struct page *page, unsigned long uaddr, void *kaddr, 1298c2ecf20Sopenharmony_ci unsigned long len, unsigned int flags) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci if (cache_is_vivt()) { 1328c2ecf20Sopenharmony_ci if (flags & FLAG_PA_CORE_IN_MM) { 1338c2ecf20Sopenharmony_ci unsigned long addr = (unsigned long)kaddr; 1348c2ecf20Sopenharmony_ci __cpuc_coherent_kern_range(addr, addr + len); 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci return; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (cache_is_vipt_aliasing()) { 1408c2ecf20Sopenharmony_ci flush_pfn_alias(page_to_pfn(page), uaddr); 1418c2ecf20Sopenharmony_ci __flush_icache_all(); 1428c2ecf20Sopenharmony_ci return; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* VIPT non-aliasing D-cache */ 1468c2ecf20Sopenharmony_ci if (flags & FLAG_PA_IS_EXEC) { 1478c2ecf20Sopenharmony_ci unsigned long addr = (unsigned long)kaddr; 1488c2ecf20Sopenharmony_ci if (icache_is_vipt_aliasing()) 1498c2ecf20Sopenharmony_ci flush_icache_alias(page_to_pfn(page), uaddr, len); 1508c2ecf20Sopenharmony_ci else 1518c2ecf20Sopenharmony_ci __cpuc_coherent_kern_range(addr, addr + len); 1528c2ecf20Sopenharmony_ci if (cache_ops_need_broadcast()) 1538c2ecf20Sopenharmony_ci smp_call_function(flush_ptrace_access_other, 1548c2ecf20Sopenharmony_ci NULL, 1); 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic 1598c2ecf20Sopenharmony_civoid flush_ptrace_access(struct vm_area_struct *vma, struct page *page, 1608c2ecf20Sopenharmony_ci unsigned long uaddr, void *kaddr, unsigned long len) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci unsigned int flags = 0; 1638c2ecf20Sopenharmony_ci if (cpumask_test_cpu(smp_processor_id(), mm_cpumask(vma->vm_mm))) 1648c2ecf20Sopenharmony_ci flags |= FLAG_PA_CORE_IN_MM; 1658c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_EXEC) 1668c2ecf20Sopenharmony_ci flags |= FLAG_PA_IS_EXEC; 1678c2ecf20Sopenharmony_ci __flush_ptrace_access(page, uaddr, kaddr, len, flags); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_civoid flush_uprobe_xol_access(struct page *page, unsigned long uaddr, 1718c2ecf20Sopenharmony_ci void *kaddr, unsigned long len) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci unsigned int flags = FLAG_PA_CORE_IN_MM|FLAG_PA_IS_EXEC; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci __flush_ptrace_access(page, uaddr, kaddr, len, flags); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* 1798c2ecf20Sopenharmony_ci * Copy user data from/to a page which is mapped into a different 1808c2ecf20Sopenharmony_ci * processes address space. Really, we want to allow our "user 1818c2ecf20Sopenharmony_ci * space" model to handle this. 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci * Note that this code needs to run on the current CPU. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_civoid copy_to_user_page(struct vm_area_struct *vma, struct page *page, 1868c2ecf20Sopenharmony_ci unsigned long uaddr, void *dst, const void *src, 1878c2ecf20Sopenharmony_ci unsigned long len) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 1908c2ecf20Sopenharmony_ci preempt_disable(); 1918c2ecf20Sopenharmony_ci#endif 1928c2ecf20Sopenharmony_ci memcpy(dst, src, len); 1938c2ecf20Sopenharmony_ci flush_ptrace_access(vma, page, uaddr, dst, len); 1948c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 1958c2ecf20Sopenharmony_ci preempt_enable(); 1968c2ecf20Sopenharmony_ci#endif 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_civoid __flush_dcache_page(struct address_space *mapping, struct page *page) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci /* 2028c2ecf20Sopenharmony_ci * Writeback any data associated with the kernel mapping of this 2038c2ecf20Sopenharmony_ci * page. This ensures that data in the physical page is mutually 2048c2ecf20Sopenharmony_ci * coherent with the kernels mapping. 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci if (!PageHighMem(page)) { 2078c2ecf20Sopenharmony_ci __cpuc_flush_dcache_area(page_address(page), page_size(page)); 2088c2ecf20Sopenharmony_ci } else { 2098c2ecf20Sopenharmony_ci unsigned long i; 2108c2ecf20Sopenharmony_ci if (cache_is_vipt_nonaliasing()) { 2118c2ecf20Sopenharmony_ci for (i = 0; i < compound_nr(page); i++) { 2128c2ecf20Sopenharmony_ci void *addr = kmap_atomic(page + i); 2138c2ecf20Sopenharmony_ci __cpuc_flush_dcache_area(addr, PAGE_SIZE); 2148c2ecf20Sopenharmony_ci kunmap_atomic(addr); 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci } else { 2178c2ecf20Sopenharmony_ci for (i = 0; i < compound_nr(page); i++) { 2188c2ecf20Sopenharmony_ci void *addr = kmap_high_get(page + i); 2198c2ecf20Sopenharmony_ci if (addr) { 2208c2ecf20Sopenharmony_ci __cpuc_flush_dcache_area(addr, PAGE_SIZE); 2218c2ecf20Sopenharmony_ci kunmap_high(page + i); 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* 2288c2ecf20Sopenharmony_ci * If this is a page cache page, and we have an aliasing VIPT cache, 2298c2ecf20Sopenharmony_ci * we only need to do one flush - which would be at the relevant 2308c2ecf20Sopenharmony_ci * userspace colour, which is congruent with page->index. 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_ci if (mapping && cache_is_vipt_aliasing()) 2338c2ecf20Sopenharmony_ci flush_pfn_alias(page_to_pfn(page), 2348c2ecf20Sopenharmony_ci page->index << PAGE_SHIFT); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic void __flush_dcache_aliases(struct address_space *mapping, struct page *page) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci struct mm_struct *mm = current->active_mm; 2408c2ecf20Sopenharmony_ci struct vm_area_struct *mpnt; 2418c2ecf20Sopenharmony_ci pgoff_t pgoff; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* 2448c2ecf20Sopenharmony_ci * There are possible user space mappings of this page: 2458c2ecf20Sopenharmony_ci * - VIVT cache: we need to also write back and invalidate all user 2468c2ecf20Sopenharmony_ci * data in the current VM view associated with this page. 2478c2ecf20Sopenharmony_ci * - aliasing VIPT: we only need to find one mapping of this page. 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_ci pgoff = page->index; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci flush_dcache_mmap_lock(mapping); 2528c2ecf20Sopenharmony_ci vma_interval_tree_foreach(mpnt, &mapping->i_mmap, pgoff, pgoff) { 2538c2ecf20Sopenharmony_ci unsigned long offset; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* 2568c2ecf20Sopenharmony_ci * If this VMA is not in our MM, we can ignore it. 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_ci if (mpnt->vm_mm != mm) 2598c2ecf20Sopenharmony_ci continue; 2608c2ecf20Sopenharmony_ci if (!(mpnt->vm_flags & VM_MAYSHARE)) 2618c2ecf20Sopenharmony_ci continue; 2628c2ecf20Sopenharmony_ci offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT; 2638c2ecf20Sopenharmony_ci flush_cache_page(mpnt, mpnt->vm_start + offset, page_to_pfn(page)); 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci flush_dcache_mmap_unlock(mapping); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci#if __LINUX_ARM_ARCH__ >= 6 2698c2ecf20Sopenharmony_civoid __sync_icache_dcache(pte_t pteval) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci unsigned long pfn; 2728c2ecf20Sopenharmony_ci struct page *page; 2738c2ecf20Sopenharmony_ci struct address_space *mapping; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (cache_is_vipt_nonaliasing() && !pte_exec(pteval)) 2768c2ecf20Sopenharmony_ci /* only flush non-aliasing VIPT caches for exec mappings */ 2778c2ecf20Sopenharmony_ci return; 2788c2ecf20Sopenharmony_ci pfn = pte_pfn(pteval); 2798c2ecf20Sopenharmony_ci if (!pfn_valid(pfn)) 2808c2ecf20Sopenharmony_ci return; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci page = pfn_to_page(pfn); 2838c2ecf20Sopenharmony_ci if (cache_is_vipt_aliasing()) 2848c2ecf20Sopenharmony_ci mapping = page_mapping_file(page); 2858c2ecf20Sopenharmony_ci else 2868c2ecf20Sopenharmony_ci mapping = NULL; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (!test_and_set_bit(PG_dcache_clean, &page->flags)) 2898c2ecf20Sopenharmony_ci __flush_dcache_page(mapping, page); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (pte_exec(pteval)) 2928c2ecf20Sopenharmony_ci __flush_icache_all(); 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci#endif 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci/* 2978c2ecf20Sopenharmony_ci * Ensure cache coherency between kernel mapping and userspace mapping 2988c2ecf20Sopenharmony_ci * of this page. 2998c2ecf20Sopenharmony_ci * 3008c2ecf20Sopenharmony_ci * We have three cases to consider: 3018c2ecf20Sopenharmony_ci * - VIPT non-aliasing cache: fully coherent so nothing required. 3028c2ecf20Sopenharmony_ci * - VIVT: fully aliasing, so we need to handle every alias in our 3038c2ecf20Sopenharmony_ci * current VM view. 3048c2ecf20Sopenharmony_ci * - VIPT aliasing: need to handle one alias in our current VM view. 3058c2ecf20Sopenharmony_ci * 3068c2ecf20Sopenharmony_ci * If we need to handle aliasing: 3078c2ecf20Sopenharmony_ci * If the page only exists in the page cache and there are no user 3088c2ecf20Sopenharmony_ci * space mappings, we can be lazy and remember that we may have dirty 3098c2ecf20Sopenharmony_ci * kernel cache lines for later. Otherwise, we assume we have 3108c2ecf20Sopenharmony_ci * aliasing mappings. 3118c2ecf20Sopenharmony_ci * 3128c2ecf20Sopenharmony_ci * Note that we disable the lazy flush for SMP configurations where 3138c2ecf20Sopenharmony_ci * the cache maintenance operations are not automatically broadcasted. 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_civoid flush_dcache_page(struct page *page) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct address_space *mapping; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* 3208c2ecf20Sopenharmony_ci * The zero page is never written to, so never has any dirty 3218c2ecf20Sopenharmony_ci * cache lines, and therefore never needs to be flushed. 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_ci if (page == ZERO_PAGE(0)) 3248c2ecf20Sopenharmony_ci return; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (!cache_ops_need_broadcast() && cache_is_vipt_nonaliasing()) { 3278c2ecf20Sopenharmony_ci if (test_bit(PG_dcache_clean, &page->flags)) 3288c2ecf20Sopenharmony_ci clear_bit(PG_dcache_clean, &page->flags); 3298c2ecf20Sopenharmony_ci return; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci mapping = page_mapping_file(page); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (!cache_ops_need_broadcast() && 3358c2ecf20Sopenharmony_ci mapping && !page_mapcount(page)) 3368c2ecf20Sopenharmony_ci clear_bit(PG_dcache_clean, &page->flags); 3378c2ecf20Sopenharmony_ci else { 3388c2ecf20Sopenharmony_ci __flush_dcache_page(mapping, page); 3398c2ecf20Sopenharmony_ci if (mapping && cache_is_vivt()) 3408c2ecf20Sopenharmony_ci __flush_dcache_aliases(mapping, page); 3418c2ecf20Sopenharmony_ci else if (mapping) 3428c2ecf20Sopenharmony_ci __flush_icache_all(); 3438c2ecf20Sopenharmony_ci set_bit(PG_dcache_clean, &page->flags); 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(flush_dcache_page); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci/* 3498c2ecf20Sopenharmony_ci * Ensure cache coherency for the kernel mapping of this page. We can 3508c2ecf20Sopenharmony_ci * assume that the page is pinned via kmap. 3518c2ecf20Sopenharmony_ci * 3528c2ecf20Sopenharmony_ci * If the page only exists in the page cache and there are no user 3538c2ecf20Sopenharmony_ci * space mappings, this is a no-op since the page was already marked 3548c2ecf20Sopenharmony_ci * dirty at creation. Otherwise, we need to flush the dirty kernel 3558c2ecf20Sopenharmony_ci * cache lines directly. 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_civoid flush_kernel_dcache_page(struct page *page) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci if (cache_is_vivt() || cache_is_vipt_aliasing()) { 3608c2ecf20Sopenharmony_ci struct address_space *mapping; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci mapping = page_mapping_file(page); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (!mapping || mapping_mapped(mapping)) { 3658c2ecf20Sopenharmony_ci void *addr; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci addr = page_address(page); 3688c2ecf20Sopenharmony_ci /* 3698c2ecf20Sopenharmony_ci * kmap_atomic() doesn't set the page virtual 3708c2ecf20Sopenharmony_ci * address for highmem pages, and 3718c2ecf20Sopenharmony_ci * kunmap_atomic() takes care of cache 3728c2ecf20Sopenharmony_ci * flushing already. 3738c2ecf20Sopenharmony_ci */ 3748c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_HIGHMEM) || addr) 3758c2ecf20Sopenharmony_ci __cpuc_flush_dcache_area(addr, PAGE_SIZE); 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(flush_kernel_dcache_page); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci/* 3828c2ecf20Sopenharmony_ci * Flush an anonymous page so that users of get_user_pages() 3838c2ecf20Sopenharmony_ci * can safely access the data. The expected sequence is: 3848c2ecf20Sopenharmony_ci * 3858c2ecf20Sopenharmony_ci * get_user_pages() 3868c2ecf20Sopenharmony_ci * -> flush_anon_page 3878c2ecf20Sopenharmony_ci * memcpy() to/from page 3888c2ecf20Sopenharmony_ci * if written to page, flush_dcache_page() 3898c2ecf20Sopenharmony_ci */ 3908c2ecf20Sopenharmony_civoid __flush_anon_page(struct vm_area_struct *vma, struct page *page, unsigned long vmaddr) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci unsigned long pfn; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* VIPT non-aliasing caches need do nothing */ 3958c2ecf20Sopenharmony_ci if (cache_is_vipt_nonaliasing()) 3968c2ecf20Sopenharmony_ci return; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* 3998c2ecf20Sopenharmony_ci * Write back and invalidate userspace mapping. 4008c2ecf20Sopenharmony_ci */ 4018c2ecf20Sopenharmony_ci pfn = page_to_pfn(page); 4028c2ecf20Sopenharmony_ci if (cache_is_vivt()) { 4038c2ecf20Sopenharmony_ci flush_cache_page(vma, vmaddr, pfn); 4048c2ecf20Sopenharmony_ci } else { 4058c2ecf20Sopenharmony_ci /* 4068c2ecf20Sopenharmony_ci * For aliasing VIPT, we can flush an alias of the 4078c2ecf20Sopenharmony_ci * userspace address only. 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_ci flush_pfn_alias(pfn, vmaddr); 4108c2ecf20Sopenharmony_ci __flush_icache_all(); 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* 4148c2ecf20Sopenharmony_ci * Invalidate kernel mapping. No data should be contained 4158c2ecf20Sopenharmony_ci * in this mapping of the page. FIXME: this is overkill 4168c2ecf20Sopenharmony_ci * since we actually ask for a write-back and invalidate. 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_ci __cpuc_flush_dcache_area(page_address(page), PAGE_SIZE); 4198c2ecf20Sopenharmony_ci} 420