]>
Commit | Line | Data |
---|---|---|
e9564df7 GR |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd. | |
3 | ||
4 | #include <linux/ptrace.h> | |
5 | #include <linux/uaccess.h> | |
6 | #include <abi/reg_ops.h> | |
7 | ||
8 | #define MTCR_MASK 0xFC00FFE0 | |
9 | #define MFCR_MASK 0xFC00FFE0 | |
10 | #define MTCR_DIST 0xC0006420 | |
11 | #define MFCR_DIST 0xC0006020 | |
12 | ||
13 | void __init init_fpu(void) | |
14 | { | |
15 | mtcr("cr<1, 2>", 0); | |
16 | } | |
17 | ||
18 | /* | |
19 | * fpu_libc_helper() is to help libc to excute: | |
20 | * - mfcr %a, cr<1, 2> | |
21 | * - mfcr %a, cr<2, 2> | |
22 | * - mtcr %a, cr<1, 2> | |
23 | * - mtcr %a, cr<2, 2> | |
24 | */ | |
25 | int fpu_libc_helper(struct pt_regs *regs) | |
26 | { | |
27 | int fault; | |
28 | unsigned long instrptr, regx = 0; | |
29 | unsigned long index = 0, tmp = 0; | |
30 | unsigned long tinstr = 0; | |
31 | u16 instr_hi, instr_low; | |
32 | ||
33 | instrptr = instruction_pointer(regs); | |
34 | if (instrptr & 1) | |
35 | return 0; | |
36 | ||
37 | fault = __get_user(instr_low, (u16 *)instrptr); | |
38 | if (fault) | |
39 | return 0; | |
40 | ||
41 | fault = __get_user(instr_hi, (u16 *)(instrptr + 2)); | |
42 | if (fault) | |
43 | return 0; | |
44 | ||
45 | tinstr = instr_hi | ((unsigned long)instr_low << 16); | |
46 | ||
47 | if (((tinstr >> 21) & 0x1F) != 2) | |
48 | return 0; | |
49 | ||
50 | if ((tinstr & MTCR_MASK) == MTCR_DIST) { | |
51 | index = (tinstr >> 16) & 0x1F; | |
52 | if (index > 13) | |
53 | return 0; | |
54 | ||
55 | tmp = tinstr & 0x1F; | |
56 | if (tmp > 2) | |
57 | return 0; | |
58 | ||
59 | regx = *(®s->a0 + index); | |
60 | ||
61 | if (tmp == 1) | |
62 | mtcr("cr<1, 2>", regx); | |
63 | else if (tmp == 2) | |
64 | mtcr("cr<2, 2>", regx); | |
65 | else | |
66 | return 0; | |
67 | ||
68 | regs->pc += 4; | |
69 | return 1; | |
70 | } | |
71 | ||
72 | if ((tinstr & MFCR_MASK) == MFCR_DIST) { | |
73 | index = tinstr & 0x1F; | |
74 | if (index > 13) | |
75 | return 0; | |
76 | ||
77 | tmp = ((tinstr >> 16) & 0x1F); | |
78 | if (tmp > 2) | |
79 | return 0; | |
80 | ||
81 | if (tmp == 1) | |
82 | regx = mfcr("cr<1, 2>"); | |
83 | else if (tmp == 2) | |
84 | regx = mfcr("cr<2, 2>"); | |
85 | else | |
86 | return 0; | |
87 | ||
88 | *(®s->a0 + index) = regx; | |
89 | ||
90 | regs->pc += 4; | |
91 | return 1; | |
92 | } | |
93 | ||
94 | return 0; | |
95 | } | |
96 | ||
97 | void fpu_fpe(struct pt_regs *regs) | |
98 | { | |
99 | int sig, code; | |
100 | unsigned int fesr; | |
101 | ||
102 | fesr = mfcr("cr<2, 2>"); | |
103 | ||
104 | sig = SIGFPE; | |
105 | code = FPE_FLTUNK; | |
106 | ||
107 | if (fesr & FPE_ILLE) { | |
108 | sig = SIGILL; | |
109 | code = ILL_ILLOPC; | |
110 | } else if (fesr & FPE_IDC) { | |
111 | sig = SIGILL; | |
112 | code = ILL_ILLOPN; | |
113 | } else if (fesr & FPE_FEC) { | |
114 | sig = SIGFPE; | |
115 | if (fesr & FPE_IOC) | |
116 | code = FPE_FLTINV; | |
117 | else if (fesr & FPE_DZC) | |
118 | code = FPE_FLTDIV; | |
119 | else if (fesr & FPE_UFC) | |
120 | code = FPE_FLTUND; | |
121 | else if (fesr & FPE_OFC) | |
122 | code = FPE_FLTOVF; | |
123 | else if (fesr & FPE_IXC) | |
124 | code = FPE_FLTRES; | |
125 | } | |
126 | ||
2e1661d2 | 127 | force_sig_fault(sig, code, (void __user *)regs->pc); |
e9564df7 GR |
128 | } |
129 | ||
130 | #define FMFVR_FPU_REGS(vrx, vry) \ | |
131 | "fmfvrl %0, "#vrx"\n" \ | |
132 | "fmfvrh %1, "#vrx"\n" \ | |
133 | "fmfvrl %2, "#vry"\n" \ | |
134 | "fmfvrh %3, "#vry"\n" | |
135 | ||
136 | #define FMTVR_FPU_REGS(vrx, vry) \ | |
137 | "fmtvrl "#vrx", %0\n" \ | |
138 | "fmtvrh "#vrx", %1\n" \ | |
139 | "fmtvrl "#vry", %2\n" \ | |
140 | "fmtvrh "#vry", %3\n" | |
141 | ||
142 | #define STW_FPU_REGS(a, b, c, d) \ | |
143 | "stw %0, (%4, "#a")\n" \ | |
144 | "stw %1, (%4, "#b")\n" \ | |
145 | "stw %2, (%4, "#c")\n" \ | |
146 | "stw %3, (%4, "#d")\n" | |
147 | ||
148 | #define LDW_FPU_REGS(a, b, c, d) \ | |
149 | "ldw %0, (%4, "#a")\n" \ | |
150 | "ldw %1, (%4, "#b")\n" \ | |
151 | "ldw %2, (%4, "#c")\n" \ | |
152 | "ldw %3, (%4, "#d")\n" | |
153 | ||
154 | void save_to_user_fp(struct user_fp *user_fp) | |
155 | { | |
156 | unsigned long flg; | |
157 | unsigned long tmp1, tmp2; | |
158 | unsigned long *fpregs; | |
159 | ||
160 | local_irq_save(flg); | |
161 | ||
162 | tmp1 = mfcr("cr<1, 2>"); | |
163 | tmp2 = mfcr("cr<2, 2>"); | |
164 | ||
165 | user_fp->fcr = tmp1; | |
166 | user_fp->fesr = tmp2; | |
167 | ||
168 | fpregs = &user_fp->vr[0]; | |
169 | #ifdef CONFIG_CPU_HAS_FPUV2 | |
170 | #ifdef CONFIG_CPU_HAS_VDSP | |
171 | asm volatile( | |
172 | "vstmu.32 vr0-vr3, (%0)\n" | |
173 | "vstmu.32 vr4-vr7, (%0)\n" | |
174 | "vstmu.32 vr8-vr11, (%0)\n" | |
175 | "vstmu.32 vr12-vr15, (%0)\n" | |
176 | "fstmu.64 vr16-vr31, (%0)\n" | |
177 | : "+a"(fpregs) | |
178 | ::"memory"); | |
179 | #else | |
180 | asm volatile( | |
181 | "fstmu.64 vr0-vr31, (%0)\n" | |
182 | : "+a"(fpregs) | |
183 | ::"memory"); | |
184 | #endif | |
185 | #else | |
186 | { | |
187 | unsigned long tmp3, tmp4; | |
188 | ||
189 | asm volatile( | |
190 | FMFVR_FPU_REGS(vr0, vr1) | |
191 | STW_FPU_REGS(0, 4, 16, 20) | |
192 | FMFVR_FPU_REGS(vr2, vr3) | |
193 | STW_FPU_REGS(32, 36, 48, 52) | |
194 | FMFVR_FPU_REGS(vr4, vr5) | |
195 | STW_FPU_REGS(64, 68, 80, 84) | |
196 | FMFVR_FPU_REGS(vr6, vr7) | |
197 | STW_FPU_REGS(96, 100, 112, 116) | |
198 | "addi %4, 128\n" | |
199 | FMFVR_FPU_REGS(vr8, vr9) | |
200 | STW_FPU_REGS(0, 4, 16, 20) | |
201 | FMFVR_FPU_REGS(vr10, vr11) | |
202 | STW_FPU_REGS(32, 36, 48, 52) | |
203 | FMFVR_FPU_REGS(vr12, vr13) | |
204 | STW_FPU_REGS(64, 68, 80, 84) | |
205 | FMFVR_FPU_REGS(vr14, vr15) | |
206 | STW_FPU_REGS(96, 100, 112, 116) | |
207 | : "=a"(tmp1), "=a"(tmp2), "=a"(tmp3), | |
208 | "=a"(tmp4), "+a"(fpregs) | |
209 | ::"memory"); | |
210 | } | |
211 | #endif | |
212 | ||
213 | local_irq_restore(flg); | |
214 | } | |
215 | ||
216 | void restore_from_user_fp(struct user_fp *user_fp) | |
217 | { | |
218 | unsigned long flg; | |
219 | unsigned long tmp1, tmp2; | |
220 | unsigned long *fpregs; | |
221 | ||
222 | local_irq_save(flg); | |
223 | ||
224 | tmp1 = user_fp->fcr; | |
225 | tmp2 = user_fp->fesr; | |
226 | ||
227 | mtcr("cr<1, 2>", tmp1); | |
228 | mtcr("cr<2, 2>", tmp2); | |
229 | ||
230 | fpregs = &user_fp->vr[0]; | |
231 | #ifdef CONFIG_CPU_HAS_FPUV2 | |
232 | #ifdef CONFIG_CPU_HAS_VDSP | |
233 | asm volatile( | |
234 | "vldmu.32 vr0-vr3, (%0)\n" | |
235 | "vldmu.32 vr4-vr7, (%0)\n" | |
236 | "vldmu.32 vr8-vr11, (%0)\n" | |
237 | "vldmu.32 vr12-vr15, (%0)\n" | |
238 | "fldmu.64 vr16-vr31, (%0)\n" | |
239 | : "+a"(fpregs) | |
240 | ::"memory"); | |
241 | #else | |
242 | asm volatile( | |
243 | "fldmu.64 vr0-vr31, (%0)\n" | |
244 | : "+a"(fpregs) | |
245 | ::"memory"); | |
246 | #endif | |
247 | #else | |
248 | { | |
249 | unsigned long tmp3, tmp4; | |
250 | ||
251 | asm volatile( | |
252 | LDW_FPU_REGS(0, 4, 16, 20) | |
253 | FMTVR_FPU_REGS(vr0, vr1) | |
254 | LDW_FPU_REGS(32, 36, 48, 52) | |
255 | FMTVR_FPU_REGS(vr2, vr3) | |
256 | LDW_FPU_REGS(64, 68, 80, 84) | |
257 | FMTVR_FPU_REGS(vr4, vr5) | |
258 | LDW_FPU_REGS(96, 100, 112, 116) | |
259 | FMTVR_FPU_REGS(vr6, vr7) | |
260 | "addi %4, 128\n" | |
261 | LDW_FPU_REGS(0, 4, 16, 20) | |
262 | FMTVR_FPU_REGS(vr8, vr9) | |
263 | LDW_FPU_REGS(32, 36, 48, 52) | |
264 | FMTVR_FPU_REGS(vr10, vr11) | |
265 | LDW_FPU_REGS(64, 68, 80, 84) | |
266 | FMTVR_FPU_REGS(vr12, vr13) | |
267 | LDW_FPU_REGS(96, 100, 112, 116) | |
268 | FMTVR_FPU_REGS(vr14, vr15) | |
269 | : "=a"(tmp1), "=a"(tmp2), "=a"(tmp3), | |
270 | "=a"(tmp4), "+a"(fpregs) | |
271 | ::"memory"); | |
272 | } | |
273 | #endif | |
274 | local_irq_restore(flg); | |
275 | } |