hc
2024-11-01 2f529f9b558ca1c1bd74be7437a84e4711743404
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/vfp/vfphw.S
 *
 *  Copyright (C) 2004 ARM Limited.
 *  Written by Deep Blue Solutions Limited.
 *
 * This code is called from the kernel's undefined instruction trap.
 * r9 holds the return address for successful handling.
 * lr holds the return address for unrecognised instructions.
 * r10 points at the start of the private FP workspace in the thread structure
 * sp points to a struct pt_regs (as defined in include/asm/proc/ptrace.h)
 */
#include <linux/init.h>
#include <linux/linkage.h>
#include <asm/thread_info.h>
#include <asm/vfpmacros.h>
#include <linux/kern_levels.h>
#include <asm/assembler.h>
#include <asm/asm-offsets.h>
 
   .macro    DBGSTR, str
#ifdef DEBUG
   stmfd    sp!, {r0-r3, ip, lr}
   ldr    r0, =1f
   bl    printk
   ldmfd    sp!, {r0-r3, ip, lr}
 
   .pushsection .rodata, "a"
1:    .ascii    KERN_DEBUG "VFP: \str\n"
   .byte    0
   .previous
#endif
   .endm
 
   .macro  DBGSTR1, str, arg
#ifdef DEBUG
   stmfd    sp!, {r0-r3, ip, lr}
   mov    r1, \arg
   ldr    r0, =1f
   bl    printk
   ldmfd    sp!, {r0-r3, ip, lr}
 
   .pushsection .rodata, "a"
1:    .ascii    KERN_DEBUG "VFP: \str\n"
   .byte    0
   .previous
#endif
   .endm
 
   .macro  DBGSTR3, str, arg1, arg2, arg3
#ifdef DEBUG
   stmfd    sp!, {r0-r3, ip, lr}
   mov    r3, \arg3
   mov    r2, \arg2
   mov    r1, \arg1
   ldr    r0, =1f
   bl    printk
   ldmfd    sp!, {r0-r3, ip, lr}
 
   .pushsection .rodata, "a"
1:    .ascii    KERN_DEBUG "VFP: \str\n"
   .byte    0
   .previous
#endif
   .endm
 
 
