GNU Linux-libre 4.19.264-gnu1
[releases.git] / arch / arm / mm / abort-lv4t.S
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  */
18 ENTRY(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