162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-or-later */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) IBM Corporation, 2012
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Author: Anton Blanchard <anton@au.ibm.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/export.h>
1062306a36Sopenharmony_ci#include <asm/ppc_asm.h>
1162306a36Sopenharmony_ci#include <asm/linkage.h>
1262306a36Sopenharmony_ci#include <asm/asm-offsets.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/**
1562306a36Sopenharmony_ci * __arch_clear_user: - Zero a block of memory in user space, with less checking.
1662306a36Sopenharmony_ci * @to:   Destination address, in user space.
1762306a36Sopenharmony_ci * @n:    Number of bytes to zero.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * Zero a block of memory in user space.  Caller must check
2062306a36Sopenharmony_ci * the specified block with access_ok() before calling this function.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * Returns number of bytes that could not be cleared.
2362306a36Sopenharmony_ci * On success, this will be zero.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	.macro err1
2762306a36Sopenharmony_ci100:
2862306a36Sopenharmony_ci	EX_TABLE(100b,.Ldo_err1)
2962306a36Sopenharmony_ci	.endm
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	.macro err2
3262306a36Sopenharmony_ci200:
3362306a36Sopenharmony_ci	EX_TABLE(200b,.Ldo_err2)
3462306a36Sopenharmony_ci	.endm
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	.macro err3
3762306a36Sopenharmony_ci300:
3862306a36Sopenharmony_ci	EX_TABLE(300b,.Ldo_err3)
3962306a36Sopenharmony_ci	.endm
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci.Ldo_err1:
4262306a36Sopenharmony_ci	mr	r3,r8
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci.Ldo_err2:
4562306a36Sopenharmony_ci	mtctr	r4
4662306a36Sopenharmony_ci1:
4762306a36Sopenharmony_cierr3;	stb	r0,0(r3)
4862306a36Sopenharmony_ci	addi	r3,r3,1
4962306a36Sopenharmony_ci	addi	r4,r4,-1
5062306a36Sopenharmony_ci	bdnz	1b
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci.Ldo_err3:
5362306a36Sopenharmony_ci	mr	r3,r4
5462306a36Sopenharmony_ci	blr
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci_GLOBAL_TOC(__arch_clear_user)
5762306a36Sopenharmony_ci	cmpdi	r4,32
5862306a36Sopenharmony_ci	neg	r6,r3
5962306a36Sopenharmony_ci	li	r0,0
6062306a36Sopenharmony_ci	blt	.Lshort_clear
6162306a36Sopenharmony_ci	mr	r8,r3
6262306a36Sopenharmony_ci	mtocrf	0x01,r6
6362306a36Sopenharmony_ci	clrldi	r6,r6,(64-3)
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	/* Get the destination 8 byte aligned */
6662306a36Sopenharmony_ci	bf	cr7*4+3,1f
6762306a36Sopenharmony_cierr1;	stb	r0,0(r3)
6862306a36Sopenharmony_ci	addi	r3,r3,1
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci1:	bf	cr7*4+2,2f
7162306a36Sopenharmony_cierr1;	sth	r0,0(r3)
7262306a36Sopenharmony_ci	addi	r3,r3,2
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci2:	bf	cr7*4+1,3f
7562306a36Sopenharmony_cierr1;	stw	r0,0(r3)
7662306a36Sopenharmony_ci	addi	r3,r3,4
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci3:	sub	r4,r4,r6
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	cmpdi	r4,32
8162306a36Sopenharmony_ci	cmpdi	cr1,r4,512
8262306a36Sopenharmony_ci	blt	.Lshort_clear
8362306a36Sopenharmony_ci	bgt	cr1,.Llong_clear
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci.Lmedium_clear:
8662306a36Sopenharmony_ci	srdi	r6,r4,5
8762306a36Sopenharmony_ci	mtctr	r6
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* Do 32 byte chunks */
9062306a36Sopenharmony_ci4:
9162306a36Sopenharmony_cierr2;	std	r0,0(r3)
9262306a36Sopenharmony_cierr2;	std	r0,8(r3)
9362306a36Sopenharmony_cierr2;	std	r0,16(r3)
9462306a36Sopenharmony_cierr2;	std	r0,24(r3)
9562306a36Sopenharmony_ci	addi	r3,r3,32
9662306a36Sopenharmony_ci	addi	r4,r4,-32
9762306a36Sopenharmony_ci	bdnz	4b
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci.Lshort_clear:
10062306a36Sopenharmony_ci	/* up to 31 bytes to go */
10162306a36Sopenharmony_ci	cmpdi	r4,16
10262306a36Sopenharmony_ci	blt	6f
10362306a36Sopenharmony_cierr2;	std	r0,0(r3)
10462306a36Sopenharmony_cierr2;	std	r0,8(r3)
10562306a36Sopenharmony_ci	addi	r3,r3,16
10662306a36Sopenharmony_ci	addi	r4,r4,-16
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/* Up to 15 bytes to go */
10962306a36Sopenharmony_ci6:	mr	r8,r3
11062306a36Sopenharmony_ci	clrldi	r4,r4,(64-4)
11162306a36Sopenharmony_ci	mtocrf	0x01,r4
11262306a36Sopenharmony_ci	bf	cr7*4+0,7f
11362306a36Sopenharmony_cierr1;	std	r0,0(r3)
11462306a36Sopenharmony_ci	addi	r3,r3,8
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci7:	bf	cr7*4+1,8f
11762306a36Sopenharmony_cierr1;	stw	r0,0(r3)
11862306a36Sopenharmony_ci	addi	r3,r3,4
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci8:	bf	cr7*4+2,9f
12162306a36Sopenharmony_cierr1;	sth	r0,0(r3)
12262306a36Sopenharmony_ci	addi	r3,r3,2
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci9:	bf	cr7*4+3,10f
12562306a36Sopenharmony_cierr1;	stb	r0,0(r3)
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci10:	li	r3,0
12862306a36Sopenharmony_ci	blr
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci.Llong_clear:
13162306a36Sopenharmony_ci	LOAD_REG_ADDR(r5, ppc64_caches)
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	bf	cr7*4+0,11f
13462306a36Sopenharmony_cierr2;	std	r0,0(r3)
13562306a36Sopenharmony_ci	addi	r3,r3,8
13662306a36Sopenharmony_ci	addi	r4,r4,-8
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* Destination is 16 byte aligned, need to get it cache block aligned */
13962306a36Sopenharmony_ci11:	lwz	r7,DCACHEL1LOGBLOCKSIZE(r5)
14062306a36Sopenharmony_ci	lwz	r9,DCACHEL1BLOCKSIZE(r5)
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/*
14362306a36Sopenharmony_ci	 * With worst case alignment the long clear loop takes a minimum
14462306a36Sopenharmony_ci	 * of 1 byte less than 2 cachelines.
14562306a36Sopenharmony_ci	 */
14662306a36Sopenharmony_ci	sldi	r10,r9,2
14762306a36Sopenharmony_ci	cmpd	r4,r10
14862306a36Sopenharmony_ci	blt	.Lmedium_clear
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	neg	r6,r3
15162306a36Sopenharmony_ci	addi	r10,r9,-1
15262306a36Sopenharmony_ci	and.	r5,r6,r10
15362306a36Sopenharmony_ci	beq	13f
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	srdi	r6,r5,4
15662306a36Sopenharmony_ci	mtctr	r6
15762306a36Sopenharmony_ci	mr	r8,r3
15862306a36Sopenharmony_ci12:
15962306a36Sopenharmony_cierr1;	std	r0,0(r3)
16062306a36Sopenharmony_cierr1;	std	r0,8(r3)
16162306a36Sopenharmony_ci	addi	r3,r3,16
16262306a36Sopenharmony_ci	bdnz	12b
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	sub	r4,r4,r5
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci13:	srd	r6,r4,r7
16762306a36Sopenharmony_ci	mtctr	r6
16862306a36Sopenharmony_ci	mr	r8,r3
16962306a36Sopenharmony_ci14:
17062306a36Sopenharmony_cierr1;	dcbz	0,r3
17162306a36Sopenharmony_ci	add	r3,r3,r9
17262306a36Sopenharmony_ci	bdnz	14b
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	and	r4,r4,r10
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	cmpdi	r4,32
17762306a36Sopenharmony_ci	blt	.Lshort_clear
17862306a36Sopenharmony_ci	b	.Lmedium_clear
17962306a36Sopenharmony_ciEXPORT_SYMBOL(__arch_clear_user)
180