@ VFP hardware support entry point.
@
@  r0  = instruction opcode (32-bit ARM or two 16-bit Thumb)
@  r2  = PC value to resume execution after successful emulation
@  r9  = normal "successful" return address
@  r10 = vfp_state union
@  r11 = CPU number
@  lr  = unrecognised instruction return address
@  IRQs enabled.
ENTRY(vfp_support_entry)
   DBGSTR3    "instr %08x pc %08x state %p", r0, r2, r10
 
   .fpu    vfpv2
   VFPFMRX    r1, FPEXC        @ Is the VFP enabled?
   DBGSTR1    "fpexc %08x", r1
   tst    r1, #FPEXC_EN
   bne    look_for_VFP_exceptions    @ VFP is already enabled
 
   DBGSTR1 "enable %x", r10
   ldr    r3, vfp_current_hw_state_address
   orr    r1, r1, #FPEXC_EN    @ user FPEXC has the enable bit set
   ldr    r4, [r3, r11, lsl #2]    @ vfp_current_hw_state pointer
   bic    r5, r1, #FPEXC_EX    @ make sure exceptions are disabled
   cmp    r4, r10            @ this thread owns the hw context?
#ifndef CONFIG_SMP
   @ For UP, checking that this thread owns the hw context is
   @ sufficient to determine that the hardware state is valid.
   beq    vfp_hw_state_valid
 
   @ On UP, we lazily save the VFP context.  As a different
   @ thread wants ownership of the VFP hardware, save the old
   @ state if there was a previous (valid) owner.
 
   VFPFMXR    FPEXC, r5        @ enable VFP, disable any pending
                   @ exceptions, so we can get at the
                   @ rest of it
 
   DBGSTR1    "save old state %p", r4
   cmp    r4, #0            @ if the vfp_current_hw_state is NULL
   beq    vfp_reload_hw        @ then the hw state needs reloading
   VFPFSTMIA r4, r5        @ save the working registers
   VFPFMRX    r5, FPSCR        @ current status
#ifndef CONFIG_CPU_FEROCEON
   tst    r1, #FPEXC_EX        @ is there additional state to save?
   beq    1f
   VFPFMRX    r6, FPINST        @ FPINST (only if FPEXC.EX is set)
   tst    r1, #FPEXC_FP2V        @ is there an FPINST2 to read?
   beq    1f
   VFPFMRX    r8, FPINST2        @ FPINST2 if needed (and present)
1:
#endif
   stmia    r4, {r1, r5, r6, r8}    @ save FPEXC, FPSCR, FPINST, FPINST2
vfp_reload_hw:
 
#else
   @ For SMP, if this thread does not own the hw context, then we
   @ need to reload it.  No need to save the old state as on SMP,
   @ we always save the state when we switch away from a thread.
   bne    vfp_reload_hw
 
   @ This thread has ownership of the current hardware context.
   @ However, it may have been migrated to another CPU, in which
   @ case the saved state is newer than the hardware context.
   @ Check this by looking at the CPU number which the state was
   @ last loaded onto.
   ldr    ip, [r10, #VFP_CPU]
   teq    ip, r11
   beq    vfp_hw_state_valid
 
vfp_reload_hw:
   @ We're loading this threads state into the VFP hardware. Update
   @ the CPU number which contains the most up to date VFP context.
   str    r11, [r10, #VFP_CPU]
 
   VFPFMXR    FPEXC, r5        @ enable VFP, disable any pending
                   @ exceptions, so we can get at the
                   @ rest of it
#endif
 
   DBGSTR1    "load state %p", r10
   str    r10, [r3, r11, lsl #2]    @ update the vfp_current_hw_state pointer
                   @ Load the saved state back into the VFP
   VFPFLDMIA r10, r5        @ reload the working registers while
                   @ FPEXC is in a safe state
   ldmia    r10, {r1, r5, r6, r8}    @ load FPEXC, FPSCR, FPINST, FPINST2
#ifndef CONFIG_CPU_FEROCEON
   tst    r1, #FPEXC_EX        @ is there additional state to restore?
   beq    1f
   VFPFMXR    FPINST, r6        @ restore FPINST (only if FPEXC.EX is set)
   tst    r1, #FPEXC_FP2V        @ is there an FPINST2 to write?
   beq    1f
   VFPFMXR    FPINST2, r8        @ FPINST2 if needed (and present)
1:
#endif
   VFPFMXR    FPSCR, r5        @ restore status
 
@ The context stored in the VFP hardware is up to date with this thread
vfp_hw_state_valid:
   tst    r1, #FPEXC_EX
   bne    process_exception    @ might as well handle the pending
                   @ exception before retrying branch
                   @ out before setting an FPEXC that
                   @ stops us reading stuff
   VFPFMXR    FPEXC, r1        @ Restore FPEXC last
   enable_irq_if_pipelined
   sub    r2, r2, #4        @ Retry current instruction - if Thumb
   str    r2, [sp, #S_PC]        @ mode it's two 16-bit instructions,
                   @ else it's one 32-bit instruction, so
                   @ always subtract 4 from the following
                   @ instruction address.
   dec_preempt_count_ti r10, r4
   ret    r9            @ we think we have handled things
 
 
look_for_VFP_exceptions:
   @ Check for synchronous or asynchronous exception
   tst    r1, #FPEXC_EX | FPEXC_DEX
   bne    process_exception
   @ On some implementations of the VFP subarch 1, setting FPSCR.IXE
   @ causes all the CDP instructions to be bounced synchronously without
   @ setting the FPEXC.EX bit
   VFPFMRX    r5, FPSCR
   tst    r5, #FPSCR_IXE
   bne    process_exception
 
   tst    r5, #FPSCR_LENGTH_MASK
   beq    skip
   orr    r1, r1, #FPEXC_DEX
   b    process_exception
skip:
 
   @ Fall into hand on to next handler - appropriate coproc instr
   @ not recognised by VFP
 
   enable_irq_if_pipelined
   DBGSTR    "not VFP"
   dec_preempt_count_ti r10, r4
   ret    lr
 
process_exception:
   DBGSTR    "bounce"
   mov    r2, sp            @ nothing stacked - regdump is at TOS
   mov    lr, r9            @ setup for a return to the user code.
 
   @ Now call the C code to package up the bounce to the support code
   @   r0 holds the trigger instruction
   @   r1 holds the FPEXC value
   @   r2 pointer to register dump
   b    VFP_bounce        @ we have handled this - the support
                   @ code will raise an exception if
                   @ required. If not, the user code will
                   @ retry the faulted instruction
ENDPROC(vfp_support_entry)
 
ENTRY(vfp_save_state)
   @ Save the current VFP state
   @ r0 - save location
   @ r1 - FPEXC
   DBGSTR1    "save VFP state %p", r0
   VFPFSTMIA r0, r2        @ save the working registers
   VFPFMRX    r2, FPSCR        @ current status
   tst    r1, #FPEXC_EX        @ is there additional state to save?
   beq    1f
   VFPFMRX    r3, FPINST        @ FPINST (only if FPEXC.EX is set)
   tst    r1, #FPEXC_FP2V        @ is there an FPINST2 to read?
   beq    1f
   VFPFMRX    r12, FPINST2        @ FPINST2 if needed (and present)
1:
   stmia    r0, {r1, r2, r3, r12}    @ save FPEXC, FPSCR, FPINST, FPINST2
   ret    lr
ENDPROC(vfp_save_state)
 
   .align
vfp_current_hw_state_address:
   .word    vfp_current_hw_state
 
   .macro    tbl_branch, base, tmp, shift
#ifdef CONFIG_THUMB2_KERNEL
   adr    \tmp, 1f
   add    \tmp, \tmp, \base, lsl \shift
   ret    \tmp
#else
   add    pc, pc, \base, lsl \shift
   mov    r0, r0
#endif
1:
   .endm
 
ENTRY(vfp_get_float)
   tbl_branch r0, r3, #3
   .fpu    vfpv2
   .irp    dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
1:    vmov    r0, s\dr
   ret    lr
   .org    1b + 8
   .endr
   .irp    dr,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
1:    vmov    r0, s\dr
   ret    lr
   .org    1b + 8
   .endr
ENDPROC(vfp_get_float)
 
ENTRY(vfp_put_float)
   tbl_branch r1, r3, #3
   .fpu    vfpv2
   .irp    dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
1:    vmov    s\dr, r0
   ret    lr
   .org    1b + 8
   .endr
   .irp    dr,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
1:    vmov    s\dr, r0
   ret    lr
   .org    1b + 8
   .endr
ENDPROC(vfp_put_float)
 
ENTRY(vfp_get_double)
   tbl_branch r0, r3, #3
   .fpu    vfpv2
   .irp    dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
1:    vmov    r0, r1, d\dr
   ret    lr
   .org    1b + 8
   .endr
#ifdef CONFIG_VFPv3
   @ d16 - d31 registers
   .fpu    vfpv3
   .irp    dr,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
1:    vmov    r0, r1, d\dr
   ret    lr
   .org    1b + 8
   .endr
#endif
 
   @ virtual register 16 (or 32 if VFPv3) for compare with zero
   mov    r0, #0
   mov    r1, #0
   ret    lr
ENDPROC(vfp_get_double)
 
ENTRY(vfp_put_double)
   tbl_branch r2, r3, #3
   .fpu    vfpv2
   .irp    dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
1:    vmov    d\dr, r0, r1
   ret    lr
   .org    1b + 8
   .endr
#ifdef CONFIG_VFPv3
   .fpu    vfpv3
   @ d16 - d31 registers
   .irp    dr,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
1:    vmov    d\dr, r0, r1
   ret    lr
   .org    1b + 8
   .endr
#endif
ENDPROC(vfp_put_double)