162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/highmem.h> 462306a36Sopenharmony_ci#include <linux/kprobes.h> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/** 762306a36Sopenharmony_ci * flush_coherent_icache() - if a CPU has a coherent icache, flush it 862306a36Sopenharmony_ci * Return true if the cache was flushed, false otherwise 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_cistatic inline bool flush_coherent_icache(void) 1162306a36Sopenharmony_ci{ 1262306a36Sopenharmony_ci /* 1362306a36Sopenharmony_ci * For a snooping icache, we still need a dummy icbi to purge all the 1462306a36Sopenharmony_ci * prefetched instructions from the ifetch buffers. We also need a sync 1562306a36Sopenharmony_ci * before the icbi to order the actual stores to memory that might 1662306a36Sopenharmony_ci * have modified instructions with the icbi. 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_COHERENT_ICACHE)) { 1962306a36Sopenharmony_ci mb(); /* sync */ 2062306a36Sopenharmony_ci icbi((void *)PAGE_OFFSET); 2162306a36Sopenharmony_ci mb(); /* sync */ 2262306a36Sopenharmony_ci isync(); 2362306a36Sopenharmony_ci return true; 2462306a36Sopenharmony_ci } 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci return false; 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/** 3062306a36Sopenharmony_ci * invalidate_icache_range() - Flush the icache by issuing icbi across an address range 3162306a36Sopenharmony_ci * @start: the start address 3262306a36Sopenharmony_ci * @stop: the stop address (exclusive) 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_cistatic void invalidate_icache_range(unsigned long start, unsigned long stop) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci unsigned long shift = l1_icache_shift(); 3762306a36Sopenharmony_ci unsigned long bytes = l1_icache_bytes(); 3862306a36Sopenharmony_ci char *addr = (char *)(start & ~(bytes - 1)); 3962306a36Sopenharmony_ci unsigned long size = stop - (unsigned long)addr + (bytes - 1); 4062306a36Sopenharmony_ci unsigned long i; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci for (i = 0; i < size >> shift; i++, addr += bytes) 4362306a36Sopenharmony_ci icbi(addr); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci mb(); /* sync */ 4662306a36Sopenharmony_ci isync(); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/** 5062306a36Sopenharmony_ci * flush_icache_range: Write any modified data cache blocks out to memory 5162306a36Sopenharmony_ci * and invalidate the corresponding blocks in the instruction cache 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * Generic code will call this after writing memory, before executing from it. 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * @start: the start address 5662306a36Sopenharmony_ci * @stop: the stop address (exclusive) 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_civoid flush_icache_range(unsigned long start, unsigned long stop) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci if (flush_coherent_icache()) 6162306a36Sopenharmony_ci return; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci clean_dcache_range(start, stop); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_44x)) { 6662306a36Sopenharmony_ci /* 6762306a36Sopenharmony_ci * Flash invalidate on 44x because we are passed kmapped 6862306a36Sopenharmony_ci * addresses and this doesn't work for userspace pages due to 6962306a36Sopenharmony_ci * the virtually tagged icache. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci iccci((void *)start); 7262306a36Sopenharmony_ci mb(); /* sync */ 7362306a36Sopenharmony_ci isync(); 7462306a36Sopenharmony_ci } else 7562306a36Sopenharmony_ci invalidate_icache_range(start, stop); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ciEXPORT_SYMBOL(flush_icache_range); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#ifdef CONFIG_HIGHMEM 8062306a36Sopenharmony_ci/** 8162306a36Sopenharmony_ci * flush_dcache_icache_phys() - Flush a page by it's physical address 8262306a36Sopenharmony_ci * @physaddr: the physical address of the page 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_cistatic void flush_dcache_icache_phys(unsigned long physaddr) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci unsigned long bytes = l1_dcache_bytes(); 8762306a36Sopenharmony_ci unsigned long nb = PAGE_SIZE / bytes; 8862306a36Sopenharmony_ci unsigned long addr = physaddr & PAGE_MASK; 8962306a36Sopenharmony_ci unsigned long msr, msr0; 9062306a36Sopenharmony_ci unsigned long loop1 = addr, loop2 = addr; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci msr0 = mfmsr(); 9362306a36Sopenharmony_ci msr = msr0 & ~MSR_DR; 9462306a36Sopenharmony_ci /* 9562306a36Sopenharmony_ci * This must remain as ASM to prevent potential memory accesses 9662306a36Sopenharmony_ci * while the data MMU is disabled 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci asm volatile( 9962306a36Sopenharmony_ci " mtctr %2;\n" 10062306a36Sopenharmony_ci " mtmsr %3;\n" 10162306a36Sopenharmony_ci " isync;\n" 10262306a36Sopenharmony_ci "0: dcbst 0, %0;\n" 10362306a36Sopenharmony_ci " addi %0, %0, %4;\n" 10462306a36Sopenharmony_ci " bdnz 0b;\n" 10562306a36Sopenharmony_ci " sync;\n" 10662306a36Sopenharmony_ci " mtctr %2;\n" 10762306a36Sopenharmony_ci "1: icbi 0, %1;\n" 10862306a36Sopenharmony_ci " addi %1, %1, %4;\n" 10962306a36Sopenharmony_ci " bdnz 1b;\n" 11062306a36Sopenharmony_ci " sync;\n" 11162306a36Sopenharmony_ci " mtmsr %5;\n" 11262306a36Sopenharmony_ci " isync;\n" 11362306a36Sopenharmony_ci : "+&r" (loop1), "+&r" (loop2) 11462306a36Sopenharmony_ci : "r" (nb), "r" (msr), "i" (bytes), "r" (msr0) 11562306a36Sopenharmony_ci : "ctr", "memory"); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ciNOKPROBE_SYMBOL(flush_dcache_icache_phys) 11862306a36Sopenharmony_ci#else 11962306a36Sopenharmony_cistatic void flush_dcache_icache_phys(unsigned long physaddr) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci#endif 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/** 12562306a36Sopenharmony_ci * __flush_dcache_icache(): Flush a particular page from the data cache to RAM. 12662306a36Sopenharmony_ci * Note: this is necessary because the instruction cache does *not* 12762306a36Sopenharmony_ci * snoop from the data cache. 12862306a36Sopenharmony_ci * 12962306a36Sopenharmony_ci * @p: the address of the page to flush 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_cistatic void __flush_dcache_icache(void *p) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci unsigned long addr = (unsigned long)p & PAGE_MASK; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci clean_dcache_range(addr, addr + PAGE_SIZE); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* 13862306a36Sopenharmony_ci * We don't flush the icache on 44x. Those have a virtual icache and we 13962306a36Sopenharmony_ci * don't have access to the virtual address here (it's not the page 14062306a36Sopenharmony_ci * vaddr but where it's mapped in user space). The flushing of the 14162306a36Sopenharmony_ci * icache on these is handled elsewhere, when a change in the address 14262306a36Sopenharmony_ci * space occurs, before returning to user space. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (mmu_has_feature(MMU_FTR_TYPE_44x)) 14662306a36Sopenharmony_ci return; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci invalidate_icache_range(addr, addr + PAGE_SIZE); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_civoid flush_dcache_icache_folio(struct folio *folio) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci unsigned int i, nr = folio_nr_pages(folio); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (flush_coherent_icache()) 15662306a36Sopenharmony_ci return; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (!folio_test_highmem(folio)) { 15962306a36Sopenharmony_ci void *addr = folio_address(folio); 16062306a36Sopenharmony_ci for (i = 0; i < nr; i++) 16162306a36Sopenharmony_ci __flush_dcache_icache(addr + i * PAGE_SIZE); 16262306a36Sopenharmony_ci } else if (IS_ENABLED(CONFIG_BOOKE) || sizeof(phys_addr_t) > sizeof(void *)) { 16362306a36Sopenharmony_ci for (i = 0; i < nr; i++) { 16462306a36Sopenharmony_ci void *start = kmap_local_folio(folio, i * PAGE_SIZE); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci __flush_dcache_icache(start); 16762306a36Sopenharmony_ci kunmap_local(start); 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci } else { 17062306a36Sopenharmony_ci unsigned long pfn = folio_pfn(folio); 17162306a36Sopenharmony_ci for (i = 0; i < nr; i++) 17262306a36Sopenharmony_ci flush_dcache_icache_phys((pfn + i) * PAGE_SIZE); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ciEXPORT_SYMBOL(flush_dcache_icache_folio); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_civoid clear_user_page(void *page, unsigned long vaddr, struct page *pg) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci clear_page(page); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* 18262306a36Sopenharmony_ci * We shouldn't have to do this, but some versions of glibc 18362306a36Sopenharmony_ci * require it (ld.so assumes zero filled pages are icache clean) 18462306a36Sopenharmony_ci * - Anton 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci flush_dcache_page(pg); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ciEXPORT_SYMBOL(clear_user_page); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_civoid copy_user_page(void *vto, void *vfrom, unsigned long vaddr, 19162306a36Sopenharmony_ci struct page *pg) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci copy_page(vto, vfrom); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* 19662306a36Sopenharmony_ci * We should be able to use the following optimisation, however 19762306a36Sopenharmony_ci * there are two problems. 19862306a36Sopenharmony_ci * Firstly a bug in some versions of binutils meant PLT sections 19962306a36Sopenharmony_ci * were not marked executable. 20062306a36Sopenharmony_ci * Secondly the first word in the GOT section is blrl, used 20162306a36Sopenharmony_ci * to establish the GOT address. Until recently the GOT was 20262306a36Sopenharmony_ci * not marked executable. 20362306a36Sopenharmony_ci * - Anton 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ci#if 0 20662306a36Sopenharmony_ci if (!vma->vm_file && ((vma->vm_flags & VM_EXEC) == 0)) 20762306a36Sopenharmony_ci return; 20862306a36Sopenharmony_ci#endif 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci flush_dcache_page(pg); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_civoid flush_icache_user_page(struct vm_area_struct *vma, struct page *page, 21462306a36Sopenharmony_ci unsigned long addr, int len) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci void *maddr; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci maddr = kmap_local_page(page) + (addr & ~PAGE_MASK); 21962306a36Sopenharmony_ci flush_icache_range((unsigned long)maddr, (unsigned long)maddr + len); 22062306a36Sopenharmony_ci kunmap_local(maddr); 22162306a36Sopenharmony_ci} 222