diff options
author | Leonid Yegoshin <Leonid.Yegoshin@imgtec.com> | 2013-03-25 13:09:02 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2013-05-09 11:55:18 -0400 |
commit | 102cedc32a6e3cd537374a3678d407591d5a6fab (patch) | |
tree | 7a91493a82e65c0da845afb12020e1534d83ee5d /arch/mips | |
parent | cf6d905828c2c75ebe8c818901e71e09ffe6f629 (diff) |
MIPS: microMIPS: Floating point support.
Add logic needed to do floating point emulation in microMIPS mode.
Signed-off-by: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>
Signed-off-by: Steven J. Hill <Steven. Hill@imgtec.com>
Diffstat (limited to 'arch/mips')
-rw-r--r-- | arch/mips/include/asm/fpu_emulator.h | 6 | ||||
-rw-r--r-- | arch/mips/include/asm/inst.h | 9 | ||||
-rw-r--r-- | arch/mips/include/uapi/asm/inst.h | 5 | ||||
-rw-r--r-- | arch/mips/kernel/traps.c | 2 | ||||
-rw-r--r-- | arch/mips/kernel/unaligned.c | 22 | ||||
-rw-r--r-- | arch/mips/math-emu/cp1emu.c | 919 | ||||
-rw-r--r-- | arch/mips/math-emu/dsemul.c | 30 |
7 files changed, 893 insertions, 100 deletions
diff --git a/arch/mips/include/asm/fpu_emulator.h b/arch/mips/include/asm/fpu_emulator.h index 3b4092705567..2abb587d5ab4 100644 --- a/arch/mips/include/asm/fpu_emulator.h +++ b/arch/mips/include/asm/fpu_emulator.h | |||
@@ -54,6 +54,12 @@ do { \ | |||
54 | extern int mips_dsemul(struct pt_regs *regs, mips_instruction ir, | 54 | extern int mips_dsemul(struct pt_regs *regs, mips_instruction ir, |
55 | unsigned long cpc); | 55 | unsigned long cpc); |
56 | extern int do_dsemulret(struct pt_regs *xcp); | 56 | extern int do_dsemulret(struct pt_regs *xcp); |
57 | extern int fpu_emulator_cop1Handler(struct pt_regs *xcp, | ||
58 | struct mips_fpu_struct *ctx, int has_fpu, | ||
59 | void *__user *fault_addr); | ||
60 | int process_fpemu_return(int sig, void __user *fault_addr); | ||
61 | int mm_isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, | ||
62 | unsigned long *contpc); | ||
57 | 63 | ||
58 | /* | 64 | /* |
59 | * Instruction inserted following the badinst to further tag the sequence | 65 | * Instruction inserted following the badinst to further tag the sequence |
diff --git a/arch/mips/include/asm/inst.h b/arch/mips/include/asm/inst.h index f1eadf764071..b27091e676c1 100644 --- a/arch/mips/include/asm/inst.h +++ b/arch/mips/include/asm/inst.h | |||
@@ -73,4 +73,13 @@ | |||
73 | 73 | ||
74 | typedef unsigned int mips_instruction; | 74 | typedef unsigned int mips_instruction; |
75 | 75 | ||
76 | /* microMIPS instruction decode structure. Do NOT export!!! */ | ||
77 | struct mm_decoded_insn { | ||
78 | mips_instruction insn; | ||
79 | mips_instruction next_insn; | ||
80 | int pc_inc; | ||
81 | int next_pc_inc; | ||
82 | int micro_mips_mode; | ||
83 | }; | ||
84 | |||
76 | #endif /* _ASM_INST_H */ | 85 | #endif /* _ASM_INST_H */ |
diff --git a/arch/mips/include/uapi/asm/inst.h b/arch/mips/include/uapi/asm/inst.h index 471533778a69..23ec2f53666f 100644 --- a/arch/mips/include/uapi/asm/inst.h +++ b/arch/mips/include/uapi/asm/inst.h | |||
@@ -424,6 +424,11 @@ enum mm_16d_minor_op { | |||
424 | }; | 424 | }; |
425 | 425 | ||
426 | /* | 426 | /* |
427 | * (microMIPS & MIPS16e) NOP instruction. | ||
428 | */ | ||
429 | #define MM_NOP16 0x0c00 | ||
430 | |||
431 | /* | ||
427 | * Damn ... bitfields depend from byteorder :-( | 432 | * Damn ... bitfields depend from byteorder :-( |
428 | */ | 433 | */ |
429 | #ifdef __MIPSEB__ | 434 | #ifdef __MIPSEB__ |
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 4b6b607b0179..333782b83164 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c | |||
@@ -675,7 +675,7 @@ asmlinkage void do_ov(struct pt_regs *regs) | |||
675 | force_sig_info(SIGFPE, &info, current); | 675 | force_sig_info(SIGFPE, &info, current); |
676 | } | 676 | } |
677 | 677 | ||
678 | static int process_fpemu_return(int sig, void __user *fault_addr) | 678 | int process_fpemu_return(int sig, void __user *fault_addr) |
679 | { | 679 | { |
680 | if (sig == SIGSEGV || sig == SIGBUS) { | 680 | if (sig == SIGSEGV || sig == SIGBUS) { |
681 | struct siginfo si = {0}; | 681 | struct siginfo si = {0}; |
diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c index 6087a54c86a0..f4c94ff3e3d3 100644 --- a/arch/mips/kernel/unaligned.c +++ b/arch/mips/kernel/unaligned.c | |||
@@ -83,6 +83,8 @@ | |||
83 | #include <asm/branch.h> | 83 | #include <asm/branch.h> |
84 | #include <asm/byteorder.h> | 84 | #include <asm/byteorder.h> |
85 | #include <asm/cop2.h> | 85 | #include <asm/cop2.h> |
86 | #include <asm/fpu.h> | ||
87 | #include <asm/fpu_emulator.h> | ||
86 | #include <asm/inst.h> | 88 | #include <asm/inst.h> |
87 | #include <asm/uaccess.h> | 89 | #include <asm/uaccess.h> |
88 | 90 | ||
@@ -108,6 +110,7 @@ static void emulate_load_store_insn(struct pt_regs *regs, | |||
108 | union mips_instruction insn; | 110 | union mips_instruction insn; |
109 | unsigned long value; | 111 | unsigned long value; |
110 | unsigned int res; | 112 | unsigned int res; |
113 | void __user *fault_addr = NULL; | ||
111 | 114 | ||
112 | perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0); | 115 | perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0); |
113 | 116 | ||
@@ -447,10 +450,21 @@ static void emulate_load_store_insn(struct pt_regs *regs, | |||
447 | case ldc1_op: | 450 | case ldc1_op: |
448 | case swc1_op: | 451 | case swc1_op: |
449 | case sdc1_op: | 452 | case sdc1_op: |
450 | /* | 453 | die_if_kernel("Unaligned FP access in kernel code", regs); |
451 | * I herewith declare: this does not happen. So send SIGBUS. | 454 | BUG_ON(!used_math()); |
452 | */ | 455 | BUG_ON(!is_fpu_owner()); |
453 | goto sigbus; | 456 | |
457 | lose_fpu(1); /* Save FPU state for the emulator. */ | ||
458 | res = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1, | ||
459 | &fault_addr); | ||
460 | own_fpu(1); /* Restore FPU state. */ | ||
461 | |||
462 | /* Signal if something went wrong. */ | ||
463 | process_fpemu_return(res, fault_addr); | ||
464 | |||
465 | if (res == 0) | ||
466 | break; | ||
467 | return; | ||
454 | 468 | ||
455 | /* | 469 | /* |
456 | * COP2 is available to implementor for application specific use. | 470 | * COP2 is available to implementor for application specific use. |
diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c index afb5a0bcf7a5..f03771900813 100644 --- a/arch/mips/math-emu/cp1emu.c +++ b/arch/mips/math-emu/cp1emu.c | |||
@@ -45,6 +45,7 @@ | |||
45 | #include <asm/signal.h> | 45 | #include <asm/signal.h> |
46 | #include <asm/mipsregs.h> | 46 | #include <asm/mipsregs.h> |
47 | #include <asm/fpu_emulator.h> | 47 | #include <asm/fpu_emulator.h> |
48 | #include <asm/fpu.h> | ||
48 | #include <asm/uaccess.h> | 49 | #include <asm/uaccess.h> |
49 | #include <asm/branch.h> | 50 | #include <asm/branch.h> |
50 | 51 | ||
@@ -81,6 +82,11 @@ DEFINE_PER_CPU(struct mips_fpu_emulator_stats, fpuemustats); | |||
81 | /* Determine rounding mode from the RM bits of the FCSR */ | 82 | /* Determine rounding mode from the RM bits of the FCSR */ |
82 | #define modeindex(v) ((v) & FPU_CSR_RM) | 83 | #define modeindex(v) ((v) & FPU_CSR_RM) |
83 | 84 | ||
85 | /* microMIPS bitfields */ | ||
86 | #define MM_POOL32A_MINOR_MASK 0x3f | ||
87 | #define MM_POOL32A_MINOR_SHIFT 0x6 | ||
88 | #define MM_MIPS32_COND_FC 0x30 | ||
89 | |||
84 | /* Convert Mips rounding mode (0..3) to IEEE library modes. */ | 90 | /* Convert Mips rounding mode (0..3) to IEEE library modes. */ |
85 | static const unsigned char ieee_rm[4] = { | 91 | static const unsigned char ieee_rm[4] = { |
86 | [FPU_CSR_RN] = IEEE754_RN, | 92 | [FPU_CSR_RN] = IEEE754_RN, |
@@ -110,6 +116,556 @@ static const unsigned int fpucondbit[8] = { | |||
110 | }; | 116 | }; |
111 | #endif | 117 | #endif |
112 | 118 | ||
119 | /* (microMIPS) Convert 16-bit register encoding to 32-bit register encoding. */ | ||
120 | static const unsigned int reg16to32map[8] = {16, 17, 2, 3, 4, 5, 6, 7}; | ||
121 | |||
122 | /* (microMIPS) Convert certain microMIPS instructions to MIPS32 format. */ | ||
123 | static const int sd_format[] = {16, 17, 0, 0, 0, 0, 0, 0}; | ||
124 | static const int sdps_format[] = {16, 17, 22, 0, 0, 0, 0, 0}; | ||
125 | static const int dwl_format[] = {17, 20, 21, 0, 0, 0, 0, 0}; | ||
126 | static const int swl_format[] = {16, 20, 21, 0, 0, 0, 0, 0}; | ||
127 | |||
128 | /* | ||
129 | * This functions translates a 32-bit microMIPS instruction | ||
130 | * into a 32-bit MIPS32 instruction. Returns 0 on success | ||
131 | * and SIGILL otherwise. | ||
132 | */ | ||
133 | static int microMIPS32_to_MIPS32(union mips_instruction *insn_ptr) | ||
134 | { | ||
135 | union mips_instruction insn = *insn_ptr; | ||
136 | union mips_instruction mips32_insn = insn; | ||
137 | int func, fmt, op; | ||
138 | |||
139 | switch (insn.mm_i_format.opcode) { | ||
140 | case mm_ldc132_op: | ||
141 | mips32_insn.mm_i_format.opcode = ldc1_op; | ||
142 | mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; | ||
143 | mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; | ||
144 | break; | ||
145 | case mm_lwc132_op: | ||
146 | mips32_insn.mm_i_format.opcode = lwc1_op; | ||
147 | mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; | ||
148 | mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; | ||
149 | break; | ||
150 | case mm_sdc132_op: | ||
151 | mips32_insn.mm_i_format.opcode = sdc1_op; | ||
152 | mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; | ||
153 | mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; | ||
154 | break; | ||
155 | case mm_swc132_op: | ||
156 | mips32_insn.mm_i_format.opcode = swc1_op; | ||
157 | mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; | ||
158 | mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; | ||
159 | break; | ||
160 | case mm_pool32i_op: | ||
161 | /* NOTE: offset is << by 1 if in microMIPS mode. */ | ||
162 | if ((insn.mm_i_format.rt == mm_bc1f_op) || | ||
163 | (insn.mm_i_format.rt == mm_bc1t_op)) { | ||
164 | mips32_insn.fb_format.opcode = cop1_op; | ||
165 | mips32_insn.fb_format.bc = bc_op; | ||
166 | mips32_insn.fb_format.flag = | ||
167 | (insn.mm_i_format.rt == mm_bc1t_op) ? 1 : 0; | ||
168 | } else | ||
169 | return SIGILL; | ||
170 | break; | ||
171 | case mm_pool32f_op: | ||
172 | switch (insn.mm_fp0_format.func) { | ||
173 | case mm_32f_01_op: | ||
174 | case mm_32f_11_op: | ||
175 | case mm_32f_02_op: | ||
176 | case mm_32f_12_op: | ||
177 | case mm_32f_41_op: | ||
178 | case mm_32f_51_op: | ||
179 | case mm_32f_42_op: | ||
180 | case mm_32f_52_op: | ||
181 | op = insn.mm_fp0_format.func; | ||
182 | if (op == mm_32f_01_op) | ||
183 | func = madd_s_op; | ||
184 | else if (op == mm_32f_11_op) | ||
185 | func = madd_d_op; | ||
186 | else if (op == mm_32f_02_op) | ||
187 | func = nmadd_s_op; | ||
188 | else if (op == mm_32f_12_op) | ||
189 | func = nmadd_d_op; | ||
190 | else if (op == mm_32f_41_op) | ||
191 | func = msub_s_op; | ||
192 | else if (op == mm_32f_51_op) | ||
193 | func = msub_d_op; | ||
194 | else if (op == mm_32f_42_op) | ||
195 | func = nmsub_s_op; | ||
196 | else | ||
197 | func = nmsub_d_op; | ||
198 | mips32_insn.fp6_format.opcode = cop1x_op; | ||
199 | mips32_insn.fp6_format.fr = insn.mm_fp6_format.fr; | ||
200 | mips32_insn.fp6_format.ft = insn.mm_fp6_format.ft; | ||
201 | mips32_insn.fp6_format.fs = insn.mm_fp6_format.fs; | ||
202 | mips32_insn.fp6_format.fd = insn.mm_fp6_format.fd; | ||
203 | mips32_insn.fp6_format.func = func; | ||
204 | break; | ||
205 | case mm_32f_10_op: | ||
206 | func = -1; /* Invalid */ | ||
207 | op = insn.mm_fp5_format.op & 0x7; | ||
208 | if (op == mm_ldxc1_op) | ||
209 | func = ldxc1_op; | ||
210 | else if (op == mm_sdxc1_op) | ||
211 | func = sdxc1_op; | ||
212 | else if (op == mm_lwxc1_op) | ||
213 | func = lwxc1_op; | ||
214 | else if (op == mm_swxc1_op) | ||
215 | func = swxc1_op; | ||
216 | |||
217 | if (func != -1) { | ||
218 | mips32_insn.r_format.opcode = cop1x_op; | ||
219 | mips32_insn.r_format.rs = | ||
220 | insn.mm_fp5_format.base; | ||
221 | mips32_insn.r_format.rt = | ||
222 | insn.mm_fp5_format.index; | ||
223 | mips32_insn.r_format.rd = 0; | ||
224 | mips32_insn.r_format.re = insn.mm_fp5_format.fd; | ||
225 | mips32_insn.r_format.func = func; | ||
226 | } else | ||
227 | return SIGILL; | ||
228 | break; | ||
229 | case mm_32f_40_op: | ||
230 | op = -1; /* Invalid */ | ||
231 | if (insn.mm_fp2_format.op == mm_fmovt_op) | ||
232 | op = 1; | ||
233 | else if (insn.mm_fp2_format.op == mm_fmovf_op) | ||
234 | op = 0; | ||
235 | if (op != -1) { | ||
236 | mips32_insn.fp0_format.opcode = cop1_op; | ||
237 | mips32_insn.fp0_format.fmt = | ||
238 | sdps_format[insn.mm_fp2_format.fmt]; | ||
239 | mips32_insn.fp0_format.ft = | ||
240 | (insn.mm_fp2_format.cc<<2) + op; | ||
241 | mips32_insn.fp0_format.fs = | ||
242 | insn.mm_fp2_format.fs; | ||
243 | mips32_insn.fp0_format.fd = | ||
244 | insn.mm_fp2_format.fd; | ||
245 | mips32_insn.fp0_format.func = fmovc_op; | ||
246 | } else | ||
247 | return SIGILL; | ||
248 | break; | ||
249 | case mm_32f_60_op: | ||
250 | func = -1; /* Invalid */ | ||
251 | if (insn.mm_fp0_format.op == mm_fadd_op) | ||
252 | func = fadd_op; | ||
253 | else if (insn.mm_fp0_format.op == mm_fsub_op) | ||
254 | func = fsub_op; | ||
255 | else if (insn.mm_fp0_format.op == mm_fmul_op) | ||
256 | func = fmul_op; | ||
257 | else if (insn.mm_fp0_format.op == mm_fdiv_op) | ||
258 | func = fdiv_op; | ||
259 | if (func != -1) { | ||
260 | mips32_insn.fp0_format.opcode = cop1_op; | ||
261 | mips32_insn.fp0_format.fmt = | ||
262 | sdps_format[insn.mm_fp0_format.fmt]; | ||
263 | mips32_insn.fp0_format.ft = | ||
264 | insn.mm_fp0_format.ft; | ||
265 | mips32_insn.fp0_format.fs = | ||
266 | insn.mm_fp0_format.fs; | ||
267 | mips32_insn.fp0_format.fd = | ||
268 | insn.mm_fp0_format.fd; | ||
269 | mips32_insn.fp0_format.func = func; | ||
270 | } else | ||
271 | return SIGILL; | ||
272 | break; | ||
273 | case mm_32f_70_op: | ||
274 | func = -1; /* Invalid */ | ||
275 | if (insn.mm_fp0_format.op == mm_fmovn_op) | ||
276 | func = fmovn_op; | ||
277 | else if (insn.mm_fp0_format.op == mm_fmovz_op) | ||
278 | func = fmovz_op; | ||
279 | if (func != -1) { | ||
280 | mips32_insn.fp0_format.opcode = cop1_op; | ||
281 | mips32_insn.fp0_format.fmt = | ||
282 | sdps_format[insn.mm_fp0_format.fmt]; | ||
283 | mips32_insn.fp0_format.ft = | ||
284 | insn.mm_fp0_format.ft; | ||
285 | mips32_insn.fp0_format.fs = | ||
286 | insn.mm_fp0_format.fs; | ||
287 | mips32_insn.fp0_format.fd = | ||
288 | insn.mm_fp0_format.fd; | ||
289 | mips32_insn.fp0_format.func = func; | ||
290 | } else | ||
291 | return SIGILL; | ||
292 | break; | ||
293 | case mm_32f_73_op: /* POOL32FXF */ | ||
294 | switch (insn.mm_fp1_format.op) { | ||
295 | case mm_movf0_op: | ||
296 | case mm_movf1_op: | ||
297 | case mm_movt0_op: | ||
298 | case mm_movt1_op: | ||
299 | if ((insn.mm_fp1_format.op & 0x7f) == | ||
300 | mm_movf0_op) | ||
301 | op = 0; | ||
302 | else | ||
303 | op = 1; | ||
304 | mips32_insn.r_format.opcode = spec_op; | ||
305 | mips32_insn.r_format.rs = insn.mm_fp4_format.fs; | ||
306 | mips32_insn.r_format.rt = | ||
307 | (insn.mm_fp4_format.cc << 2) + op; | ||
308 | mips32_insn.r_format.rd = insn.mm_fp4_format.rt; | ||
309 | mips32_insn.r_format.re = 0; | ||
310 | mips32_insn.r_format.func = movc_op; | ||
311 | break; | ||
312 | case mm_fcvtd0_op: | ||
313 | case mm_fcvtd1_op: | ||
314 | case mm_fcvts0_op: | ||
315 | case mm_fcvts1_op: | ||
316 | if ((insn.mm_fp1_format.op & 0x7f) == | ||
317 | mm_fcvtd0_op) { | ||
318 | func = fcvtd_op; | ||
319 | fmt = swl_format[insn.mm_fp3_format.fmt]; | ||
320 | } else { | ||
321 | func = fcvts_op; | ||
322 | fmt = dwl_format[insn.mm_fp3_format.fmt]; | ||
323 | } | ||
324 | mips32_insn.fp0_format.opcode = cop1_op; | ||
325 | mips32_insn.fp0_format.fmt = fmt; | ||
326 | mips32_insn.fp0_format.ft = 0; | ||
327 | mips32_insn.fp0_format.fs = | ||
328 | insn.mm_fp3_format.fs; | ||
329 | mips32_insn.fp0_format.fd = | ||
330 | insn.mm_fp3_format.rt; | ||
331 | mips32_insn.fp0_format.func = func; | ||
332 | break; | ||
333 | case mm_fmov0_op: | ||
334 | case mm_fmov1_op: | ||
335 | case mm_fabs0_op: | ||
336 | case mm_fabs1_op: | ||
337 | case mm_fneg0_op: | ||
338 | case mm_fneg1_op: | ||
339 | if ((insn.mm_fp1_format.op & 0x7f) == | ||
340 | mm_fmov0_op) | ||
341 | func = fmov_op; | ||
342 | else if ((insn.mm_fp1_format.op & 0x7f) == | ||
343 | mm_fabs0_op) | ||
344 | func = fabs_op; | ||
345 | else | ||
346 | func = fneg_op; | ||
347 | mips32_insn.fp0_format.opcode = cop1_op; | ||
348 | mips32_insn.fp0_format.fmt = | ||
349 | sdps_format[insn.mm_fp3_format.fmt]; | ||
350 | mips32_insn.fp0_format.ft = 0; | ||
351 | mips32_insn.fp0_format.fs = | ||
352 | insn.mm_fp3_format.fs; | ||
353 | mips32_insn.fp0_format.fd = | ||
354 | insn.mm_fp3_format.rt; | ||
355 | mips32_insn.fp0_format.func = func; | ||
356 | break; | ||
357 | case mm_ffloorl_op: | ||
358 | case mm_ffloorw_op: | ||
359 | case mm_fceill_op: | ||
360 | case mm_fceilw_op: | ||
361 | case mm_ftruncl_op: | ||
362 | case mm_ftruncw_op: | ||
363 | case mm_froundl_op: | ||
364 | case mm_froundw_op: | ||
365 | case mm_fcvtl_op: | ||
366 | case mm_fcvtw_op: | ||
367 | if (insn.mm_fp1_format.op == mm_ffloorl_op) | ||
368 | func = ffloorl_op; | ||
369 | else if (insn.mm_fp1_format.op == mm_ffloorw_op) | ||
370 | func = ffloor_op; | ||
371 | else if (insn.mm_fp1_format.op == mm_fceill_op) | ||
372 | func = fceill_op; | ||
373 | else if (insn.mm_fp1_format.op == mm_fceilw_op) | ||
374 | func = fceil_op; | ||
375 | else if (insn.mm_fp1_format.op == mm_ftruncl_op) | ||
376 | func = ftruncl_op; | ||
377 | else if (insn.mm_fp1_format.op == mm_ftruncw_op) | ||
378 | func = ftrunc_op; | ||
379 | else if (insn.mm_fp1_format.op == mm_froundl_op) | ||
380 | func = froundl_op; | ||
381 | else if (insn.mm_fp1_format.op == mm_froundw_op) | ||
382 | func = fround_op; | ||
383 | else if (insn.mm_fp1_format.op == mm_fcvtl_op) | ||
384 | func = fcvtl_op; | ||
385 | else | ||
386 | func = fcvtw_op; | ||
387 | mips32_insn.fp0_format.opcode = cop1_op; | ||
388 | mips32_insn.fp0_format.fmt = | ||
389 | sd_format[insn.mm_fp1_format.fmt]; | ||
390 | mips32_insn.fp0_format.ft = 0; | ||
391 | mips32_insn.fp0_format.fs = | ||
392 | insn.mm_fp1_format.fs; | ||
393 | mips32_insn.fp0_format.fd = | ||
394 | insn.mm_fp1_format.rt; | ||
395 | mips32_insn.fp0_format.func = func; | ||
396 | break; | ||
397 | case mm_frsqrt_op: | ||
398 | case mm_fsqrt_op: | ||
399 | case mm_frecip_op: | ||
400 | if (insn.mm_fp1_format.op == mm_frsqrt_op) | ||
401 | func = frsqrt_op; | ||
402 | else if (insn.mm_fp1_format.op == mm_fsqrt_op) | ||
403 | func = fsqrt_op; | ||
404 | else | ||
405 | func = frecip_op; | ||
406 | mips32_insn.fp0_format.opcode = cop1_op; | ||
407 | mips32_insn.fp0_format.fmt = | ||
408 | sdps_format[insn.mm_fp1_format.fmt]; | ||
409 | mips32_insn.fp0_format.ft = 0; | ||
410 | mips32_insn.fp0_format.fs = | ||
411 | insn.mm_fp1_format.fs; | ||
412 | mips32_insn.fp0_format.fd = | ||
413 | insn.mm_fp1_format.rt; | ||
414 | mips32_insn.fp0_format.func = func; | ||
415 | break; | ||
416 | case mm_mfc1_op: | ||
417 | case mm_mtc1_op: | ||
418 | case mm_cfc1_op: | ||
419 | case mm_ctc1_op: | ||
420 | if (insn.mm_fp1_format.op == mm_mfc1_op) | ||
421 | op = mfc_op; | ||
422 | else if (insn.mm_fp1_format.op == mm_mtc1_op) | ||
423 | op = mtc_op; | ||
424 | else if (insn.mm_fp1_format.op == mm_cfc1_op) | ||
425 | op = cfc_op; | ||
426 | else | ||
427 | op = ctc_op; | ||
428 | mips32_insn.fp1_format.opcode = cop1_op; | ||
429 | mips32_insn.fp1_format.op = op; | ||
430 | mips32_insn.fp1_format.rt = | ||
431 | insn.mm_fp1_format.rt; | ||
432 | mips32_insn.fp1_format.fs = | ||
433 | insn.mm_fp1_format.fs; | ||
434 | mips32_insn.fp1_format.fd = 0; | ||
435 | mips32_insn.fp1_format.func = 0; | ||
436 | break; | ||
437 | default: | ||
438 | return SIGILL; | ||
439 | break; | ||
440 | } | ||
441 | break; | ||
442 | case mm_32f_74_op: /* c.cond.fmt */ | ||
443 | mips32_insn.fp0_format.opcode = cop1_op; | ||
444 | mips32_insn.fp0_format.fmt = | ||
445 | sdps_format[insn.mm_fp4_format.fmt]; | ||
446 | mips32_insn.fp0_format.ft = insn.mm_fp4_format.rt; | ||
447 | mips32_insn.fp0_format.fs = insn.mm_fp4_format.fs; | ||
448 | mips32_insn.fp0_format.fd = insn.mm_fp4_format.cc << 2; | ||
449 | mips32_insn.fp0_format.func = | ||
450 | insn.mm_fp4_format.cond | MM_MIPS32_COND_FC; | ||
451 | break; | ||
452 | default: | ||
453 | return SIGILL; | ||
454 | break; | ||
455 | } | ||
456 | break; | ||
457 | default: | ||
458 | return SIGILL; | ||
459 | break; | ||
460 | } | ||
461 | |||
462 | *insn_ptr = mips32_insn; | ||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | int mm_isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, | ||
467 | unsigned long *contpc) | ||
468 | { | ||
469 | union mips_instruction insn = (union mips_instruction)dec_insn.insn; | ||
470 | int bc_false = 0; | ||
471 | unsigned int fcr31; | ||
472 | unsigned int bit; | ||
473 | |||
474 | switch (insn.mm_i_format.opcode) { | ||
475 | case mm_pool32a_op: | ||
476 | if ((insn.mm_i_format.simmediate & MM_POOL32A_MINOR_MASK) == | ||
477 | mm_pool32axf_op) { | ||
478 | switch (insn.mm_i_format.simmediate >> | ||
479 | MM_POOL32A_MINOR_SHIFT) { | ||
480 | case mm_jalr_op: | ||
481 | case mm_jalrhb_op: | ||
482 | case mm_jalrs_op: | ||
483 | case mm_jalrshb_op: | ||
484 | if (insn.mm_i_format.rt != 0) /* Not mm_jr */ | ||
485 | regs->regs[insn.mm_i_format.rt] = | ||
486 | regs->cp0_epc + | ||
487 | dec_insn.pc_inc + | ||
488 | dec_insn.next_pc_inc; | ||
489 | *contpc = regs->regs[insn.mm_i_format.rs]; | ||
490 | return 1; | ||
491 | break; | ||
492 | } | ||
493 | } | ||
494 | break; | ||
495 | case mm_pool32i_op: | ||
496 | switch (insn.mm_i_format.rt) { | ||
497 | case mm_bltzals_op: | ||
498 | case mm_bltzal_op: | ||
499 | regs->regs[31] = regs->cp0_epc + | ||
500 | dec_insn.pc_inc + | ||
501 | dec_insn.next_pc_inc; | ||
502 | /* Fall through */ | ||
503 | case mm_bltz_op: | ||
504 | if ((long)regs->regs[insn.mm_i_format.rs] < 0) | ||
505 | *contpc = regs->cp0_epc + | ||
506 | dec_insn.pc_inc + | ||
507 | (insn.mm_i_format.simmediate << 1); | ||
508 | else | ||
509 | *contpc = regs->cp0_epc + | ||
510 | dec_insn.pc_inc + | ||
511 | dec_insn.next_pc_inc; | ||
512 | return 1; | ||
513 | break; | ||
514 | case mm_bgezals_op: | ||
515 | case mm_bgezal_op: | ||
516 | regs->regs[31] = regs->cp0_epc + | ||
517 | dec_insn.pc_inc + | ||
518 | dec_insn.next_pc_inc; | ||
519 | /* Fall through */ | ||
520 | case mm_bgez_op: | ||
521 | if ((long)regs->regs[insn.mm_i_format.rs] >= 0) | ||
522 | *contpc = regs->cp0_epc + | ||
523 | dec_insn.pc_inc + | ||
524 | (insn.mm_i_format.simmediate << 1); | ||
525 | else | ||
526 | *contpc = regs->cp0_epc + | ||
527 | dec_insn.pc_inc + | ||
528 | dec_insn.next_pc_inc; | ||
529 | return 1; | ||
530 | break; | ||
531 | case mm_blez_op: | ||
532 | if ((long)regs->regs[insn.mm_i_format.rs] <= 0) | ||
533 | *contpc = regs->cp0_epc + | ||
534 | dec_insn.pc_inc + | ||
535 | (insn.mm_i_format.simmediate << 1); | ||
536 | else | ||
537 | *contpc = regs->cp0_epc + | ||
538 | dec_insn.pc_inc + | ||
539 | dec_insn.next_pc_inc; | ||
540 | return 1; | ||
541 | break; | ||
542 | case mm_bgtz_op: | ||
543 | if ((long)regs->regs[insn.mm_i_format.rs] <= 0) | ||
544 | *contpc = regs->cp0_epc + | ||
545 | dec_insn.pc_inc + | ||
546 | (insn.mm_i_format.simmediate << 1); | ||
547 | else | ||
548 | *contpc = regs->cp0_epc + | ||
549 | dec_insn.pc_inc + | ||
550 | dec_insn.next_pc_inc; | ||
551 | return 1; | ||
552 | break; | ||
553 | case mm_bc2f_op: | ||
554 | case mm_bc1f_op: | ||
555 | bc_false = 1; | ||
556 | /* Fall through */ | ||
557 | case mm_bc2t_op: | ||
558 | case mm_bc1t_op: | ||
559 | preempt_disable(); | ||
560 | if (is_fpu_owner()) | ||
561 | asm volatile("cfc1\t%0,$31" : "=r" (fcr31)); | ||
562 | else | ||
563 | fcr31 = current->thread.fpu.fcr31; | ||
564 | preempt_enable(); | ||
565 | |||
566 | if (bc_false) | ||
567 | fcr31 = ~fcr31; | ||
568 | |||
569 | bit = (insn.mm_i_format.rs >> 2); | ||
570 | bit += (bit != 0); | ||
571 | bit += 23; | ||
572 | if (fcr31 & (1 << bit)) | ||
573 | *contpc = regs->cp0_epc + | ||
574 | dec_insn.pc_inc + | ||
575 | (insn.mm_i_format.simmediate << 1); | ||
576 | else | ||
577 | *contpc = regs->cp0_epc + | ||
578 | dec_insn.pc_inc + dec_insn.next_pc_inc; | ||
579 | return 1; | ||
580 | break; | ||
581 | } | ||
582 | break; | ||
583 | case mm_pool16c_op: | ||
584 | switch (insn.mm_i_format.rt) { | ||
585 | case mm_jalr16_op: | ||
586 | case mm_jalrs16_op: | ||
587 | regs->regs[31] = regs->cp0_epc + | ||
588 | dec_insn.pc_inc + dec_insn.next_pc_inc; | ||
589 | /* Fall through */ | ||
590 | case mm_jr16_op: | ||
591 | *contpc = regs->regs[insn.mm_i_format.rs]; | ||
592 | return 1; | ||
593 | break; | ||
594 | } | ||
595 | break; | ||
596 | case mm_beqz16_op: | ||
597 | if ((long)regs->regs[reg16to32map[insn.mm_b1_format.rs]] == 0) | ||
598 | *contpc = regs->cp0_epc + | ||
599 | dec_insn.pc_inc + | ||
600 | (insn.mm_b1_format.simmediate << 1); | ||
601 | else | ||
602 | *contpc = regs->cp0_epc + | ||
603 | dec_insn.pc_inc + dec_insn.next_pc_inc; | ||
604 | return 1; | ||
605 | break; | ||
606 | case mm_bnez16_op: | ||
607 | if ((long)regs->regs[reg16to32map[insn.mm_b1_format.rs]] != 0) | ||
608 | *contpc = regs->cp0_epc + | ||
609 | dec_insn.pc_inc + | ||
610 | (insn.mm_b1_format.simmediate << 1); | ||
611 | else | ||
612 | *contpc = regs->cp0_epc + | ||
613 | dec_insn.pc_inc + dec_insn.next_pc_inc; | ||
614 | return 1; | ||
615 | break; | ||
616 | case mm_b16_op: | ||
617 | *contpc = regs->cp0_epc + dec_insn.pc_inc + | ||
618 | (insn.mm_b0_format.simmediate << 1); | ||
619 | return 1; | ||
620 | break; | ||
621 | case mm_beq32_op: | ||
622 | if (regs->regs[insn.mm_i_format.rs] == | ||
623 | regs->regs[insn.mm_i_format.rt]) | ||
624 | *contpc = regs->cp0_epc + | ||
625 | dec_insn.pc_inc + | ||
626 | (insn.mm_i_format.simmediate << 1); | ||
627 | else | ||
628 | *contpc = regs->cp0_epc + | ||
629 | dec_insn.pc_inc + | ||
630 | dec_insn.next_pc_inc; | ||
631 | return 1; | ||
632 | break; | ||
633 | case mm_bne32_op: | ||
634 | if (regs->regs[insn.mm_i_format.rs] != | ||
635 | regs->regs[insn.mm_i_format.rt]) | ||
636 | *contpc = regs->cp0_epc + | ||
637 | dec_insn.pc_inc + | ||
638 | (insn.mm_i_format.simmediate << 1); | ||
639 | else | ||
640 | *contpc = regs->cp0_epc + | ||
641 | dec_insn.pc_inc + dec_insn.next_pc_inc; | ||
642 | return 1; | ||
643 | break; | ||
644 | case mm_jalx32_op: | ||
645 | regs->regs[31] = regs->cp0_epc + | ||
646 | dec_insn.pc_inc + dec_insn.next_pc_inc; | ||
647 | *contpc = regs->cp0_epc + dec_insn.pc_inc; | ||
648 | *contpc >>= 28; | ||
649 | *contpc <<= 28; | ||
650 | *contpc |= (insn.j_format.target << 2); | ||
651 | return 1; | ||
652 | break; | ||
653 | case mm_jals32_op: | ||
654 | case mm_jal32_op: | ||
655 | regs->regs[31] = regs->cp0_epc + | ||
656 | dec_insn.pc_inc + dec_insn.next_pc_inc; | ||
657 | /* Fall through */ | ||
658 | case mm_j32_op: | ||
659 | *contpc = regs->cp0_epc + dec_insn.pc_inc; | ||
660 | *contpc >>= 27; | ||
661 | *contpc <<= 27; | ||
662 | *contpc |= (insn.j_format.target << 1); | ||
663 | set_isa16_mode(*contpc); | ||
664 | return 1; | ||
665 | break; | ||
666 | } | ||
667 | return 0; | ||
668 | } | ||
113 | 669 | ||
114 | /* | 670 | /* |
115 | * Redundant with logic already in kernel/branch.c, | 671 | * Redundant with logic already in kernel/branch.c, |
@@ -117,53 +673,177 @@ static const unsigned int fpucondbit[8] = { | |||
117 | * a single subroutine should be used across both | 673 | * a single subroutine should be used across both |
118 | * modules. | 674 | * modules. |
119 | */ | 675 | */ |
120 | static int isBranchInstr(mips_instruction * i) | 676 | static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, |
677 | unsigned long *contpc) | ||
121 | { | 678 | { |
122 | switch (MIPSInst_OPCODE(*i)) { | 679 | union mips_instruction insn = (union mips_instruction)dec_insn.insn; |
680 | unsigned int fcr31; | ||
681 | unsigned int bit = 0; | ||
682 | |||
683 | switch (insn.i_format.opcode) { | ||
123 | case spec_op: | 684 | case spec_op: |
124 | switch (MIPSInst_FUNC(*i)) { | 685 | switch (insn.r_format.func) { |
125 | case jalr_op: | 686 | case jalr_op: |
687 | regs->regs[insn.r_format.rd] = | ||
688 | regs->cp0_epc + dec_insn.pc_inc + | ||
689 | dec_insn.next_pc_inc; | ||
690 | /* Fall through */ | ||
126 | case jr_op: | 691 | case jr_op: |
692 | *contpc = regs->regs[insn.r_format.rs]; | ||
127 | return 1; | 693 | return 1; |
694 | break; | ||
128 | } | 695 | } |
129 | break; | 696 | break; |
130 | |||
131 | case bcond_op: | 697 | case bcond_op: |
132 | switch (MIPSInst_RT(*i)) { | 698 | switch (insn.i_format.rt) { |
699 | case bltzal_op: | ||
700 | case bltzall_op: | ||
701 | regs->regs[31] = regs->cp0_epc + | ||
702 | dec_insn.pc_inc + | ||
703 | dec_insn.next_pc_inc; | ||
704 | /* Fall through */ | ||
133 | case bltz_op: | 705 | case bltz_op: |
134 | case bgez_op: | ||
135 | case bltzl_op: | 706 | case bltzl_op: |
136 | case bgezl_op: | 707 | if ((long)regs->regs[insn.i_format.rs] < 0) |
137 | case bltzal_op: | 708 | *contpc = regs->cp0_epc + |
709 | dec_insn.pc_inc + | ||
710 | (insn.i_format.simmediate << 2); | ||
711 | else | ||
712 | *contpc = regs->cp0_epc + | ||
713 | dec_insn.pc_inc + | ||
714 | dec_insn.next_pc_inc; | ||
715 | return 1; | ||
716 | break; | ||
138 | case bgezal_op: | 717 | case bgezal_op: |
139 | case bltzall_op: | ||
140 | case bgezall_op: | 718 | case bgezall_op: |
719 | regs->regs[31] = regs->cp0_epc + | ||
720 | dec_insn.pc_inc + | ||
721 | dec_insn.next_pc_inc; | ||
722 | /* Fall through */ | ||
723 | case bgez_op: | ||
724 | case bgezl_op: | ||
725 | if ((long)regs->regs[insn.i_format.rs] >= 0) | ||
726 | *contpc = regs->cp0_epc + | ||
727 | dec_insn.pc_inc + | ||
728 | (insn.i_format.simmediate << 2); | ||
729 | else | ||
730 | *contpc = regs->cp0_epc + | ||
731 | dec_insn.pc_inc + | ||
732 | dec_insn.next_pc_inc; | ||
141 | return 1; | 733 | return 1; |
734 | break; | ||
142 | } | 735 | } |
143 | break; | 736 | break; |
144 | |||
145 | case j_op: | ||
146 | case jal_op: | ||
147 | case jalx_op: | 737 | case jalx_op: |
738 | set_isa16_mode(bit); | ||
739 | case jal_op: | ||
740 | regs->regs[31] = regs->cp0_epc + | ||
741 | dec_insn.pc_inc + | ||
742 | dec_insn.next_pc_inc; | ||
743 | /* Fall through */ | ||
744 | case j_op: | ||
745 | *contpc = regs->cp0_epc + dec_insn.pc_inc; | ||
746 | *contpc >>= 28; | ||
747 | *contpc <<= 28; | ||
748 | *contpc |= (insn.j_format.target << 2); | ||
749 | /* Set microMIPS mode bit: XOR for jalx. */ | ||
750 | *contpc ^= bit; | ||
751 | return 1; | ||
752 | break; | ||
148 | case beq_op: | 753 | case beq_op: |
149 | case bne_op: | ||
150 | case blez_op: | ||
151 | case bgtz_op: | ||
152 | case beql_op: | 754 | case beql_op: |
755 | if (regs->regs[insn.i_format.rs] == | ||
756 | regs->regs[insn.i_format.rt]) | ||
757 | *contpc = regs->cp0_epc + | ||
758 | dec_insn.pc_inc + | ||
759 | (insn.i_format.simmediate << 2); | ||
760 | else | ||
761 | *contpc = regs->cp0_epc + | ||
762 | dec_insn.pc_inc + | ||
763 | dec_insn.next_pc_inc; | ||
764 | return 1; | ||
765 | break; | ||
766 | case bne_op: | ||
153 | case bnel_op: | 767 | case bnel_op: |
768 | if (regs->regs[insn.i_format.rs] != | ||
769 | regs->regs[insn.i_format.rt]) | ||
770 | *contpc = regs->cp0_epc + | ||
771 | dec_insn.pc_inc + | ||
772 | (insn.i_format.simmediate << 2); | ||
773 | else | ||
774 | *contpc = regs->cp0_epc + | ||
775 | dec_insn.pc_inc + | ||
776 | dec_insn.next_pc_inc; | ||
777 | return 1; | ||
778 | break; | ||
779 | case blez_op: | ||
154 | case blezl_op: | 780 | case blezl_op: |
781 | if ((long)regs->regs[insn.i_format.rs] <= 0) | ||
782 | *contpc = regs->cp0_epc + | ||
783 | dec_insn.pc_inc + | ||
784 | (insn.i_format.simmediate << 2); | ||
785 | else | ||
786 | *contpc = regs->cp0_epc + | ||
787 | dec_insn.pc_inc + | ||
788 | dec_insn.next_pc_inc; | ||
789 | return 1; | ||
790 | break; | ||
791 | case bgtz_op: | ||
155 | case bgtzl_op: | 792 | case bgtzl_op: |
793 | if ((long)regs->regs[insn.i_format.rs] > 0) | ||
794 | *contpc = regs->cp0_epc + | ||
795 | dec_insn.pc_inc + | ||
796 | (insn.i_format.simmediate << 2); | ||
797 | else | ||
798 | *contpc = regs->cp0_epc + | ||
799 | dec_insn.pc_inc + | ||
800 | dec_insn.next_pc_inc; | ||
156 | return 1; | 801 | return 1; |
157 | 802 | break; | |
158 | case cop0_op: | 803 | case cop0_op: |
159 | case cop1_op: | 804 | case cop1_op: |
160 | case cop2_op: | 805 | case cop2_op: |
161 | case cop1x_op: | 806 | case cop1x_op: |
162 | if (MIPSInst_RS(*i) == bc_op) | 807 | if (insn.i_format.rs == bc_op) { |
163 | return 1; | 808 | preempt_disable(); |
809 | if (is_fpu_owner()) | ||
810 | asm volatile("cfc1\t%0,$31" : "=r" (fcr31)); | ||
811 | else | ||
812 | fcr31 = current->thread.fpu.fcr31; | ||
813 | preempt_enable(); | ||
814 | |||
815 | bit = (insn.i_format.rt >> 2); | ||
816 | bit += (bit != 0); | ||
817 | bit += 23; | ||
818 | switch (insn.i_format.rt & 3) { | ||
819 | case 0: /* bc1f */ | ||
820 | case 2: /* bc1fl */ | ||
821 | if (~fcr31 & (1 << bit)) | ||
822 | *contpc = regs->cp0_epc + | ||
823 | dec_insn.pc_inc + | ||
824 | (insn.i_format.simmediate << 2); | ||
825 | else | ||
826 | *contpc = regs->cp0_epc + | ||
827 | dec_insn.pc_inc + | ||
828 | dec_insn.next_pc_inc; | ||
829 | return 1; | ||
830 | break; | ||
831 | case 1: /* bc1t */ | ||
832 | case 3: /* bc1tl */ | ||
833 | if (fcr31 & (1 << bit)) | ||
834 | *contpc = regs->cp0_epc + | ||
835 | dec_insn.pc_inc + | ||
836 | (insn.i_format.simmediate << 2); | ||
837 | else | ||
838 | *contpc = regs->cp0_epc + | ||
839 | dec_insn.pc_inc + | ||
840 | dec_insn.next_pc_inc; | ||
841 | return 1; | ||
842 | break; | ||
843 | } | ||
844 | } | ||
164 | break; | 845 | break; |
165 | } | 846 | } |
166 | |||
167 | return 0; | 847 | return 0; |
168 | } | 848 | } |
169 | 849 | ||
@@ -210,26 +890,23 @@ static inline int cop1_64bit(struct pt_regs *xcp) | |||
210 | */ | 890 | */ |
211 | 891 | ||
212 | static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, | 892 | static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, |
213 | void *__user *fault_addr) | 893 | struct mm_decoded_insn dec_insn, void *__user *fault_addr) |
214 | { | 894 | { |
215 | mips_instruction ir; | 895 | mips_instruction ir; |
216 | unsigned long emulpc, contpc; | 896 | unsigned long contpc = xcp->cp0_epc + dec_insn.pc_inc; |
217 | unsigned int cond; | 897 | unsigned int cond; |
218 | 898 | int pc_inc; | |
219 | if (!access_ok(VERIFY_READ, xcp->cp0_epc, sizeof(mips_instruction))) { | ||
220 | MIPS_FPU_EMU_INC_STATS(errors); | ||
221 | *fault_addr = (mips_instruction __user *)xcp->cp0_epc; | ||
222 | return SIGBUS; | ||
223 | } | ||
224 | if (__get_user(ir, (mips_instruction __user *) xcp->cp0_epc)) { | ||
225 | MIPS_FPU_EMU_INC_STATS(errors); | ||
226 | *fault_addr = (mips_instruction __user *)xcp->cp0_epc; | ||
227 | return SIGSEGV; | ||
228 | } | ||
229 | 899 | ||
230 | /* XXX NEC Vr54xx bug workaround */ | 900 | /* XXX NEC Vr54xx bug workaround */ |
231 | if ((xcp->cp0_cause & CAUSEF_BD) && !isBranchInstr(&ir)) | 901 | if (xcp->cp0_cause & CAUSEF_BD) { |
232 | xcp->cp0_cause &= ~CAUSEF_BD; | 902 | if (dec_insn.micro_mips_mode) { |
903 | if (!mm_isBranchInstr(xcp, dec_insn, &contpc)) | ||
904 | xcp->cp0_cause &= ~CAUSEF_BD; | ||
905 | } else { | ||
906 | if (!isBranchInstr(xcp, dec_insn, &contpc)) | ||
907 | xcp->cp0_cause &= ~CAUSEF_BD; | ||
908 | } | ||
909 | } | ||
233 | 910 | ||
234 | if (xcp->cp0_cause & CAUSEF_BD) { | 911 | if (xcp->cp0_cause & CAUSEF_BD) { |
235 | /* | 912 | /* |
@@ -244,32 +921,33 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, | |||
244 | * Linux MIPS branch emulator operates on context, updating the | 921 | * Linux MIPS branch emulator operates on context, updating the |
245 | * cp0_epc. | 922 | * cp0_epc. |
246 | */ | 923 | */ |
247 | emulpc = xcp->cp0_epc + 4; /* Snapshot emulation target */ | 924 | ir = dec_insn.next_insn; /* process delay slot instr */ |
925 | pc_inc = dec_insn.next_pc_inc; | ||
926 | } else { | ||
927 | ir = dec_insn.insn; /* process current instr */ | ||
928 | pc_inc = dec_insn.pc_inc; | ||
929 | } | ||
248 | 930 | ||
249 | if (__compute_return_epc(xcp) < 0) { | 931 | /* |
250 | #ifdef CP1DBG | 932 | * Since microMIPS FPU instructios are a subset of MIPS32 FPU |
251 | printk("failed to emulate branch at %p\n", | 933 | * instructions, we want to convert microMIPS FPU instructions |
252 | (void *) (xcp->cp0_epc)); | 934 | * into MIPS32 instructions so that we could reuse all of the |
253 | #endif | 935 | * FPU emulation code. |
936 | * | ||
937 | * NOTE: We cannot do this for branch instructions since they | ||
938 | * are not a subset. Example: Cannot emulate a 16-bit | ||
939 | * aligned target address with a MIPS32 instruction. | ||
940 | */ | ||
941 | if (dec_insn.micro_mips_mode) { | ||
942 | /* | ||
943 | * If next instruction is a 16-bit instruction, then it | ||
944 | * it cannot be a FPU instruction. This could happen | ||
945 | * since we can be called for non-FPU instructions. | ||
946 | */ | ||
947 | if ((pc_inc == 2) || | ||
948 | (microMIPS32_to_MIPS32((union mips_instruction *)&ir) | ||
949 | == SIGILL)) | ||
254 | return SIGILL; | 950 | return SIGILL; |
255 | } | ||
256 | if (!access_ok(VERIFY_READ, emulpc, sizeof(mips_instruction))) { | ||
257 | MIPS_FPU_EMU_INC_STATS(errors); | ||
258 | *fault_addr = (mips_instruction __user *)emulpc; | ||
259 | return SIGBUS; | ||
260 | } | ||
261 | if (__get_user(ir, (mips_instruction __user *) emulpc)) { | ||
262 | MIPS_FPU_EMU_INC_STATS(errors); | ||
263 | *fault_addr = (mips_instruction __user *)emulpc; | ||
264 | return SIGSEGV; | ||
265 | } | ||
266 | /* __compute_return_epc() will have updated cp0_epc */ | ||
267 | contpc = xcp->cp0_epc; | ||
268 | /* In order not to confuse ptrace() et al, tweak context */ | ||
269 | xcp->cp0_epc = emulpc - 4; | ||
270 | } else { | ||
271 | emulpc = xcp->cp0_epc; | ||
272 | contpc = xcp->cp0_epc + 4; | ||
273 | } | 951 | } |
274 | 952 | ||
275 | emul: | 953 | emul: |
@@ -474,22 +1152,35 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, | |||
474 | /* branch taken: emulate dslot | 1152 | /* branch taken: emulate dslot |
475 | * instruction | 1153 | * instruction |
476 | */ | 1154 | */ |
477 | xcp->cp0_epc += 4; | 1155 | xcp->cp0_epc += dec_insn.pc_inc; |
478 | contpc = (xcp->cp0_epc + | 1156 | |
479 | (MIPSInst_SIMM(ir) << 2)); | 1157 | contpc = MIPSInst_SIMM(ir); |
480 | 1158 | ir = dec_insn.next_insn; | |
481 | if (!access_ok(VERIFY_READ, xcp->cp0_epc, | 1159 | if (dec_insn.micro_mips_mode) { |
482 | sizeof(mips_instruction))) { | 1160 | contpc = (xcp->cp0_epc + (contpc << 1)); |
483 | MIPS_FPU_EMU_INC_STATS(errors); | 1161 | |
484 | *fault_addr = (mips_instruction __user *)xcp->cp0_epc; | 1162 | /* If 16-bit instruction, not FPU. */ |
485 | return SIGBUS; | 1163 | if ((dec_insn.next_pc_inc == 2) || |
486 | } | 1164 | (microMIPS32_to_MIPS32((union mips_instruction *)&ir) == SIGILL)) { |
487 | if (__get_user(ir, | 1165 | |
488 | (mips_instruction __user *) xcp->cp0_epc)) { | 1166 | /* |
489 | MIPS_FPU_EMU_INC_STATS(errors); | 1167 | * Since this instruction will |
490 | *fault_addr = (mips_instruction __user *)xcp->cp0_epc; | 1168 | * be put on the stack with |
491 | return SIGSEGV; | 1169 | * 32-bit words, get around |
492 | } | 1170 | * this problem by putting a |
1171 | * NOP16 as the second one. | ||
1172 | */ | ||
1173 | if (dec_insn.next_pc_inc == 2) | ||
1174 | ir = (ir & (~0xffff)) | MM_NOP16; | ||
1175 | |||
1176 | /* | ||
1177 | * Single step the non-CP1 | ||
1178 | * instruction in the dslot. | ||
1179 | */ | ||
1180 | return mips_dsemul(xcp, ir, contpc); | ||
1181 | } | ||
1182 | } else | ||
1183 | contpc = (xcp->cp0_epc + (contpc << 2)); | ||
493 | 1184 | ||
494 | switch (MIPSInst_OPCODE(ir)) { | 1185 | switch (MIPSInst_OPCODE(ir)) { |
495 | case lwc1_op: | 1186 | case lwc1_op: |
@@ -525,8 +1216,8 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, | |||
525 | * branch likely nullifies | 1216 | * branch likely nullifies |
526 | * dslot if not taken | 1217 | * dslot if not taken |
527 | */ | 1218 | */ |
528 | xcp->cp0_epc += 4; | 1219 | xcp->cp0_epc += dec_insn.pc_inc; |
529 | contpc += 4; | 1220 | contpc += dec_insn.pc_inc; |
530 | /* | 1221 | /* |
531 | * else continue & execute | 1222 | * else continue & execute |
532 | * dslot as normal insn | 1223 | * dslot as normal insn |
@@ -1313,25 +2004,75 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx, | |||
1313 | int has_fpu, void *__user *fault_addr) | 2004 | int has_fpu, void *__user *fault_addr) |
1314 | { | 2005 | { |
1315 | unsigned long oldepc, prevepc; | 2006 | unsigned long oldepc, prevepc; |
1316 | mips_instruction insn; | 2007 | struct mm_decoded_insn dec_insn; |
2008 | u16 instr[4]; | ||
2009 | u16 *instr_ptr; | ||
1317 | int sig = 0; | 2010 | int sig = 0; |
1318 | 2011 | ||
1319 | oldepc = xcp->cp0_epc; | 2012 | oldepc = xcp->cp0_epc; |
1320 | do { | 2013 | do { |
1321 | prevepc = xcp->cp0_epc; | 2014 | prevepc = xcp->cp0_epc; |
1322 | 2015 | ||
1323 | if (!access_ok(VERIFY_READ, xcp->cp0_epc, sizeof(mips_instruction))) { | 2016 | if (get_isa16_mode(prevepc) && cpu_has_mmips) { |
1324 | MIPS_FPU_EMU_INC_STATS(errors); | 2017 | /* |
1325 | *fault_addr = (mips_instruction __user *)xcp->cp0_epc; | 2018 | * Get next 2 microMIPS instructions and convert them |
1326 | return SIGBUS; | 2019 | * into 32-bit instructions. |
1327 | } | 2020 | */ |
1328 | if (__get_user(insn, (mips_instruction __user *) xcp->cp0_epc)) { | 2021 | if ((get_user(instr[0], (u16 __user *)msk_isa16_mode(xcp->cp0_epc))) || |
1329 | MIPS_FPU_EMU_INC_STATS(errors); | 2022 | (get_user(instr[1], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 2))) || |
1330 | *fault_addr = (mips_instruction __user *)xcp->cp0_epc; | 2023 | (get_user(instr[2], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 4))) || |
1331 | return SIGSEGV; | 2024 | (get_user(instr[3], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 6)))) { |
2025 | MIPS_FPU_EMU_INC_STATS(errors); | ||
2026 | return SIGBUS; | ||
2027 | } | ||
2028 | instr_ptr = instr; | ||
2029 | |||
2030 | /* Get first instruction. */ | ||
2031 | if (mm_insn_16bit(*instr_ptr)) { | ||
2032 | /* Duplicate the half-word. */ | ||
2033 | dec_insn.insn = (*instr_ptr << 16) | | ||
2034 | (*instr_ptr); | ||
2035 | /* 16-bit instruction. */ | ||
2036 | dec_insn.pc_inc = 2; | ||
2037 | instr_ptr += 1; | ||
2038 | } else { | ||
2039 | dec_insn.insn = (*instr_ptr << 16) | | ||
2040 | *(instr_ptr+1); | ||
2041 | /* 32-bit instruction. */ | ||
2042 | dec_insn.pc_inc = 4; | ||
2043 | instr_ptr += 2; | ||
2044 | } | ||
2045 | /* Get second instruction. */ | ||
2046 | if (mm_insn_16bit(*instr_ptr)) { | ||
2047 | /* Duplicate the half-word. */ | ||
2048 | dec_insn.next_insn = (*instr_ptr << 16) | | ||
2049 | (*instr_ptr); | ||
2050 | /* 16-bit instruction. */ | ||
2051 | dec_insn.next_pc_inc = 2; | ||
2052 | } else { | ||
2053 | dec_insn.next_insn = (*instr_ptr << 16) | | ||
2054 | *(instr_ptr+1); | ||
2055 | /* 32-bit instruction. */ | ||
2056 | dec_insn.next_pc_inc = 4; | ||
2057 | } | ||
2058 | dec_insn.micro_mips_mode = 1; | ||
2059 | } else { | ||
2060 | if ((get_user(dec_insn.insn, | ||
2061 | (mips_instruction __user *) xcp->cp0_epc)) || | ||
2062 | (get_user(dec_insn.next_insn, | ||
2063 | (mips_instruction __user *)(xcp->cp0_epc+4)))) { | ||
2064 | MIPS_FPU_EMU_INC_STATS(errors); | ||
2065 | return SIGBUS; | ||
2066 | } | ||
2067 | dec_insn.pc_inc = 4; | ||
2068 | dec_insn.next_pc_inc = 4; | ||
2069 | dec_insn.micro_mips_mode = 0; | ||
1332 | } | 2070 | } |
1333 | if (insn == 0) | 2071 | |
1334 | xcp->cp0_epc += 4; /* skip nops */ | 2072 | if ((dec_insn.insn == 0) || |
2073 | ((dec_insn.pc_inc == 2) && | ||
2074 | ((dec_insn.insn & 0xffff) == MM_NOP16))) | ||
2075 | xcp->cp0_epc += dec_insn.pc_inc; /* Skip NOPs */ | ||
1335 | else { | 2076 | else { |
1336 | /* | 2077 | /* |
1337 | * The 'ieee754_csr' is an alias of | 2078 | * The 'ieee754_csr' is an alias of |
@@ -1341,7 +2082,7 @@ int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx, | |||
1341 | */ | 2082 | */ |
1342 | /* convert to ieee library modes */ | 2083 | /* convert to ieee library modes */ |
1343 | ieee754_csr.rm = ieee_rm[ieee754_csr.rm]; | 2084 | ieee754_csr.rm = ieee_rm[ieee754_csr.rm]; |
1344 | sig = cop1Emulate(xcp, ctx, fault_addr); | 2085 | sig = cop1Emulate(xcp, ctx, dec_insn, fault_addr); |
1345 | /* revert to mips rounding mode */ | 2086 | /* revert to mips rounding mode */ |
1346 | ieee754_csr.rm = mips_rm[ieee754_csr.rm]; | 2087 | ieee754_csr.rm = mips_rm[ieee754_csr.rm]; |
1347 | } | 2088 | } |
diff --git a/arch/mips/math-emu/dsemul.c b/arch/mips/math-emu/dsemul.c index 384a3b0091ea..7ea622ab8dad 100644 --- a/arch/mips/math-emu/dsemul.c +++ b/arch/mips/math-emu/dsemul.c | |||
@@ -55,7 +55,9 @@ int mips_dsemul(struct pt_regs *regs, mips_instruction ir, unsigned long cpc) | |||
55 | struct emuframe __user *fr; | 55 | struct emuframe __user *fr; |
56 | int err; | 56 | int err; |
57 | 57 | ||
58 | if (ir == 0) { /* a nop is easy */ | 58 | if ((get_isa16_mode(regs->cp0_epc) && ((ir >> 16) == MM_NOP16)) || |
59 | (ir == 0)) { | ||
60 | /* NOP is easy */ | ||
59 | regs->cp0_epc = cpc; | 61 | regs->cp0_epc = cpc; |
60 | regs->cp0_cause &= ~CAUSEF_BD; | 62 | regs->cp0_cause &= ~CAUSEF_BD; |
61 | return 0; | 63 | return 0; |
@@ -91,8 +93,16 @@ int mips_dsemul(struct pt_regs *regs, mips_instruction ir, unsigned long cpc) | |||
91 | if (unlikely(!access_ok(VERIFY_WRITE, fr, sizeof(struct emuframe)))) | 93 | if (unlikely(!access_ok(VERIFY_WRITE, fr, sizeof(struct emuframe)))) |
92 | return SIGBUS; | 94 | return SIGBUS; |
93 | 95 | ||
94 | err = __put_user(ir, &fr->emul); | 96 | if (get_isa16_mode(regs->cp0_epc)) { |
95 | err |= __put_user((mips_instruction)BREAK_MATH, &fr->badinst); | 97 | err = __put_user(ir >> 16, (u16 __user *)(&fr->emul)); |
98 | err |= __put_user(ir & 0xffff, (u16 __user *)((long)(&fr->emul) + 2)); | ||
99 | err |= __put_user(BREAK_MATH >> 16, (u16 __user *)(&fr->badinst)); | ||
100 | err |= __put_user(BREAK_MATH & 0xffff, (u16 __user *)((long)(&fr->badinst) + 2)); | ||
101 | } else { | ||
102 | err = __put_user(ir, &fr->emul); | ||
103 | err |= __put_user((mips_instruction)BREAK_MATH, &fr->badinst); | ||
104 | } | ||
105 | |||
96 | err |= __put_user((mips_instruction)BD_COOKIE, &fr->cookie); | 106 | err |= __put_user((mips_instruction)BD_COOKIE, &fr->cookie); |
97 | err |= __put_user(cpc, &fr->epc); | 107 | err |= __put_user(cpc, &fr->epc); |
98 | 108 | ||
@@ -101,7 +111,8 @@ int mips_dsemul(struct pt_regs *regs, mips_instruction ir, unsigned long cpc) | |||
101 | return SIGBUS; | 111 | return SIGBUS; |
102 | } | 112 | } |
103 | 113 | ||
104 | regs->cp0_epc = (unsigned long) &fr->emul; | 114 | regs->cp0_epc = ((unsigned long) &fr->emul) | |
115 | get_isa16_mode(regs->cp0_epc); | ||
105 | 116 | ||
106 | flush_cache_sigtramp((unsigned long)&fr->badinst); | 117 | flush_cache_sigtramp((unsigned long)&fr->badinst); |
107 | 118 | ||
@@ -114,9 +125,10 @@ int do_dsemulret(struct pt_regs *xcp) | |||
114 | unsigned long epc; | 125 | unsigned long epc; |
115 | u32 insn, cookie; | 126 | u32 insn, cookie; |
116 | int err = 0; | 127 | int err = 0; |
128 | u16 instr[2]; | ||
117 | 129 | ||
118 | fr = (struct emuframe __user *) | 130 | fr = (struct emuframe __user *) |
119 | (xcp->cp0_epc - sizeof(mips_instruction)); | 131 | (msk_isa16_mode(xcp->cp0_epc) - sizeof(mips_instruction)); |
120 | 132 | ||
121 | /* | 133 | /* |
122 | * If we can't even access the area, something is very wrong, but we'll | 134 | * If we can't even access the area, something is very wrong, but we'll |
@@ -131,7 +143,13 @@ int do_dsemulret(struct pt_regs *xcp) | |||
131 | * - Is the instruction pointed to by the EPC an BREAK_MATH? | 143 | * - Is the instruction pointed to by the EPC an BREAK_MATH? |
132 | * - Is the following memory word the BD_COOKIE? | 144 | * - Is the following memory word the BD_COOKIE? |
133 | */ | 145 | */ |
134 | err = __get_user(insn, &fr->badinst); | 146 | if (get_isa16_mode(xcp->cp0_epc)) { |
147 | err = __get_user(instr[0], (u16 __user *)(&fr->badinst)); | ||
148 | err |= __get_user(instr[1], (u16 __user *)((long)(&fr->badinst) + 2)); | ||
149 | insn = (instr[0] << 16) | instr[1]; | ||
150 | } else { | ||
151 | err = __get_user(insn, &fr->badinst); | ||
152 | } | ||
135 | err |= __get_user(cookie, &fr->cookie); | 153 | err |= __get_user(cookie, &fr->cookie); |
136 | 154 | ||
137 | if (unlikely(err || (insn != BREAK_MATH) || (cookie != BD_COOKIE))) { | 155 | if (unlikely(err || (insn != BREAK_MATH) || (cookie != BD_COOKIE))) { |