1/* SPDX-License-Identifier: GPL-2.0 */ 2#include <linux/linkage.h> 3#include <asm/assembler.h> 4/* 5 * Function: v4t_late_abort 6 * 7 * Params : r2 = pt_regs 8 * : r4 = aborted context pc 9 * : r5 = aborted context psr 10 * 11 * Returns : r4-r5, r9-r11, r13 preserved 12 * 13 * Purpose : obtain information about current aborted instruction. 14 * Note: we read user space. This means we might cause a data 15 * abort here if the I-TLB and D-TLB aren't seeing the same 16 * picture. Unfortunately, this does happen. We live with it. 17 */ 18ENTRY(v4t_late_abort) 19 tst r5, #PSR_T_BIT @ check for thumb mode 20#ifdef CONFIG_CPU_CP15_MMU 21 mrc p15, 0, r1, c5, c0, 0 @ get FSR 22 mrc p15, 0, r0, c6, c0, 0 @ get FAR 23 bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR 24#else 25 mov r0, #0 @ clear r0, r1 (no FSR/FAR) 26 mov r1, #0 27#endif 28 bne .data_thumb_abort 29 ldr r8, [r4] @ read arm instruction 30 uaccess_disable ip @ disable userspace access 31 tst r8, #1 << 20 @ L = 1 -> write? 32 orreq r1, r1, #1 << 11 @ yes. 33 and r7, r8, #15 << 24 34 add pc, pc, r7, lsr #22 @ Now branch to the relevant processing routine 35 nop 36 37/* 0 */ b .data_arm_lateldrhpost @ ldrh rd, [rn], #m/rm 38/* 1 */ b .data_arm_lateldrhpre @ ldrh rd, [rn, #m/rm] 39/* 2 */ b .data_unknown 40/* 3 */ b .data_unknown 41/* 4 */ b .data_arm_lateldrpostconst @ ldr rd, [rn], #m 42/* 5 */ b .data_arm_lateldrpreconst @ ldr rd, [rn, #m] 43/* 6 */ b .data_arm_lateldrpostreg @ ldr rd, [rn], rm 44/* 7 */ b .data_arm_lateldrprereg @ ldr rd, [rn, rm] 45/* 8 */ b .data_arm_ldmstm @ ldm*a rn, <rlist> 46/* 9 */ b .data_arm_ldmstm @ ldm*b rn, <rlist> 47/* a */ b .data_unknown 48/* b */ b .data_unknown 49/* c */ b do_DataAbort @ ldc rd, [rn], #m @ Same as ldr rd, [rn], #m 50/* d */ b do_DataAbort @ ldc rd, [rn, #m] 51/* e */ b .data_unknown 52/* f */ b .data_unknown 53 54.data_unknown_r9: 55 ldr r9, [sp], #4 56.data_unknown: @ Part of jumptable 57 mov r0, r4 58 mov r1, r8 59 b baddataabort 60 61.data_arm_ldmstm: 62 tst r8, #1 << 21 @ check writeback bit 63 beq do_DataAbort @ no writeback -> no fixup 64 str r9, [sp, #-4]! 65 mov r7, #0x11 66 orr r7, r7, #0x1100 67 and r6, r8, r7 68 and r9, r8, r7, lsl #1 69 add r6, r6, r9, lsr #1 70 and r9, r8, r7, lsl #2 71 add r6, r6, r9, lsr #2 72 and r9, r8, r7, lsl #3 73 add r6, r6, r9, lsr #3 74 add r6, r6, r6, lsr #8 75 add r6, r6, r6, lsr #4 76 and r6, r6, #15 @ r6 = no. of registers to transfer. 77 and r9, r8, #15 << 16 @ Extract 'n' from instruction 78 ldr r7, [r2, r9, lsr #14] @ Get register 'Rn' 79 tst r8, #1 << 23 @ Check U bit 80 subne r7, r7, r6, lsl #2 @ Undo increment 81 addeq r7, r7, r6, lsl #2 @ Undo decrement 82 str r7, [r2, r9, lsr #14] @ Put register 'Rn' 83 ldr r9, [sp], #4 84 b do_DataAbort 85 86.data_arm_lateldrhpre: 87 tst r8, #1 << 21 @ Check writeback bit 88 beq do_DataAbort @ No writeback -> no fixup 89.data_arm_lateldrhpost: 90 str r9, [sp, #-4]! 91 and r9, r8, #0x00f @ get Rm / low nibble of immediate value 92 tst r8, #1 << 22 @ if (immediate offset) 93 andne r6, r8, #0xf00 @ { immediate high nibble 94 orrne r6, r9, r6, lsr #4 @ combine nibbles } else 95 ldreq r6, [r2, r9, lsl #2] @ { load Rm value } 96.data_arm_apply_r6_and_rn: 97 and r9, r8, #15 << 16 @ Extract 'n' from instruction 98 ldr r7, [r2, r9, lsr #14] @ Get register 'Rn' 99 tst r8, #1 << 23 @ Check U bit 100 subne r7, r7, r6 @ Undo incrmenet 101 addeq r7, r7, r6 @ Undo decrement 102 str r7, [r2, r9, lsr #14] @ Put register 'Rn' 103 ldr r9, [sp], #4 104 b do_DataAbort 105 106.data_arm_lateldrpreconst: 107 tst r8, #1 << 21 @ check writeback bit 108 beq do_DataAbort @ no writeback -> no fixup 109.data_arm_lateldrpostconst: 110 movs r6, r8, lsl #20 @ Get offset 111 beq do_DataAbort @ zero -> no fixup 112 str r9, [sp, #-4]! 113 and r9, r8, #15 << 16 @ Extract 'n' from instruction 114 ldr r7, [r2, r9, lsr #14] @ Get register 'Rn' 115 tst r8, #1 << 23 @ Check U bit 116 subne r7, r7, r6, lsr #20 @ Undo increment 117 addeq r7, r7, r6, lsr #20 @ Undo decrement 118 str r7, [r2, r9, lsr #14] @ Put register 'Rn' 119 ldr r9, [sp], #4 120 b do_DataAbort 121 122.data_arm_lateldrprereg: 123 tst r8, #1 << 21 @ check writeback bit 124 beq do_DataAbort @ no writeback -> no fixup 125.data_arm_lateldrpostreg: 126 and r7, r8, #15 @ Extract 'm' from instruction 127 ldr r6, [r2, r7, lsl #2] @ Get register 'Rm' 128 str r9, [sp, #-4]! 129 mov r9, r8, lsr #7 @ get shift count 130 ands r9, r9, #31 131 and r7, r8, #0x70 @ get shift type 132 orreq r7, r7, #8 @ shift count = 0 133 add pc, pc, r7 134 nop 135 136 mov r6, r6, lsl r9 @ 0: LSL #!0 137 b .data_arm_apply_r6_and_rn 138 b .data_arm_apply_r6_and_rn @ 1: LSL #0 139 nop 140 b .data_unknown_r9 @ 2: MUL? 141 nop 142 b .data_unknown_r9 @ 3: MUL? 143 nop 144 mov r6, r6, lsr r9 @ 4: LSR #!0 145 b .data_arm_apply_r6_and_rn 146 mov r6, r6, lsr #32 @ 5: LSR #32 147 b .data_arm_apply_r6_and_rn 148 b .data_unknown_r9 @ 6: MUL? 149 nop 150 b .data_unknown_r9 @ 7: MUL? 151 nop 152 mov r6, r6, asr r9 @ 8: ASR #!0 153 b .data_arm_apply_r6_and_rn 154 mov r6, r6, asr #32 @ 9: ASR #32 155 b .data_arm_apply_r6_and_rn 156 b .data_unknown_r9 @ A: MUL? 157 nop 158 b .data_unknown_r9 @ B: MUL? 159 nop 160 mov r6, r6, ror r9 @ C: ROR #!0 161 b .data_arm_apply_r6_and_rn 162 mov r6, r6, rrx @ D: RRX 163 b .data_arm_apply_r6_and_rn 164 b .data_unknown_r9 @ E: MUL? 165 nop 166 b .data_unknown_r9 @ F: MUL? 167 168.data_thumb_abort: 169 ldrh r8, [r4] @ read instruction 170 uaccess_disable ip @ disable userspace access 171 tst r8, #1 << 11 @ L = 1 -> write? 172 orreq r1, r1, #1 << 8 @ yes 173 and r7, r8, #15 << 12 174 add pc, pc, r7, lsr #10 @ lookup in table 175 nop 176 177/* 0 */ b .data_unknown 178/* 1 */ b .data_unknown 179/* 2 */ b .data_unknown 180/* 3 */ b .data_unknown 181/* 4 */ b .data_unknown 182/* 5 */ b .data_thumb_reg 183/* 6 */ b do_DataAbort 184/* 7 */ b do_DataAbort 185/* 8 */ b do_DataAbort 186/* 9 */ b do_DataAbort 187/* A */ b .data_unknown 188/* B */ b .data_thumb_pushpop 189/* C */ b .data_thumb_ldmstm 190/* D */ b .data_unknown 191/* E */ b .data_unknown 192/* F */ b .data_unknown 193 194.data_thumb_reg: 195 tst r8, #1 << 9 196 beq do_DataAbort 197 tst r8, #1 << 10 @ If 'S' (signed) bit is set 198 movne r1, #0 @ it must be a load instr 199 b do_DataAbort 200 201.data_thumb_pushpop: 202 tst r8, #1 << 10 203 beq .data_unknown 204 str r9, [sp, #-4]! 205 and r6, r8, #0x55 @ hweight8(r8) + R bit 206 and r9, r8, #0xaa 207 add r6, r6, r9, lsr #1 208 and r9, r6, #0xcc 209 and r6, r6, #0x33 210 add r6, r6, r9, lsr #2 211 movs r7, r8, lsr #9 @ C = r8 bit 8 (R bit) 212 adc r6, r6, r6, lsr #4 @ high + low nibble + R bit 213 and r6, r6, #15 @ number of regs to transfer 214 ldr r7, [r2, #13 << 2] 215 tst r8, #1 << 11 216 addeq r7, r7, r6, lsl #2 @ increment SP if PUSH 217 subne r7, r7, r6, lsl #2 @ decrement SP if POP 218 str r7, [r2, #13 << 2] 219 ldr r9, [sp], #4 220 b do_DataAbort 221 222.data_thumb_ldmstm: 223 str r9, [sp, #-4]! 224 and r6, r8, #0x55 @ hweight8(r8) 225 and r9, r8, #0xaa 226 add r6, r6, r9, lsr #1 227 and r9, r6, #0xcc 228 and r6, r6, #0x33 229 add r6, r6, r9, lsr #2 230 add r6, r6, r6, lsr #4 231 and r9, r8, #7 << 8 232 ldr r7, [r2, r9, lsr #6] 233 and r6, r6, #15 @ number of regs to transfer 234 sub r7, r7, r6, lsl #2 @ always decrement 235 str r7, [r2, r9, lsr #6] 236 ldr r9, [sp], #4 237 b do_DataAbort 238