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 60 ENTRY(memcpy) 61 cmp r0, r1 62 moveq pc, 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 74 1: 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] ) 87 2: 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 93 3: PLD( pld [r1, #124] ) 94 4: 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 101 5: 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 108 6: 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 140 7: ldmfd sp!, {r5 - r8} 141 142 8: 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, pc 151 152 9: rsb ip, ip, #4 153 cmp ip, #2 154 ldr1b r1, r3, gt, abort=21f 155 ldr1b r1, r4, ge, abort=21f 156 ldr1b r1, lr, abort=21f 157 str1b r0, r3, gt, abort=21f 158 str1b r0, r4, ge, abort=21f 159 subs r2, r2, ip 160 str1b r0, lr, abort=21f 161 blt 8b 162 ands ip, r1, #3 163 beq 1b 164 165 10: bic r1, r1, #3 166 cmp ip, #2 167 ldr1w r1, lr, abort=21f 168 beq 17f 169 bgt 18f 170 171 172 .macro forward_copy_shift pull push 173 174 subs r2, r2, #28 175 blt 14f 176 177 CALGN( ands ip, r0, #31 ) 178 CALGN( rsb ip, ip, #32 ) 179 CALGN( sbcsne r4, ip, r2 ) @ C is always set here 180 CALGN( subcc r2, r2, ip ) 181 CALGN( bcc 15f ) 182 183 11: stmfd sp!, {r5 - r9} 184 185 PLD( pld [r1, #0] ) 186 PLD( subs r2, r2, #96 ) 187 PLD( pld [r1, #28] ) 188 PLD( blt 13f ) 189 PLD( pld [r1, #60] ) 190 PLD( pld [r1, #92] ) 191 192 12: PLD( pld [r1, #124] ) 193 13: ldr4w r1, r4, r5, r6, r7, abort=19f 194 mov r3, lr, lspull #\pull 195 subs r2, r2, #32 196 ldr4w r1, r8, r9, ip, lr, abort=19f 197 orr r3, r3, r4, lspush #\push 198 mov r4, r4, lspull #\pull 199 orr r4, r4, r5, lspush #\push 200 mov r5, r5, lspull #\pull 201 orr r5, r5, r6, lspush #\push 202 mov r6, r6, lspull #\pull 203 orr r6, r6, r7, lspush #\push 204 mov r7, r7, lspull #\pull 205 orr r7, r7, r8, lspush #\push 206 mov r8, r8, lspull #\pull 207 orr r8, r8, r9, lspush #\push 208 mov r9, r9, lspull #\pull 209 orr r9, r9, ip, lspush #\push 210 mov ip, ip, lspull #\pull 211 orr ip, ip, lr, lspush #\push 212 str8w r0, r3, r4, r5, r6, r7, r8, r9, ip, , abort=19f 213 bge 12b 214 PLD( cmn r2, #96 ) 215 PLD( bge 13b ) 216 217 ldmfd sp!, {r5 - r9} 218 219 14: ands ip, r2, #28 220 beq 16f 221 222 15: mov r3, lr, lspull #\pull 223 ldr1w r1, lr, abort=21f 224 subs ip, ip, #4 225 orr r3, r3, lr, lspush #\push 226 str1w r0, r3, abort=21f 227 bgt 15b 228 CALGN( cmp r2, #0 ) 229 CALGN( bge 11b ) 230 231 16: sub r1, r1, #(\push / 8) 232 b 8b 233 234 .endm 235 236 237 forward_copy_shift pull=8 push=24 238 239 17: forward_copy_shift pull=16 push=16 240 241 18: forward_copy_shift pull=24 push=8 242 243 244 /* 245 * Abort preamble and completion macros. 246 * If a fixup handler is required then those macros must surround it. 247 * It is assumed that the fixup code will handle the private part of 248 * the exit macro. 249 */ 250 251 .macro copy_abort_preamble 252 19: ldmfd sp!, {r5 - r9} 253 b 21f 254 20: ldmfd sp!, {r5 - r8} 255 21: 256 .endm 257 258 .macro copy_abort_end 259 ldmfd sp!, {r4, pc} 260 .endm 261 262 ENDPROC(memcpy) 263