162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/arch/arm/lib/memmove.S
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Author:	Nicolas Pitre
662306a36Sopenharmony_ci *  Created:	Sep 28, 2005
762306a36Sopenharmony_ci *  Copyright:	(C) MontaVista Software Inc.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/linkage.h>
1162306a36Sopenharmony_ci#include <asm/assembler.h>
1262306a36Sopenharmony_ci#include <asm/unwind.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci		.text
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/*
1762306a36Sopenharmony_ci * Prototype: void *memmove(void *dest, const void *src, size_t n);
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * Note:
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * If the memory regions don't overlap, we simply branch to memcpy which is
2262306a36Sopenharmony_ci * normally a bit faster. Otherwise the copy is done going downwards.  This
2362306a36Sopenharmony_ci * is a transposition of the code from copy_template.S but with the copy
2462306a36Sopenharmony_ci * occurring in the opposite direction.
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ciENTRY(__memmove)
2862306a36Sopenharmony_ciWEAK(memmove)
2962306a36Sopenharmony_ci	UNWIND(	.fnstart			)
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci		subs	ip, r0, r1
3262306a36Sopenharmony_ci		cmphi	r2, ip
3362306a36Sopenharmony_ci		bls	__memcpy
3462306a36Sopenharmony_ci	UNWIND(	.fnend				)
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	UNWIND(	.fnstart			)
3762306a36Sopenharmony_ci	UNWIND(	.save	{r0, r4, fpreg, lr}	)
3862306a36Sopenharmony_ci		stmfd	sp!, {r0, r4, UNWIND(fpreg,) lr}
3962306a36Sopenharmony_ci	UNWIND(	.setfp	fpreg, sp		)
4062306a36Sopenharmony_ci	UNWIND(	mov	fpreg, sp		)
4162306a36Sopenharmony_ci		add	r1, r1, r2
4262306a36Sopenharmony_ci		add	r0, r0, r2
4362306a36Sopenharmony_ci		subs	r2, r2, #4
4462306a36Sopenharmony_ci		blt	8f
4562306a36Sopenharmony_ci		ands	ip, r0, #3
4662306a36Sopenharmony_ci	PLD(	pld	[r1, #-4]		)
4762306a36Sopenharmony_ci		bne	9f
4862306a36Sopenharmony_ci		ands	ip, r1, #3
4962306a36Sopenharmony_ci		bne	10f
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci1:		subs	r2, r2, #(28)
5262306a36Sopenharmony_ci		stmfd	sp!, {r5, r6, r8, r9}
5362306a36Sopenharmony_ci		blt	5f
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	CALGN(	ands	ip, r0, #31		)
5662306a36Sopenharmony_ci	CALGN(	sbcsne	r4, ip, r2		)  @ C is always set here
5762306a36Sopenharmony_ci	CALGN(	bcs	2f			)
5862306a36Sopenharmony_ci	CALGN(	adr	r4, 6f			)
5962306a36Sopenharmony_ci	CALGN(	subs	r2, r2, ip		)  @ C is set here
6062306a36Sopenharmony_ci	CALGN(	rsb	ip, ip, #32		)
6162306a36Sopenharmony_ci	CALGN(	add	pc, r4, ip		)
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	PLD(	pld	[r1, #-4]		)
6462306a36Sopenharmony_ci2:	PLD(	subs	r2, r2, #96		)
6562306a36Sopenharmony_ci	PLD(	pld	[r1, #-32]		)
6662306a36Sopenharmony_ci	PLD(	blt	4f			)
6762306a36Sopenharmony_ci	PLD(	pld	[r1, #-64]		)
6862306a36Sopenharmony_ci	PLD(	pld	[r1, #-96]		)
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci3:	PLD(	pld	[r1, #-128]		)
7162306a36Sopenharmony_ci4:		ldmdb	r1!, {r3, r4, r5, r6, r8, r9, ip, lr}
7262306a36Sopenharmony_ci		subs	r2, r2, #32
7362306a36Sopenharmony_ci		stmdb	r0!, {r3, r4, r5, r6, r8, r9, ip, lr}
7462306a36Sopenharmony_ci		bge	3b
7562306a36Sopenharmony_ci	PLD(	cmn	r2, #96			)
7662306a36Sopenharmony_ci	PLD(	bge	4b			)
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci5:		ands	ip, r2, #28
7962306a36Sopenharmony_ci		rsb	ip, ip, #32
8062306a36Sopenharmony_ci		addne	pc, pc, ip		@ C is always clear here
8162306a36Sopenharmony_ci		b	7f
8262306a36Sopenharmony_ci6:		W(nop)
8362306a36Sopenharmony_ci		W(ldr)	r3, [r1, #-4]!
8462306a36Sopenharmony_ci		W(ldr)	r4, [r1, #-4]!
8562306a36Sopenharmony_ci		W(ldr)	r5, [r1, #-4]!
8662306a36Sopenharmony_ci		W(ldr)	r6, [r1, #-4]!
8762306a36Sopenharmony_ci		W(ldr)	r8, [r1, #-4]!
8862306a36Sopenharmony_ci		W(ldr)	r9, [r1, #-4]!
8962306a36Sopenharmony_ci		W(ldr)	lr, [r1, #-4]!
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci		add	pc, pc, ip
9262306a36Sopenharmony_ci		nop
9362306a36Sopenharmony_ci		W(nop)
9462306a36Sopenharmony_ci		W(str)	r3, [r0, #-4]!
9562306a36Sopenharmony_ci		W(str)	r4, [r0, #-4]!
9662306a36Sopenharmony_ci		W(str)	r5, [r0, #-4]!
9762306a36Sopenharmony_ci		W(str)	r6, [r0, #-4]!
9862306a36Sopenharmony_ci		W(str)	r8, [r0, #-4]!
9962306a36Sopenharmony_ci		W(str)	r9, [r0, #-4]!
10062306a36Sopenharmony_ci		W(str)	lr, [r0, #-4]!
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	CALGN(	bcs	2b			)
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci7:		ldmfd	sp!, {r5, r6, r8, r9}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci8:		movs	r2, r2, lsl #31
10762306a36Sopenharmony_ci		ldrbne	r3, [r1, #-1]!
10862306a36Sopenharmony_ci		ldrbcs	r4, [r1, #-1]!
10962306a36Sopenharmony_ci		ldrbcs	ip, [r1, #-1]
11062306a36Sopenharmony_ci		strbne	r3, [r0, #-1]!
11162306a36Sopenharmony_ci		strbcs	r4, [r0, #-1]!
11262306a36Sopenharmony_ci		strbcs	ip, [r0, #-1]
11362306a36Sopenharmony_ci		ldmfd	sp!, {r0, r4, UNWIND(fpreg,) pc}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci9:		cmp	ip, #2
11662306a36Sopenharmony_ci		ldrbgt	r3, [r1, #-1]!
11762306a36Sopenharmony_ci		ldrbge	r4, [r1, #-1]!
11862306a36Sopenharmony_ci		ldrb	lr, [r1, #-1]!
11962306a36Sopenharmony_ci		strbgt	r3, [r0, #-1]!
12062306a36Sopenharmony_ci		strbge	r4, [r0, #-1]!
12162306a36Sopenharmony_ci		subs	r2, r2, ip
12262306a36Sopenharmony_ci		strb	lr, [r0, #-1]!
12362306a36Sopenharmony_ci		blt	8b
12462306a36Sopenharmony_ci		ands	ip, r1, #3
12562306a36Sopenharmony_ci		beq	1b
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci10:		bic	r1, r1, #3
12862306a36Sopenharmony_ci		cmp	ip, #2
12962306a36Sopenharmony_ci		ldr	r3, [r1, #0]
13062306a36Sopenharmony_ci		beq	17f
13162306a36Sopenharmony_ci		blt	18f
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		.macro	backward_copy_shift push pull
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		subs	r2, r2, #28
13762306a36Sopenharmony_ci		blt	14f
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	CALGN(	ands	ip, r0, #31		)
14062306a36Sopenharmony_ci	CALGN(	sbcsne	r4, ip, r2		)  @ C is always set here
14162306a36Sopenharmony_ci	CALGN(	subcc	r2, r2, ip		)
14262306a36Sopenharmony_ci	CALGN(	bcc	15f			)
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci11:		stmfd	sp!, {r5, r6, r8 - r10}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	PLD(	pld	[r1, #-4]		)
14762306a36Sopenharmony_ci	PLD(	subs	r2, r2, #96		)
14862306a36Sopenharmony_ci	PLD(	pld	[r1, #-32]		)
14962306a36Sopenharmony_ci	PLD(	blt	13f			)
15062306a36Sopenharmony_ci	PLD(	pld	[r1, #-64]		)
15162306a36Sopenharmony_ci	PLD(	pld	[r1, #-96]		)
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci12:	PLD(	pld	[r1, #-128]		)
15462306a36Sopenharmony_ci13:		ldmdb   r1!, {r8, r9, r10, ip}
15562306a36Sopenharmony_ci		mov     lr, r3, lspush #\push
15662306a36Sopenharmony_ci		subs    r2, r2, #32
15762306a36Sopenharmony_ci		ldmdb   r1!, {r3, r4, r5, r6}
15862306a36Sopenharmony_ci		orr     lr, lr, ip, lspull #\pull
15962306a36Sopenharmony_ci		mov     ip, ip, lspush #\push
16062306a36Sopenharmony_ci		orr     ip, ip, r10, lspull #\pull
16162306a36Sopenharmony_ci		mov     r10, r10, lspush #\push
16262306a36Sopenharmony_ci		orr     r10, r10, r9, lspull #\pull
16362306a36Sopenharmony_ci		mov     r9, r9, lspush #\push
16462306a36Sopenharmony_ci		orr     r9, r9, r8, lspull #\pull
16562306a36Sopenharmony_ci		mov     r8, r8, lspush #\push
16662306a36Sopenharmony_ci		orr     r8, r8, r6, lspull #\pull
16762306a36Sopenharmony_ci		mov     r6, r6, lspush #\push
16862306a36Sopenharmony_ci		orr     r6, r6, r5, lspull #\pull
16962306a36Sopenharmony_ci		mov     r5, r5, lspush #\push
17062306a36Sopenharmony_ci		orr     r5, r5, r4, lspull #\pull
17162306a36Sopenharmony_ci		mov     r4, r4, lspush #\push
17262306a36Sopenharmony_ci		orr     r4, r4, r3, lspull #\pull
17362306a36Sopenharmony_ci		stmdb   r0!, {r4 - r6, r8 - r10, ip, lr}
17462306a36Sopenharmony_ci		bge	12b
17562306a36Sopenharmony_ci	PLD(	cmn	r2, #96			)
17662306a36Sopenharmony_ci	PLD(	bge	13b			)
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci		ldmfd	sp!, {r5, r6, r8 - r10}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci14:		ands	ip, r2, #28
18162306a36Sopenharmony_ci		beq	16f
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci15:		mov     lr, r3, lspush #\push
18462306a36Sopenharmony_ci		ldr	r3, [r1, #-4]!
18562306a36Sopenharmony_ci		subs	ip, ip, #4
18662306a36Sopenharmony_ci		orr	lr, lr, r3, lspull #\pull
18762306a36Sopenharmony_ci		str	lr, [r0, #-4]!
18862306a36Sopenharmony_ci		bgt	15b
18962306a36Sopenharmony_ci	CALGN(	cmp	r2, #0			)
19062306a36Sopenharmony_ci	CALGN(	bge	11b			)
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci16:		add	r1, r1, #(\pull / 8)
19362306a36Sopenharmony_ci		b	8b
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci		.endm
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		backward_copy_shift	push=8	pull=24
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci17:		backward_copy_shift	push=16	pull=16
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci18:		backward_copy_shift	push=24	pull=8
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	UNWIND(	.fnend				)
20562306a36Sopenharmony_ciENDPROC(memmove)
20662306a36Sopenharmony_ciENDPROC(__memmove)
207