162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/arch/arm/mm/copypage-v4wb.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 1995-1999 Russell King
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include <linux/init.h>
862306a36Sopenharmony_ci#include <linux/highmem.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/*
1162306a36Sopenharmony_ci * ARMv4 optimised copy_user_highpage
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * We flush the destination cache lines just before we write the data into the
1462306a36Sopenharmony_ci * corresponding address.  Since the Dcache is read-allocate, this removes the
1562306a36Sopenharmony_ci * Dcache aliasing issue.  The writes will be forwarded to the write buffer,
1662306a36Sopenharmony_ci * and merged as appropriate.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * Note: We rely on all ARMv4 processors implementing the "invalidate D line"
1962306a36Sopenharmony_ci * instruction.  If your processor does not supply this, you have to write your
2062306a36Sopenharmony_ci * own copy_user_highpage that does the right thing.
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_cistatic void v4wb_copy_user_page(void *kto, const void *kfrom)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	int tmp;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	asm volatile ("\
2762306a36Sopenharmony_ci	.syntax unified\n\
2862306a36Sopenharmony_ci	ldmia	%1!, {r3, r4, ip, lr}		@ 4\n\
2962306a36Sopenharmony_ci1:	mcr	p15, 0, %0, c7, c6, 1		@ 1   invalidate D line\n\
3062306a36Sopenharmony_ci	stmia	%0!, {r3, r4, ip, lr}		@ 4\n\
3162306a36Sopenharmony_ci	ldmia	%1!, {r3, r4, ip, lr}		@ 4+1\n\
3262306a36Sopenharmony_ci	stmia	%0!, {r3, r4, ip, lr}		@ 4\n\
3362306a36Sopenharmony_ci	ldmia	%1!, {r3, r4, ip, lr}		@ 4\n\
3462306a36Sopenharmony_ci	mcr	p15, 0, %0, c7, c6, 1		@ 1   invalidate D line\n\
3562306a36Sopenharmony_ci	stmia	%0!, {r3, r4, ip, lr}		@ 4\n\
3662306a36Sopenharmony_ci	ldmia	%1!, {r3, r4, ip, lr}		@ 4\n\
3762306a36Sopenharmony_ci	subs	%2, %2, #1			@ 1\n\
3862306a36Sopenharmony_ci	stmia	%0!, {r3, r4, ip, lr}		@ 4\n\
3962306a36Sopenharmony_ci	ldmiane	%1!, {r3, r4, ip, lr}		@ 4\n\
4062306a36Sopenharmony_ci	bne	1b				@ 1\n\
4162306a36Sopenharmony_ci	mcr	p15, 0, %1, c7, c10, 4		@ 1   drain WB"
4262306a36Sopenharmony_ci	: "+&r" (kto), "+&r" (kfrom), "=&r" (tmp)
4362306a36Sopenharmony_ci	: "2" (PAGE_SIZE / 64)
4462306a36Sopenharmony_ci	: "r3", "r4", "ip", "lr");
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_civoid v4wb_copy_user_highpage(struct page *to, struct page *from,
4862306a36Sopenharmony_ci	unsigned long vaddr, struct vm_area_struct *vma)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	void *kto, *kfrom;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	kto = kmap_atomic(to);
5362306a36Sopenharmony_ci	kfrom = kmap_atomic(from);
5462306a36Sopenharmony_ci	flush_cache_page(vma, vaddr, page_to_pfn(from));
5562306a36Sopenharmony_ci	v4wb_copy_user_page(kto, kfrom);
5662306a36Sopenharmony_ci	kunmap_atomic(kfrom);
5762306a36Sopenharmony_ci	kunmap_atomic(kto);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/*
6162306a36Sopenharmony_ci * ARMv4 optimised clear_user_page
6262306a36Sopenharmony_ci *
6362306a36Sopenharmony_ci * Same story as above.
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_civoid v4wb_clear_user_highpage(struct page *page, unsigned long vaddr)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	void *ptr, *kaddr = kmap_atomic(page);
6862306a36Sopenharmony_ci	asm volatile("\
6962306a36Sopenharmony_ci	mov	r1, %2				@ 1\n\
7062306a36Sopenharmony_ci	mov	r2, #0				@ 1\n\
7162306a36Sopenharmony_ci	mov	r3, #0				@ 1\n\
7262306a36Sopenharmony_ci	mov	ip, #0				@ 1\n\
7362306a36Sopenharmony_ci	mov	lr, #0				@ 1\n\
7462306a36Sopenharmony_ci1:	mcr	p15, 0, %0, c7, c6, 1		@ 1   invalidate D line\n\
7562306a36Sopenharmony_ci	stmia	%0!, {r2, r3, ip, lr}		@ 4\n\
7662306a36Sopenharmony_ci	stmia	%0!, {r2, r3, ip, lr}		@ 4\n\
7762306a36Sopenharmony_ci	mcr	p15, 0, %0, c7, c6, 1		@ 1   invalidate D line\n\
7862306a36Sopenharmony_ci	stmia	%0!, {r2, r3, ip, lr}		@ 4\n\
7962306a36Sopenharmony_ci	stmia	%0!, {r2, r3, ip, lr}		@ 4\n\
8062306a36Sopenharmony_ci	subs	r1, r1, #1			@ 1\n\
8162306a36Sopenharmony_ci	bne	1b				@ 1\n\
8262306a36Sopenharmony_ci	mcr	p15, 0, r1, c7, c10, 4		@ 1   drain WB"
8362306a36Sopenharmony_ci	: "=r" (ptr)
8462306a36Sopenharmony_ci	: "0" (kaddr), "I" (PAGE_SIZE / 64)
8562306a36Sopenharmony_ci	: "r1", "r2", "r3", "ip", "lr");
8662306a36Sopenharmony_ci	kunmap_atomic(kaddr);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistruct cpu_user_fns v4wb_user_fns __initdata = {
9062306a36Sopenharmony_ci	.cpu_clear_user_highpage = v4wb_clear_user_highpage,
9162306a36Sopenharmony_ci	.cpu_copy_user_highpage	= v4wb_copy_user_highpage,
9262306a36Sopenharmony_ci};
93