ARM: fix oops when using older ARMv4T CPUs
authorRussell King <rmk+kernel@armlinux.org.uk>
Tue, 18 Oct 2016 09:24:49 +0000 (10:24 +0100)
committerRussell King <rmk+kernel@armlinux.org.uk>
Wed, 19 Oct 2016 09:18:43 +0000 (10:18 +0100)
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>
arch/arm/mm/abort-lv4t.S

index 6d8e8e3365d17321f03b37fa67ab04a65b29f4ca..4cdfab31a0b612d11f1ad88c7985e007620fdca1 100644 (file)
@@ -7,7 +7,7 @@
  *        : r4 = aborted context pc
  *        : r5 = aborted context psr
  *
  *        : r4 = aborted context pc
  *        : r5 = aborted context psr
  *
- * Returns : r4-r5, r10-r11, r13 preserved
+ * Returns : r4-r5, r9-r11, r13 preserved
  *
  * Purpose : obtain information about current aborted instruction.
  * Note: we read user space.  This means we might cause a data
  *
  * Purpose : obtain information about current aborted instruction.
  * Note: we read user space.  This means we might cause a data
@@ -48,7 +48,10 @@ ENTRY(v4t_late_abort)
 /* c */        b       do_DataAbort                    @ ldc   rd, [rn], #m    @ Same as ldr   rd, [rn], #m
 /* d */        b       do_DataAbort                    @ ldc   rd, [rn, #m]
 /* e */        b       .data_unknown
 /* c */        b       do_DataAbort                    @ ldc   rd, [rn], #m    @ Same as ldr   rd, [rn], #m
 /* d */        b       do_DataAbort                    @ ldc   rd, [rn, #m]
 /* e */        b       .data_unknown
-/* f */
+/* f */        b       .data_unknown
+
+.data_unknown_r9:
+       ldr     r9, [sp], #4
 .data_unknown: @ Part of jumptable
        mov     r0, r4
        mov     r1, r8
 .data_unknown: @ Part of jumptable
        mov     r0, r4
        mov     r1, r8
@@ -57,6 +60,7 @@ ENTRY(v4t_late_abort)
 .data_arm_ldmstm:
        tst     r8, #1 << 21                    @ check writeback bit
        beq     do_DataAbort                    @ no writeback -> no fixup
 .data_arm_ldmstm:
        tst     r8, #1 << 21                    @ check writeback bit
        beq     do_DataAbort                    @ no writeback -> no fixup
+       str     r9, [sp, #-4]!
        mov     r7, #0x11
        orr     r7, r7, #0x1100
        and     r6, r8, r7
        mov     r7, #0x11
        orr     r7, r7, #0x1100
        and     r6, r8, r7
@@ -75,12 +79,14 @@ ENTRY(v4t_late_abort)
        subne   r7, r7, r6, lsl #2              @ Undo increment
        addeq   r7, r7, r6, lsl #2              @ Undo decrement
        str     r7, [r2, r9, lsr #14]           @ Put register 'Rn'
        subne   r7, r7, r6, lsl #2              @ Undo increment
        addeq   r7, r7, r6, lsl #2              @ Undo decrement
        str     r7, [r2, r9, lsr #14]           @ Put register 'Rn'
+       ldr     r9, [sp], #4
        b       do_DataAbort
 
 .data_arm_lateldrhpre:
        tst     r8, #1 << 21                    @ Check writeback bit
        beq     do_DataAbort                    @ No writeback -> no fixup
 .data_arm_lateldrhpost:
        b       do_DataAbort
 
 .data_arm_lateldrhpre:
        tst     r8, #1 << 21                    @ Check writeback bit
        beq     do_DataAbort                    @ No writeback -> no fixup
 .data_arm_lateldrhpost:
+       str     r9, [sp, #-4]!
        and     r9, r8, #0x00f                  @ get Rm / low nibble of immediate value
        tst     r8, #1 << 22                    @ if (immediate offset)
        andne   r6, r8, #0xf00                  @ { immediate high nibble
        and     r9, r8, #0x00f                  @ get Rm / low nibble of immediate value
        tst     r8, #1 << 22                    @ if (immediate offset)
        andne   r6, r8, #0xf00                  @ { immediate high nibble
@@ -93,6 +99,7 @@ ENTRY(v4t_late_abort)
        subne   r7, r7, r6                      @ Undo incrmenet
        addeq   r7, r7, r6                      @ Undo decrement
        str     r7, [r2, r9, lsr #14]           @ Put register 'Rn'
        subne   r7, r7, r6                      @ Undo incrmenet
        addeq   r7, r7, r6                      @ Undo decrement
        str     r7, [r2, r9, lsr #14]           @ Put register 'Rn'
+       ldr     r9, [sp], #4
        b       do_DataAbort
 
 .data_arm_lateldrpreconst:
        b       do_DataAbort
 
 .data_arm_lateldrpreconst:
@@ -101,12 +108,14 @@ ENTRY(v4t_late_abort)
 .data_arm_lateldrpostconst:
        movs    r6, r8, lsl #20                 @ Get offset
        beq     do_DataAbort                    @ zero -> no fixup
 .data_arm_lateldrpostconst:
        movs    r6, r8, lsl #20                 @ Get offset
        beq     do_DataAbort                    @ zero -> no fixup
+       str     r9, [sp, #-4]!
        and     r9, r8, #15 << 16               @ Extract 'n' from instruction
        ldr     r7, [r2, r9, lsr #14]           @ Get register 'Rn'
        tst     r8, #1 << 23                    @ Check U bit
        subne   r7, r7, r6, lsr #20             @ Undo increment
        addeq   r7, r7, r6, lsr #20             @ Undo decrement
        str     r7, [r2, r9, lsr #14]           @ Put register 'Rn'
        and     r9, r8, #15 << 16               @ Extract 'n' from instruction
        ldr     r7, [r2, r9, lsr #14]           @ Get register 'Rn'
        tst     r8, #1 << 23                    @ Check U bit
        subne   r7, r7, r6, lsr #20             @ Undo increment
        addeq   r7, r7, r6, lsr #20             @ Undo decrement
        str     r7, [r2, r9, lsr #14]           @ Put register 'Rn'
+       ldr     r9, [sp], #4
        b       do_DataAbort
 
 .data_arm_lateldrprereg:
        b       do_DataAbort
 
 .data_arm_lateldrprereg:
@@ -115,6 +124,7 @@ ENTRY(v4t_late_abort)
 .data_arm_lateldrpostreg:
        and     r7, r8, #15                     @ Extract 'm' from instruction
        ldr     r6, [r2, r7, lsl #2]            @ Get register 'Rm'
 .data_arm_lateldrpostreg:
        and     r7, r8, #15                     @ Extract 'm' from instruction
        ldr     r6, [r2, r7, lsl #2]            @ Get register 'Rm'
+       str     r9, [sp, #-4]!
        mov     r9, r8, lsr #7                  @ get shift count
        ands    r9, r9, #31
        and     r7, r8, #0x70                   @ get shift type
        mov     r9, r8, lsr #7                  @ get shift count
        ands    r9, r9, #31
        and     r7, r8, #0x70                   @ get shift type
@@ -126,33 +136,33 @@ ENTRY(v4t_late_abort)
        b       .data_arm_apply_r6_and_rn
        b       .data_arm_apply_r6_and_rn       @ 1: LSL #0
        nop
        b       .data_arm_apply_r6_and_rn
        b       .data_arm_apply_r6_and_rn       @ 1: LSL #0
        nop
-       b       .data_unknown                   @ 2: MUL?
+       b       .data_unknown_r9                @ 2: MUL?
        nop
        nop
-       b       .data_unknown                   @ 3: MUL?
+       b       .data_unknown_r9                @ 3: MUL?
        nop
        mov     r6, r6, lsr r9                  @ 4: LSR #!0
        b       .data_arm_apply_r6_and_rn
        mov     r6, r6, lsr #32                 @ 5: LSR #32
        b       .data_arm_apply_r6_and_rn
        nop
        mov     r6, r6, lsr r9                  @ 4: LSR #!0
        b       .data_arm_apply_r6_and_rn
        mov     r6, r6, lsr #32                 @ 5: LSR #32
        b       .data_arm_apply_r6_and_rn
-       b       .data_unknown                   @ 6: MUL?
+       b       .data_unknown_r9                @ 6: MUL?
        nop
        nop
-       b       .data_unknown                   @ 7: MUL?
+       b       .data_unknown_r9                @ 7: MUL?
        nop
        mov     r6, r6, asr r9                  @ 8: ASR #!0
        b       .data_arm_apply_r6_and_rn
        mov     r6, r6, asr #32                 @ 9: ASR #32
        b       .data_arm_apply_r6_and_rn
        nop
        mov     r6, r6, asr r9                  @ 8: ASR #!0
        b       .data_arm_apply_r6_and_rn
        mov     r6, r6, asr #32                 @ 9: ASR #32
        b       .data_arm_apply_r6_and_rn
-       b       .data_unknown                   @ A: MUL?
+       b       .data_unknown_r9                @ A: MUL?
        nop
        nop
-       b       .data_unknown                   @ B: MUL?
+       b       .data_unknown_r9                @ B: MUL?
        nop
        mov     r6, r6, ror r9                  @ C: ROR #!0
        b       .data_arm_apply_r6_and_rn
        mov     r6, r6, rrx                     @ D: RRX
        b       .data_arm_apply_r6_and_rn
        nop
        mov     r6, r6, ror r9                  @ C: ROR #!0
        b       .data_arm_apply_r6_and_rn
        mov     r6, r6, rrx                     @ D: RRX
        b       .data_arm_apply_r6_and_rn
-       b       .data_unknown                   @ E: MUL?
+       b       .data_unknown_r9                @ E: MUL?
        nop
        nop
-       b       .data_unknown                   @ F: MUL?
+       b       .data_unknown_r9                @ F: MUL?
 
 .data_thumb_abort:
        ldrh    r8, [r4]                        @ read instruction
 
 .data_thumb_abort:
        ldrh    r8, [r4]                        @ read instruction
@@ -190,6 +200,7 @@ ENTRY(v4t_late_abort)
 .data_thumb_pushpop:
        tst     r8, #1 << 10
        beq     .data_unknown
 .data_thumb_pushpop:
        tst     r8, #1 << 10
        beq     .data_unknown
+       str     r9, [sp, #-4]!
        and     r6, r8, #0x55                   @ hweight8(r8) + R bit
        and     r9, r8, #0xaa
        add     r6, r6, r9, lsr #1
        and     r6, r8, #0x55                   @ hweight8(r8) + R bit
        and     r9, r8, #0xaa
        add     r6, r6, r9, lsr #1
@@ -204,9 +215,11 @@ ENTRY(v4t_late_abort)
        addeq   r7, r7, r6, lsl #2              @ increment SP if PUSH
        subne   r7, r7, r6, lsl #2              @ decrement SP if POP
        str     r7, [r2, #13 << 2]
        addeq   r7, r7, r6, lsl #2              @ increment SP if PUSH
        subne   r7, r7, r6, lsl #2              @ decrement SP if POP
        str     r7, [r2, #13 << 2]
+       ldr     r9, [sp], #4
        b       do_DataAbort
 
 .data_thumb_ldmstm:
        b       do_DataAbort
 
 .data_thumb_ldmstm:
+       str     r9, [sp, #-4]!
        and     r6, r8, #0x55                   @ hweight8(r8)
        and     r9, r8, #0xaa
        add     r6, r6, r9, lsr #1
        and     r6, r8, #0x55                   @ hweight8(r8)
        and     r9, r8, #0xaa
        add     r6, r6, r9, lsr #1
@@ -219,4 +232,5 @@ ENTRY(v4t_late_abort)
        and     r6, r6, #15                     @ number of regs to transfer
        sub     r7, r7, r6, lsl #2              @ always decrement
        str     r7, [r2, r9, lsr #6]
        and     r6, r6, #15                     @ number of regs to transfer
        sub     r7, r7, r6, lsl #2              @ always decrement
        str     r7, [r2, r9, lsr #6]
+       ldr     r9, [sp], #4
        b       do_DataAbort
        b       do_DataAbort