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