1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 *  linux/arch/arm/lib/memcpy.S
4 *
5 *  Author:	Nicolas Pitre
6 *  Created:	Sep 28, 2005
7 *  Copyright:	MontaVista Software, Inc.
8 */
9
10#include <linux/linkage.h>
11#include <asm/assembler.h>
12
13#define LDR1W_SHIFT	0
14#define STR1W_SHIFT	0
15
16	.macro ldr1w ptr reg abort
17	W(ldr) \reg, [\ptr], #4
18	.endm
19
20	.macro ldr4w ptr reg1 reg2 reg3 reg4 abort
21	ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4}
22	.endm
23
24	.macro ldr8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort
25	ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8}
26	.endm
27
28	.macro ldr1b ptr reg cond=al abort
29	ldrb\cond\() \reg, [\ptr], #1
30	.endm
31
32	.macro str1w ptr reg abort
33	W(str) \reg, [\ptr], #4
34	.endm
35
36	.macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort
37	stmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8}
38	.endm
39
40	.macro str1b ptr reg cond=al abort
41	strb\cond\() \reg, [\ptr], #1
42	.endm
43
44	.macro enter reg1 reg2
45	stmdb sp!, {r0, \reg1, \reg2}
46	.endm
47
48	.macro exit reg1 reg2
49	ldmfd sp!, {r0, \reg1, \reg2}
50	.endm
51
52	.text
53
54/* Prototype: void *memcpy(void *dest, const void *src, size_t n); */
55	.syntax unified
56#if CONFIG_IS_ENABLED(SYS_THUMB_BUILD) && !defined(MEMCPY_NO_THUMB_BUILD)
57	.thumb
58	.thumb_func
59#endif
60ENTRY(memcpy)
61		cmp	r0, r1
62		bxeq	lr
63
64		enter	r4, lr
65
66		subs	r2, r2, #4
67		blt	8f
68		ands	ip, r0, #3
69	PLD(	pld	[r1, #0]		)
70		bne	9f
71		ands	ip, r1, #3
72		bne	10f
73
741:		subs	r2, r2, #(28)
75		stmfd	sp!, {r5 - r8}
76		blt	5f
77
78	CALGN(	ands	ip, r0, #31		)
79	CALGN(	rsb	r3, ip, #32		)
80	CALGN(	sbcsne	r4, r3, r2		)  @ C is always set here
81	CALGN(	bcs	2f			)
82	CALGN(	adr	r4, 6f			)
83	CALGN(	subs	r2, r2, r3		)  @ C gets set
84	CALGN(	add	pc, r4, ip		)
85
86	PLD(	pld	[r1, #0]		)
872:	PLD(	subs	r2, r2, #96		)
88	PLD(	pld	[r1, #28]		)
89	PLD(	blt	4f			)
90	PLD(	pld	[r1, #60]		)
91	PLD(	pld	[r1, #92]		)
92
933:	PLD(	pld	[r1, #124]		)
944:		ldr8w	r1, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f
95		subs	r2, r2, #32
96		str8w	r0, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f
97		bge	3b
98	PLD(	cmn	r2, #96			)
99	PLD(	bge	4b			)
100
1015:		ands	ip, r2, #28
102		rsb	ip, ip, #32
103#if LDR1W_SHIFT > 0
104		lsl	ip, ip, #LDR1W_SHIFT
105#endif
106		addne	pc, pc, ip		@ C is always clear here
107		b	7f
1086:
109		.rept	(1 << LDR1W_SHIFT)
110		W(nop)
111		.endr
112		ldr1w	r1, r3, abort=20f
113		ldr1w	r1, r4, abort=20f
114		ldr1w	r1, r5, abort=20f
115		ldr1w	r1, r6, abort=20f
116		ldr1w	r1, r7, abort=20f
117		ldr1w	r1, r8, abort=20f
118		ldr1w	r1, lr, abort=20f
119
120#if LDR1W_SHIFT < STR1W_SHIFT
121		lsl	ip, ip, #STR1W_SHIFT - LDR1W_SHIFT
122#elif LDR1W_SHIFT > STR1W_SHIFT
123		lsr	ip, ip, #LDR1W_SHIFT - STR1W_SHIFT
124#endif
125		add	pc, pc, ip
126		nop
127		.rept	(1 << STR1W_SHIFT)
128		W(nop)
129		.endr
130		str1w	r0, r3, abort=20f
131		str1w	r0, r4, abort=20f
132		str1w	r0, r5, abort=20f
133		str1w	r0, r6, abort=20f
134		str1w	r0, r7, abort=20f
135		str1w	r0, r8, abort=20f
136		str1w	r0, lr, abort=20f
137
138	CALGN(	bcs	2b			)
139
1407:		ldmfd	sp!, {r5 - r8}
141
1428:		movs	r2, r2, lsl #31
143		ldr1b	r1, r3, ne, abort=21f
144		ldr1b	r1, r4, cs, abort=21f
145		ldr1b	r1, ip, cs, abort=21f
146		str1b	r0, r3, ne, abort=21f
147		str1b	r0, r4, cs, abort=21f
148		str1b	r0, ip, cs, abort=21f
149
150		exit	r4, lr
151		bx	lr
152
1539:		rsb	ip, ip, #4
154		cmp	ip, #2
155		ldr1b	r1, r3, gt, abort=21f
156		ldr1b	r1, r4, ge, abort=21f
157		ldr1b	r1, lr, abort=21f
158		str1b	r0, r3, gt, abort=21f
159		str1b	r0, r4, ge, abort=21f
160		subs	r2, r2, ip
161		str1b	r0, lr, abort=21f
162		blt	8b
163		ands	ip, r1, #3
164		beq	1b
165
16610:		bic	r1, r1, #3
167		cmp	ip, #2
168		ldr1w	r1, lr, abort=21f
169		beq	17f
170		bgt	18f
171
172
173		.macro	forward_copy_shift pull push
174
175		subs	r2, r2, #28
176		blt	14f
177
178	CALGN(	ands	ip, r0, #31		)
179	CALGN(	rsb	ip, ip, #32		)
180	CALGN(	sbcsne	r4, ip, r2		)  @ C is always set here
181	CALGN(	subcc	r2, r2, ip		)
182	CALGN(	bcc	15f			)
183
18411:		stmfd	sp!, {r5 - r9}
185
186	PLD(	pld	[r1, #0]		)
187	PLD(	subs	r2, r2, #96		)
188	PLD(	pld	[r1, #28]		)
189	PLD(	blt	13f			)
190	PLD(	pld	[r1, #60]		)
191	PLD(	pld	[r1, #92]		)
192
19312:	PLD(	pld	[r1, #124]		)
19413:		ldr4w	r1, r4, r5, r6, r7, abort=19f
195		mov	r3, lr, lspull #\pull
196		subs	r2, r2, #32
197		ldr4w	r1, r8, r9, ip, lr, abort=19f
198		orr	r3, r3, r4, lspush #\push
199		mov	r4, r4, lspull #\pull
200		orr	r4, r4, r5, lspush #\push
201		mov	r5, r5, lspull #\pull
202		orr	r5, r5, r6, lspush #\push
203		mov	r6, r6, lspull #\pull
204		orr	r6, r6, r7, lspush #\push
205		mov	r7, r7, lspull #\pull
206		orr	r7, r7, r8, lspush #\push
207		mov	r8, r8, lspull #\pull
208		orr	r8, r8, r9, lspush #\push
209		mov	r9, r9, lspull #\pull
210		orr	r9, r9, ip, lspush #\push
211		mov	ip, ip, lspull #\pull
212		orr	ip, ip, lr, lspush #\push
213		str8w	r0, r3, r4, r5, r6, r7, r8, r9, ip, , abort=19f
214		bge	12b
215	PLD(	cmn	r2, #96			)
216	PLD(	bge	13b			)
217
218		ldmfd	sp!, {r5 - r9}
219
22014:		ands	ip, r2, #28
221		beq	16f
222
22315:		mov	r3, lr, lspull #\pull
224		ldr1w	r1, lr, abort=21f
225		subs	ip, ip, #4
226		orr	r3, r3, lr, lspush #\push
227		str1w	r0, r3, abort=21f
228		bgt	15b
229	CALGN(	cmp	r2, #0			)
230	CALGN(	bge	11b			)
231
23216:		sub	r1, r1, #(\push / 8)
233		b	8b
234
235		.endm
236
237
238		forward_copy_shift	pull=8	push=24
239
24017:		forward_copy_shift	pull=16	push=16
241
24218:		forward_copy_shift	pull=24	push=8
243
244
245/*
246 * Abort preamble and completion macros.
247 * If a fixup handler is required then those macros must surround it.
248 * It is assumed that the fixup code will handle the private part of
249 * the exit macro.
250 */
251
252	.macro	copy_abort_preamble
25319:	ldmfd	sp!, {r5 - r9}
254	b	21f
25520:	ldmfd	sp!, {r5 - r8}
25621:
257	.endm
258
259	.macro	copy_abort_end
260	ldmfd	sp!, {r4, lr}
261	bx	lr
262	.endm
263
264ENDPROC(memcpy)
265