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