ARM: fix oops when using older ARMv4T CPUs

Alexander Shiyan reports that CLPS711x fails at boot time in the data
exception handler due to a NULL pointer dereference. This is caused by
the late-v4t abort handler overwriting R9 (which becomes zero). Fix
this by making the abort handler save and restore R9.

Unable to handle kernel NULL pointer dereference at virtual address 00000008
pgd = c3b58000
[00000008] *pgd=800000000, *pte=00000000, *ppte=feff4140
Internal error: Oops: 63c11817 [#1] PREEMPT ARM
CPU: 0 PID: 448 Comm: ash Not tainted 4.8.1+ #1
Hardware name: Cirrus Logic CLPS711X (Device Tree Support)
task: c39e03a0 ti: c3b4e000 task.ti: c3b4e000
PC is at __dabt_svc+0x4c/0x60
LR is at do_page_fault+0x144/0x2ac
pc : [<c000d3ac>] lr : [<c000fcec>] psr: 60000093
sp : c3b4fe6c ip : 00000001 fp : b6f1bf88
r10: c387a5a0 r9 : 00000000 r8 : e4e0e001
r7 : bee3ef83 r6 : 00100000 r5 : 80000013 r4 : c022fcf8
r3 : 00000000 r2 : 00000008 r1 : bf000000 r0 : 00000000
Flags: nZCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment user
Control: 0000217f Table: c3b58055 DAC: 00000055
Process ash (pid: 448, stack limit = 0xc3b4e190)
Stack: (0xc3b4fe6c to 0xc3b50000)
fe60: bee3ef83 c05168d1 ffffffff 00000000 c3adfe80
fe80: c3a03300 00000000 c3b4fed0 c3a03400 bee3ef83 c387a5a0 b6f1bf88 00000001
fea0: c3b4febc 00000076 c022fcf8 80000013 ffffffff 0000003f bf000000 bee3ef83
fec0: 00000004 00000000 c3adfe80 c00e432c 00000812 00000005 00000001 00000006
fee0: b6f1b000 00000000 00010000 0003c944 0004d000 0004d439 00010000 b6f1b000
ff00: 00000005 00000000 00015ecc c3b4fed0 0000000a 00000000 00000000 c00a1dc0
ff20: befff000 c3a03300 c3b4e000 c0507cd8 c0508024 fffffff8 c3a03300 00000000
ff40: c0516a58 c00a35bc c39e03a0 000001c0 bea84ce8 0004e008 c3b3a000 c00a3ac0
ff60: c3b40374 c3b3a000 bea84d11 00000000 c0500188 bea84d11 bea84ce8 00000001
ff80: 0000000b c000a304 c3b4e000 00000000 bea84ce4 c00a3cd0 00000000 bea84d11
ffa0: bea84ce8 c000a160 bea84d11 bea84ce8 bea84d11 bea84ce8 0004e008 0004d450
ffc0: bea84d11 bea84ce8 00000001 0000000b b6f45ee4 00000000 b6f5ff70 bea84ce4
ffe0: b6f2f130 bea84cb0 b6f2f194 b6ef29f4 a0000010 bea84d11 02c7cffa 02c7cffd
[<c000d3ac>] (__dabt_svc) from [<c022fcf8>] (__copy_to_user_std+0xf8/0x330)
[<c022fcf8>] (__copy_to_user_std) from [<c00e432c>]
+(load_elf_binary+0x920/0x107c)
[<c00e432c>] (load_elf_binary) from [<c00a35bc>]
+(search_binary_handler+0x80/0x16c)
[<c00a35bc>] (search_binary_handler) from [<c00a3ac0>]
+(do_execveat_common+0x418/0x600)
[<c00a3ac0>] (do_execveat_common) from [<c00a3cd0>] (do_execve+0x28/0x30)
[<c00a3cd0>] (do_execve) from [<c000a160>] (ret_fast_syscall+0x0/0x30)
Code: e1a0200d eb00136b e321f093 e59d104c (e5891008)
---[ end trace 4b4f8086ebef98c5 ]---

Fixes: e6978e4bf181 ("ARM: save and reset the address limit when entering an exception")
Reported-by: Alexander Shiyan <shc_work@mail.ru>
Tested-by: Alexander Shiyan <shc_work@mail.ru>
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>

+24 -10
+24 -10
arch/arm/mm/abort-lv4t.S
··· 7 * : r4 = aborted context pc 8 * : r5 = aborted context psr 9 * 10 - * Returns : r4-r5, r10-r11, r13 preserved 11 * 12 * Purpose : obtain information about current aborted instruction. 13 * Note: we read user space. This means we might cause a data ··· 48 /* c */ b do_DataAbort @ ldc rd, [rn], #m @ Same as ldr rd, [rn], #m 49 /* d */ b do_DataAbort @ ldc rd, [rn, #m] 50 /* e */ b .data_unknown 51 - /* f */ 52 .data_unknown: @ Part of jumptable 53 mov r0, r4 54 mov r1, r8 ··· 60 .data_arm_ldmstm: 61 tst r8, #1 << 21 @ check writeback bit 62 beq do_DataAbort @ no writeback -> no fixup 63 mov r7, #0x11 64 orr r7, r7, #0x1100 65 and r6, r8, r7 ··· 79 subne r7, r7, r6, lsl #2 @ Undo increment 80 addeq r7, r7, r6, lsl #2 @ Undo decrement 81 str r7, [r2, r9, lsr #14] @ Put register 'Rn' 82 b do_DataAbort 83 84 .data_arm_lateldrhpre: 85 tst r8, #1 << 21 @ Check writeback bit 86 beq do_DataAbort @ No writeback -> no fixup 87 .data_arm_lateldrhpost: 88 and r9, r8, #0x00f @ get Rm / low nibble of immediate value 89 tst r8, #1 << 22 @ if (immediate offset) 90 andne r6, r8, #0xf00 @ { immediate high nibble ··· 99 subne r7, r7, r6 @ Undo incrmenet 100 addeq r7, r7, r6 @ Undo decrement 101 str r7, [r2, r9, lsr #14] @ Put register 'Rn' 102 b do_DataAbort 103 104 .data_arm_lateldrpreconst: ··· 108 .data_arm_lateldrpostconst: 109 movs r6, r8, lsl #20 @ Get offset 110 beq do_DataAbort @ zero -> no fixup 111 and r9, r8, #15 << 16 @ Extract 'n' from instruction 112 ldr r7, [r2, r9, lsr #14] @ Get register 'Rn' 113 tst r8, #1 << 23 @ Check U bit 114 subne r7, r7, r6, lsr #20 @ Undo increment 115 addeq r7, r7, r6, lsr #20 @ Undo decrement 116 str r7, [r2, r9, lsr #14] @ Put register 'Rn' 117 b do_DataAbort 118 119 .data_arm_lateldrprereg: ··· 124 .data_arm_lateldrpostreg: 125 and r7, r8, #15 @ Extract 'm' from instruction 126 ldr r6, [r2, r7, lsl #2] @ Get register 'Rm' 127 mov r9, r8, lsr #7 @ get shift count 128 ands r9, r9, #31 129 and r7, r8, #0x70 @ get shift type ··· 136 b .data_arm_apply_r6_and_rn 137 b .data_arm_apply_r6_and_rn @ 1: LSL #0 138 nop 139 - b .data_unknown @ 2: MUL? 140 nop 141 - b .data_unknown @ 3: MUL? 142 nop 143 mov r6, r6, lsr r9 @ 4: LSR #!0 144 b .data_arm_apply_r6_and_rn 145 mov r6, r6, lsr #32 @ 5: LSR #32 146 b .data_arm_apply_r6_and_rn 147 - b .data_unknown @ 6: MUL? 148 nop 149 - b .data_unknown @ 7: MUL? 150 nop 151 mov r6, r6, asr r9 @ 8: ASR #!0 152 b .data_arm_apply_r6_and_rn 153 mov r6, r6, asr #32 @ 9: ASR #32 154 b .data_arm_apply_r6_and_rn 155 - b .data_unknown @ A: MUL? 156 nop 157 - b .data_unknown @ B: MUL? 158 nop 159 mov r6, r6, ror r9 @ C: ROR #!0 160 b .data_arm_apply_r6_and_rn 161 mov r6, r6, rrx @ D: RRX 162 b .data_arm_apply_r6_and_rn 163 - b .data_unknown @ E: MUL? 164 nop 165 - b .data_unknown @ F: MUL? 166 167 .data_thumb_abort: 168 ldrh r8, [r4] @ read instruction ··· 200 .data_thumb_pushpop: 201 tst r8, #1 << 10 202 beq .data_unknown 203 and r6, r8, #0x55 @ hweight8(r8) + R bit 204 and r9, r8, #0xaa 205 add r6, r6, r9, lsr #1 ··· 215 addeq r7, r7, r6, lsl #2 @ increment SP if PUSH 216 subne r7, r7, r6, lsl #2 @ decrement SP if POP 217 str r7, [r2, #13 << 2] 218 b do_DataAbort 219 220 .data_thumb_ldmstm: 221 and r6, r8, #0x55 @ hweight8(r8) 222 and r9, r8, #0xaa 223 add r6, r6, r9, lsr #1 ··· 232 and r6, r6, #15 @ number of regs to transfer 233 sub r7, r7, r6, lsl #2 @ always decrement 234 str r7, [r2, r9, lsr #6] 235 b do_DataAbort
··· 7 * : r4 = aborted context pc 8 * : r5 = aborted context psr 9 * 10 + * Returns : r4-r5, r9-r11, r13 preserved 11 * 12 * Purpose : obtain information about current aborted instruction. 13 * Note: we read user space. This means we might cause a data ··· 48 /* c */ b do_DataAbort @ ldc rd, [rn], #m @ Same as ldr rd, [rn], #m 49 /* d */ b do_DataAbort @ ldc rd, [rn, #m] 50 /* e */ b .data_unknown 51 + /* f */ b .data_unknown 52 + 53 + .data_unknown_r9: 54 + ldr r9, [sp], #4 55 .data_unknown: @ Part of jumptable 56 mov r0, r4 57 mov r1, r8 ··· 57 .data_arm_ldmstm: 58 tst r8, #1 << 21 @ check writeback bit 59 beq do_DataAbort @ no writeback -> no fixup 60 + str r9, [sp, #-4]! 61 mov r7, #0x11 62 orr r7, r7, #0x1100 63 and r6, r8, r7 ··· 75 subne r7, r7, r6, lsl #2 @ Undo increment 76 addeq r7, r7, r6, lsl #2 @ Undo decrement 77 str r7, [r2, r9, lsr #14] @ Put register 'Rn' 78 + ldr r9, [sp], #4 79 b do_DataAbort 80 81 .data_arm_lateldrhpre: 82 tst r8, #1 << 21 @ Check writeback bit 83 beq do_DataAbort @ No writeback -> no fixup 84 .data_arm_lateldrhpost: 85 + str r9, [sp, #-4]! 86 and r9, r8, #0x00f @ get Rm / low nibble of immediate value 87 tst r8, #1 << 22 @ if (immediate offset) 88 andne r6, r8, #0xf00 @ { immediate high nibble ··· 93 subne r7, r7, r6 @ Undo incrmenet 94 addeq r7, r7, r6 @ Undo decrement 95 str r7, [r2, r9, lsr #14] @ Put register 'Rn' 96 + ldr r9, [sp], #4 97 b do_DataAbort 98 99 .data_arm_lateldrpreconst: ··· 101 .data_arm_lateldrpostconst: 102 movs r6, r8, lsl #20 @ Get offset 103 beq do_DataAbort @ zero -> no fixup 104 + str r9, [sp, #-4]! 105 and r9, r8, #15 << 16 @ Extract 'n' from instruction 106 ldr r7, [r2, r9, lsr #14] @ Get register 'Rn' 107 tst r8, #1 << 23 @ Check U bit 108 subne r7, r7, r6, lsr #20 @ Undo increment 109 addeq r7, r7, r6, lsr #20 @ Undo decrement 110 str r7, [r2, r9, lsr #14] @ Put register 'Rn' 111 + ldr r9, [sp], #4 112 b do_DataAbort 113 114 .data_arm_lateldrprereg: ··· 115 .data_arm_lateldrpostreg: 116 and r7, r8, #15 @ Extract 'm' from instruction 117 ldr r6, [r2, r7, lsl #2] @ Get register 'Rm' 118 + str r9, [sp, #-4]! 119 mov r9, r8, lsr #7 @ get shift count 120 ands r9, r9, #31 121 and r7, r8, #0x70 @ get shift type ··· 126 b .data_arm_apply_r6_and_rn 127 b .data_arm_apply_r6_and_rn @ 1: LSL #0 128 nop 129 + b .data_unknown_r9 @ 2: MUL? 130 nop 131 + b .data_unknown_r9 @ 3: MUL? 132 nop 133 mov r6, r6, lsr r9 @ 4: LSR #!0 134 b .data_arm_apply_r6_and_rn 135 mov r6, r6, lsr #32 @ 5: LSR #32 136 b .data_arm_apply_r6_and_rn 137 + b .data_unknown_r9 @ 6: MUL? 138 nop 139 + b .data_unknown_r9 @ 7: MUL? 140 nop 141 mov r6, r6, asr r9 @ 8: ASR #!0 142 b .data_arm_apply_r6_and_rn 143 mov r6, r6, asr #32 @ 9: ASR #32 144 b .data_arm_apply_r6_and_rn 145 + b .data_unknown_r9 @ A: MUL? 146 nop 147 + b .data_unknown_r9 @ B: MUL? 148 nop 149 mov r6, r6, ror r9 @ C: ROR #!0 150 b .data_arm_apply_r6_and_rn 151 mov r6, r6, rrx @ D: RRX 152 b .data_arm_apply_r6_and_rn 153 + b .data_unknown_r9 @ E: MUL? 154 nop 155 + b .data_unknown_r9 @ F: MUL? 156 157 .data_thumb_abort: 158 ldrh r8, [r4] @ read instruction ··· 190 .data_thumb_pushpop: 191 tst r8, #1 << 10 192 beq .data_unknown 193 + str r9, [sp, #-4]! 194 and r6, r8, #0x55 @ hweight8(r8) + R bit 195 and r9, r8, #0xaa 196 add r6, r6, r9, lsr #1 ··· 204 addeq r7, r7, r6, lsl #2 @ increment SP if PUSH 205 subne r7, r7, r6, lsl #2 @ decrement SP if POP 206 str r7, [r2, #13 << 2] 207 + ldr r9, [sp], #4 208 b do_DataAbort 209 210 .data_thumb_ldmstm: 211 + str r9, [sp, #-4]! 212 and r6, r8, #0x55 @ hweight8(r8) 213 and r9, r8, #0xaa 214 add r6, r6, r9, lsr #1 ··· 219 and r6, r6, #15 @ number of regs to transfer 220 sub r7, r7, r6, lsl #2 @ always decrement 221 str r7, [r2, r9, lsr #6] 222 + ldr r9, [sp], #4 223 b do_DataAbort