1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
2 | /* |
3 | NetWinder Floating Point Emulator |
4 | (c) Rebel.COM, 1998 |
5 | (c) 1998, 1999 Philip Blundell |
6 | |
7 | Direct questions, comments to Scott Bambrough <scottb@netwinder.org> |
8 | |
9 | */ |
10 | #include <linux/linkage.h> |
11 | #include <asm/assembler.h> |
12 | #include <asm/opcodes.h> |
13 | |
14 | /* This is the kernel's entry point into the floating point emulator. |
15 | It is called from the kernel with code similar to this: |
16 | |
17 | sub r4, r5, #4 |
18 | ldrt r0, [r4] @ r0 = instruction |
19 | adrsvc al, r9, ret_from_exception @ r9 = normal FP return |
20 | adrsvc al, lr, fpundefinstr @ lr = undefined instr return |
21 | |
22 | get_current_task r10 |
23 | mov r8, #1 |
24 | strb r8, [r10, #TSK_USED_MATH] @ set current->used_math |
25 | add r10, r10, #TSS_FPESAVE @ r10 = workspace |
26 | ldr r4, .LC2 |
27 | ldr pc, [r4] @ Call FP emulator entry point |
28 | |
29 | The kernel expects the emulator to return via one of two possible |
30 | points of return it passes to the emulator. The emulator, if |
31 | successful in its emulation, jumps to ret_from_exception (passed in |
32 | r9) and the kernel takes care of returning control from the trap to |
33 | the user code. If the emulator is unable to emulate the instruction, |
34 | it returns via _fpundefinstr (passed via lr) and the kernel halts the |
35 | user program with a core dump. |
36 | |
37 | On entry to the emulator r10 points to an area of private FP workspace |
38 | reserved in the thread structure for this process. This is where the |
39 | emulator saves its registers across calls. The first word of this area |
40 | is used as a flag to detect the first time a process uses floating point, |
41 | so that the emulator startup cost can be avoided for tasks that don't |
42 | want it. |
43 | |
44 | This routine does three things: |
45 | |
46 | 1) The kernel has created a struct pt_regs on the stack and saved the |
47 | user registers into it. See /usr/include/asm/proc/ptrace.h for details. |
48 | |
49 | 2) It calls EmulateAll to emulate a floating point instruction. |
50 | EmulateAll returns 1 if the emulation was successful, or 0 if not. |
51 | |
52 | 3) If an instruction has been emulated successfully, it looks ahead at |
53 | the next instruction. If it is a floating point instruction, it |
54 | executes the instruction, without returning to user space. In this |
55 | way it repeatedly looks ahead and executes floating point instructions |
56 | until it encounters a non floating point instruction, at which time it |
57 | returns via _fpreturn. |
58 | |
59 | This is done to reduce the effect of the trap overhead on each |
60 | floating point instructions. GCC attempts to group floating point |
61 | instructions to allow the emulator to spread the cost of the trap over |
62 | several floating point instructions. */ |
63 | |
64 | #include <asm/asm-offsets.h> |
65 | |
66 | .globl nwfpe_enter |
67 | nwfpe_enter: |
68 | mov r4, lr @ save the failure-return addresses |
69 | mov sl, sp @ we access the registers via 'sl' |
70 | |
71 | ldr r5, [sp, #S_PC] @ get contents of PC; |
72 | mov r6, r0 @ save the opcode |
73 | emulate: |
74 | ldr r1, [sp, #S_PSR] @ fetch the PSR |
75 | bl arm_check_condition @ check the condition |
76 | cmp r0, #ARM_OPCODE_CONDTEST_PASS @ condition passed? |
77 | |
78 | @ if condition code failed to match, next insn |
79 | bne next @ get the next instruction; |
80 | |
81 | mov r0, r6 @ prepare for EmulateAll() |
82 | bl EmulateAll @ emulate the instruction |
83 | cmp r0, #0 @ was emulation successful |
84 | reteq r4 @ no, return failure |
85 | |
86 | next: |
87 | uaccess_enable r3 |
88 | .Lx1: ldrt r6, [r5], #4 @ get the next instruction and |
89 | @ increment PC |
90 | uaccess_disable r3 |
91 | and r2, r6, #0x0F000000 @ test for FP insns |
92 | teq r2, #0x0C000000 |
93 | teqne r2, #0x0D000000 |
94 | teqne r2, #0x0E000000 |
95 | retne r9 @ return ok if not a fp insn |
96 | |
97 | str r5, [sp, #S_PC] @ update PC copy in regs |
98 | |
99 | mov r0, r6 @ save a copy |
100 | b emulate @ check condition and emulate |
101 | |
102 | @ We need to be prepared for the instructions at .Lx1 and .Lx2 |
103 | @ to fault. Emit the appropriate exception gunk to fix things up. |
104 | @ ??? For some reason, faults can happen at .Lx2 even with a |
105 | @ plain LDR instruction. Weird, but it seems harmless. |
106 | .pushsection .text.fixup,"ax" |
107 | .align 2 |
108 | .Lrep: str r4, [sp, #S_PC] @ retry current instruction |
109 | .Lfix: ret r9 @ let the user eat segfaults |
110 | .popsection |
111 | |
112 | .pushsection __ex_table,"a" |
113 | .align 3 |
114 | .long .Lx1, .Lfix |
115 | .popsection |
116 | |
117 | @ |
118 | @ Check whether the instruction is a co-processor instruction. |
119 | @ If yes, we need to call the relevant co-processor handler. |
120 | @ Only FPE instructions are dispatched here, everything else |
121 | @ is handled by undef hooks. |
122 | @ |
123 | @ Emulators may wish to make use of the following registers: |
124 | @ r4 = PC value to resume execution after successful emulation |
125 | @ r9 = normal "successful" return address |
126 | @ lr = unrecognised instruction return address |
127 | @ IRQs enabled, FIQs enabled. |
128 | @ |
129 | ENTRY(call_fpe) |
130 | mov r2, r4 |
131 | sub r4, r4, #4 @ ARM instruction at user PC - 4 |
132 | USERL( .Lrep, ldrt r0, [r4]) @ load opcode from user space |
133 | ARM_BE8(rev r0, r0) @ little endian instruction |
134 | |
135 | uaccess_disable ip |
136 | |
137 | get_thread_info r10 @ get current thread |
138 | tst r0, #0x08000000 @ only CDP/CPRT/LDC/STC have bit 27 |
139 | reteq lr |
140 | and r8, r0, #0x00000f00 @ mask out CP number |
141 | #ifdef CONFIG_IWMMXT |
142 | @ Test if we need to give access to iWMMXt coprocessors |
143 | ldr r5, [r10, #TI_FLAGS] |
144 | rsbs r7, r8, #(1 << 8) @ CP 0 or 1 only |
145 | movscs r7, r5, lsr #(TIF_USING_IWMMXT + 1) |
146 | movcs r0, sp @ pass struct pt_regs |
147 | bcs iwmmxt_task_enable |
148 | #endif |
149 | add pc, pc, r8, lsr #6 |
150 | nop |
151 | |
152 | ret lr @ CP#0 |
153 | b do_fpe @ CP#1 (FPE) |
154 | b do_fpe @ CP#2 (FPE) |
155 | ret lr @ CP#3 |
156 | ret lr @ CP#4 |
157 | ret lr @ CP#5 |
158 | ret lr @ CP#6 |
159 | ret lr @ CP#7 |
160 | ret lr @ CP#8 |
161 | ret lr @ CP#9 |
162 | ret lr @ CP#10 (VFP) |
163 | ret lr @ CP#11 (VFP) |
164 | ret lr @ CP#12 |
165 | ret lr @ CP#13 |
166 | ret lr @ CP#14 (Debug) |
167 | ret lr @ CP#15 (Control) |
168 | |
169 | do_fpe: |
170 | add r10, r10, #TI_FPSTATE @ r10 = workspace |
171 | ldr_va pc, fp_enter, tmp=r4 @ Call FP module USR entry point |
172 | |
173 | @ |
174 | @ The FP module is called with these registers set: |
175 | @ r0 = instruction |
176 | @ r2 = PC+4 |
177 | @ r9 = normal "successful" return address |
178 | @ r10 = FP workspace |
179 | @ lr = unrecognised FP instruction return address |
180 | @ |
181 | |
182 | .pushsection .data |
183 | .align 2 |
184 | ENTRY(fp_enter) |
185 | .word no_fp |
186 | .popsection |
187 | |
188 | no_fp: |
189 | ret lr |
190 | ENDPROC(no_fp) |
191 | |