11da177e4SLinus Torvalds /* 23f7cac41SRalf Baechle * cp1emu.c: a MIPS coprocessor 1 (FPU) instruction emulator 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * MIPS floating point support 51da177e4SLinus Torvalds * Copyright (C) 1994-2000 Algorithmics Ltd. 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com 81da177e4SLinus Torvalds * Copyright (C) 2000 MIPS Technologies, Inc. 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * This program is free software; you can distribute it and/or modify it 111da177e4SLinus Torvalds * under the terms of the GNU General Public License (Version 2) as 121da177e4SLinus Torvalds * published by the Free Software Foundation. 131da177e4SLinus Torvalds * 141da177e4SLinus Torvalds * This program is distributed in the hope it will be useful, but WITHOUT 151da177e4SLinus Torvalds * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 161da177e4SLinus Torvalds * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 171da177e4SLinus Torvalds * for more details. 181da177e4SLinus Torvalds * 191da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License along 201da177e4SLinus Torvalds * with this program; if not, write to the Free Software Foundation, Inc., 213f7cac41SRalf Baechle * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 221da177e4SLinus Torvalds * 231da177e4SLinus Torvalds * A complete emulator for MIPS coprocessor 1 instructions. This is 241da177e4SLinus Torvalds * required for #float(switch) or #float(trap), where it catches all 251da177e4SLinus Torvalds * COP1 instructions via the "CoProcessor Unusable" exception. 261da177e4SLinus Torvalds * 271da177e4SLinus Torvalds * More surprisingly it is also required for #float(ieee), to help out 283f7cac41SRalf Baechle * the hardware FPU at the boundaries of the IEEE-754 representation 291da177e4SLinus Torvalds * (denormalised values, infinities, underflow, etc). It is made 301da177e4SLinus Torvalds * quite nasty because emulation of some non-COP1 instructions is 311da177e4SLinus Torvalds * required, e.g. in branch delay slots. 321da177e4SLinus Torvalds * 333f7cac41SRalf Baechle * Note if you know that you won't have an FPU, then you'll get much 341da177e4SLinus Torvalds * better performance by compiling with -msoft-float! 351da177e4SLinus Torvalds */ 361da177e4SLinus Torvalds #include <linux/sched.h> 3783fd38caSAtsushi Nemoto #include <linux/debugfs.h> 3885c51c51SRalf Baechle #include <linux/percpu-defs.h> 397f788d2dSDeng-Cheng Zhu #include <linux/perf_event.h> 401da177e4SLinus Torvalds 41cd8ee345SRalf Baechle #include <asm/branch.h> 421da177e4SLinus Torvalds #include <asm/inst.h> 431da177e4SLinus Torvalds #include <asm/ptrace.h> 441da177e4SLinus Torvalds #include <asm/signal.h> 457c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 46cd8ee345SRalf Baechle 47f6843626SMaciej W. Rozycki #include <asm/cpu-info.h> 48cd8ee345SRalf Baechle #include <asm/processor.h> 491da177e4SLinus Torvalds #include <asm/fpu_emulator.h> 50102cedc3SLeonid Yegoshin #include <asm/fpu.h> 51b0a668fbSLeonid Yegoshin #include <asm/mips-r2-to-r6-emul.h> 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds #include "ieee754.h" 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds /* Function which emulates a floating point instruction. */ 561da177e4SLinus Torvalds 57eae89076SAtsushi Nemoto static int fpu_emu(struct pt_regs *, struct mips_fpu_struct *, 581da177e4SLinus Torvalds mips_instruction); 591da177e4SLinus Torvalds 601da177e4SLinus Torvalds static int fpux_emu(struct pt_regs *, 61445a58ceSPaul Burton struct mips_fpu_struct *, mips_instruction, void __user **); 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds /* Control registers */ 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds #define FPCREG_RID 0 /* $0 = revision id */ 66c491cfa2SMaciej W. Rozycki #define FPCREG_FCCR 25 /* $25 = fccr */ 67c491cfa2SMaciej W. Rozycki #define FPCREG_FEXR 26 /* $26 = fexr */ 68c491cfa2SMaciej W. Rozycki #define FPCREG_FENR 28 /* $28 = fenr */ 691da177e4SLinus Torvalds #define FPCREG_CSR 31 /* $31 = csr */ 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds /* convert condition code register number to csr bit */ 72b0a668fbSLeonid Yegoshin const unsigned int fpucondbit[8] = { 73c491cfa2SMaciej W. Rozycki FPU_CSR_COND, 741da177e4SLinus Torvalds FPU_CSR_COND1, 751da177e4SLinus Torvalds FPU_CSR_COND2, 761da177e4SLinus Torvalds FPU_CSR_COND3, 771da177e4SLinus Torvalds FPU_CSR_COND4, 781da177e4SLinus Torvalds FPU_CSR_COND5, 791da177e4SLinus Torvalds FPU_CSR_COND6, 801da177e4SLinus Torvalds FPU_CSR_COND7 811da177e4SLinus Torvalds }; 821da177e4SLinus Torvalds 83102cedc3SLeonid Yegoshin /* (microMIPS) Convert certain microMIPS instructions to MIPS32 format. */ 84102cedc3SLeonid Yegoshin static const int sd_format[] = {16, 17, 0, 0, 0, 0, 0, 0}; 85102cedc3SLeonid Yegoshin static const int sdps_format[] = {16, 17, 22, 0, 0, 0, 0, 0}; 86102cedc3SLeonid Yegoshin static const int dwl_format[] = {17, 20, 21, 0, 0, 0, 0, 0}; 87102cedc3SLeonid Yegoshin static const int swl_format[] = {16, 20, 21, 0, 0, 0, 0, 0}; 88102cedc3SLeonid Yegoshin 89102cedc3SLeonid Yegoshin /* 90102cedc3SLeonid Yegoshin * This functions translates a 32-bit microMIPS instruction 91102cedc3SLeonid Yegoshin * into a 32-bit MIPS32 instruction. Returns 0 on success 92102cedc3SLeonid Yegoshin * and SIGILL otherwise. 93102cedc3SLeonid Yegoshin */ 94102cedc3SLeonid Yegoshin static int microMIPS32_to_MIPS32(union mips_instruction *insn_ptr) 95102cedc3SLeonid Yegoshin { 96102cedc3SLeonid Yegoshin union mips_instruction insn = *insn_ptr; 97102cedc3SLeonid Yegoshin union mips_instruction mips32_insn = insn; 98102cedc3SLeonid Yegoshin int func, fmt, op; 99102cedc3SLeonid Yegoshin 100102cedc3SLeonid Yegoshin switch (insn.mm_i_format.opcode) { 101102cedc3SLeonid Yegoshin case mm_ldc132_op: 102102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.opcode = ldc1_op; 103102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 104102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 105102cedc3SLeonid Yegoshin break; 106102cedc3SLeonid Yegoshin case mm_lwc132_op: 107102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.opcode = lwc1_op; 108102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 109102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 110102cedc3SLeonid Yegoshin break; 111102cedc3SLeonid Yegoshin case mm_sdc132_op: 112102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.opcode = sdc1_op; 113102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 114102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 115102cedc3SLeonid Yegoshin break; 116102cedc3SLeonid Yegoshin case mm_swc132_op: 117102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.opcode = swc1_op; 118102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 119102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 120102cedc3SLeonid Yegoshin break; 121102cedc3SLeonid Yegoshin case mm_pool32i_op: 122102cedc3SLeonid Yegoshin /* NOTE: offset is << by 1 if in microMIPS mode. */ 123102cedc3SLeonid Yegoshin if ((insn.mm_i_format.rt == mm_bc1f_op) || 124102cedc3SLeonid Yegoshin (insn.mm_i_format.rt == mm_bc1t_op)) { 125102cedc3SLeonid Yegoshin mips32_insn.fb_format.opcode = cop1_op; 126102cedc3SLeonid Yegoshin mips32_insn.fb_format.bc = bc_op; 127102cedc3SLeonid Yegoshin mips32_insn.fb_format.flag = 128102cedc3SLeonid Yegoshin (insn.mm_i_format.rt == mm_bc1t_op) ? 1 : 0; 129102cedc3SLeonid Yegoshin } else 130102cedc3SLeonid Yegoshin return SIGILL; 131102cedc3SLeonid Yegoshin break; 132102cedc3SLeonid Yegoshin case mm_pool32f_op: 133102cedc3SLeonid Yegoshin switch (insn.mm_fp0_format.func) { 134102cedc3SLeonid Yegoshin case mm_32f_01_op: 135102cedc3SLeonid Yegoshin case mm_32f_11_op: 136102cedc3SLeonid Yegoshin case mm_32f_02_op: 137102cedc3SLeonid Yegoshin case mm_32f_12_op: 138102cedc3SLeonid Yegoshin case mm_32f_41_op: 139102cedc3SLeonid Yegoshin case mm_32f_51_op: 140102cedc3SLeonid Yegoshin case mm_32f_42_op: 141102cedc3SLeonid Yegoshin case mm_32f_52_op: 142102cedc3SLeonid Yegoshin op = insn.mm_fp0_format.func; 143102cedc3SLeonid Yegoshin if (op == mm_32f_01_op) 144102cedc3SLeonid Yegoshin func = madd_s_op; 145102cedc3SLeonid Yegoshin else if (op == mm_32f_11_op) 146102cedc3SLeonid Yegoshin func = madd_d_op; 147102cedc3SLeonid Yegoshin else if (op == mm_32f_02_op) 148102cedc3SLeonid Yegoshin func = nmadd_s_op; 149102cedc3SLeonid Yegoshin else if (op == mm_32f_12_op) 150102cedc3SLeonid Yegoshin func = nmadd_d_op; 151102cedc3SLeonid Yegoshin else if (op == mm_32f_41_op) 152102cedc3SLeonid Yegoshin func = msub_s_op; 153102cedc3SLeonid Yegoshin else if (op == mm_32f_51_op) 154102cedc3SLeonid Yegoshin func = msub_d_op; 155102cedc3SLeonid Yegoshin else if (op == mm_32f_42_op) 156102cedc3SLeonid Yegoshin func = nmsub_s_op; 157102cedc3SLeonid Yegoshin else 158102cedc3SLeonid Yegoshin func = nmsub_d_op; 159102cedc3SLeonid Yegoshin mips32_insn.fp6_format.opcode = cop1x_op; 160102cedc3SLeonid Yegoshin mips32_insn.fp6_format.fr = insn.mm_fp6_format.fr; 161102cedc3SLeonid Yegoshin mips32_insn.fp6_format.ft = insn.mm_fp6_format.ft; 162102cedc3SLeonid Yegoshin mips32_insn.fp6_format.fs = insn.mm_fp6_format.fs; 163102cedc3SLeonid Yegoshin mips32_insn.fp6_format.fd = insn.mm_fp6_format.fd; 164102cedc3SLeonid Yegoshin mips32_insn.fp6_format.func = func; 165102cedc3SLeonid Yegoshin break; 166102cedc3SLeonid Yegoshin case mm_32f_10_op: 167102cedc3SLeonid Yegoshin func = -1; /* Invalid */ 168102cedc3SLeonid Yegoshin op = insn.mm_fp5_format.op & 0x7; 169102cedc3SLeonid Yegoshin if (op == mm_ldxc1_op) 170102cedc3SLeonid Yegoshin func = ldxc1_op; 171102cedc3SLeonid Yegoshin else if (op == mm_sdxc1_op) 172102cedc3SLeonid Yegoshin func = sdxc1_op; 173102cedc3SLeonid Yegoshin else if (op == mm_lwxc1_op) 174102cedc3SLeonid Yegoshin func = lwxc1_op; 175102cedc3SLeonid Yegoshin else if (op == mm_swxc1_op) 176102cedc3SLeonid Yegoshin func = swxc1_op; 177102cedc3SLeonid Yegoshin 178102cedc3SLeonid Yegoshin if (func != -1) { 179102cedc3SLeonid Yegoshin mips32_insn.r_format.opcode = cop1x_op; 180102cedc3SLeonid Yegoshin mips32_insn.r_format.rs = 181102cedc3SLeonid Yegoshin insn.mm_fp5_format.base; 182102cedc3SLeonid Yegoshin mips32_insn.r_format.rt = 183102cedc3SLeonid Yegoshin insn.mm_fp5_format.index; 184102cedc3SLeonid Yegoshin mips32_insn.r_format.rd = 0; 185102cedc3SLeonid Yegoshin mips32_insn.r_format.re = insn.mm_fp5_format.fd; 186102cedc3SLeonid Yegoshin mips32_insn.r_format.func = func; 187102cedc3SLeonid Yegoshin } else 188102cedc3SLeonid Yegoshin return SIGILL; 189102cedc3SLeonid Yegoshin break; 190102cedc3SLeonid Yegoshin case mm_32f_40_op: 191102cedc3SLeonid Yegoshin op = -1; /* Invalid */ 192102cedc3SLeonid Yegoshin if (insn.mm_fp2_format.op == mm_fmovt_op) 193102cedc3SLeonid Yegoshin op = 1; 194102cedc3SLeonid Yegoshin else if (insn.mm_fp2_format.op == mm_fmovf_op) 195102cedc3SLeonid Yegoshin op = 0; 196102cedc3SLeonid Yegoshin if (op != -1) { 197102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 198102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 199102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp2_format.fmt]; 200102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 201102cedc3SLeonid Yegoshin (insn.mm_fp2_format.cc<<2) + op; 202102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 203102cedc3SLeonid Yegoshin insn.mm_fp2_format.fs; 204102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 205102cedc3SLeonid Yegoshin insn.mm_fp2_format.fd; 206102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = fmovc_op; 207102cedc3SLeonid Yegoshin } else 208102cedc3SLeonid Yegoshin return SIGILL; 209102cedc3SLeonid Yegoshin break; 210102cedc3SLeonid Yegoshin case mm_32f_60_op: 211102cedc3SLeonid Yegoshin func = -1; /* Invalid */ 212102cedc3SLeonid Yegoshin if (insn.mm_fp0_format.op == mm_fadd_op) 213102cedc3SLeonid Yegoshin func = fadd_op; 214102cedc3SLeonid Yegoshin else if (insn.mm_fp0_format.op == mm_fsub_op) 215102cedc3SLeonid Yegoshin func = fsub_op; 216102cedc3SLeonid Yegoshin else if (insn.mm_fp0_format.op == mm_fmul_op) 217102cedc3SLeonid Yegoshin func = fmul_op; 218102cedc3SLeonid Yegoshin else if (insn.mm_fp0_format.op == mm_fdiv_op) 219102cedc3SLeonid Yegoshin func = fdiv_op; 220102cedc3SLeonid Yegoshin if (func != -1) { 221102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 222102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 223102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp0_format.fmt]; 224102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 225102cedc3SLeonid Yegoshin insn.mm_fp0_format.ft; 226102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 227102cedc3SLeonid Yegoshin insn.mm_fp0_format.fs; 228102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 229102cedc3SLeonid Yegoshin insn.mm_fp0_format.fd; 230102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 231102cedc3SLeonid Yegoshin } else 232102cedc3SLeonid Yegoshin return SIGILL; 233102cedc3SLeonid Yegoshin break; 234102cedc3SLeonid Yegoshin case mm_32f_70_op: 235102cedc3SLeonid Yegoshin func = -1; /* Invalid */ 236102cedc3SLeonid Yegoshin if (insn.mm_fp0_format.op == mm_fmovn_op) 237102cedc3SLeonid Yegoshin func = fmovn_op; 238102cedc3SLeonid Yegoshin else if (insn.mm_fp0_format.op == mm_fmovz_op) 239102cedc3SLeonid Yegoshin func = fmovz_op; 240102cedc3SLeonid Yegoshin if (func != -1) { 241102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 242102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 243102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp0_format.fmt]; 244102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 245102cedc3SLeonid Yegoshin insn.mm_fp0_format.ft; 246102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 247102cedc3SLeonid Yegoshin insn.mm_fp0_format.fs; 248102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 249102cedc3SLeonid Yegoshin insn.mm_fp0_format.fd; 250102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 251102cedc3SLeonid Yegoshin } else 252102cedc3SLeonid Yegoshin return SIGILL; 253102cedc3SLeonid Yegoshin break; 254102cedc3SLeonid Yegoshin case mm_32f_73_op: /* POOL32FXF */ 255102cedc3SLeonid Yegoshin switch (insn.mm_fp1_format.op) { 256102cedc3SLeonid Yegoshin case mm_movf0_op: 257102cedc3SLeonid Yegoshin case mm_movf1_op: 258102cedc3SLeonid Yegoshin case mm_movt0_op: 259102cedc3SLeonid Yegoshin case mm_movt1_op: 260102cedc3SLeonid Yegoshin if ((insn.mm_fp1_format.op & 0x7f) == 261102cedc3SLeonid Yegoshin mm_movf0_op) 262102cedc3SLeonid Yegoshin op = 0; 263102cedc3SLeonid Yegoshin else 264102cedc3SLeonid Yegoshin op = 1; 265102cedc3SLeonid Yegoshin mips32_insn.r_format.opcode = spec_op; 266102cedc3SLeonid Yegoshin mips32_insn.r_format.rs = insn.mm_fp4_format.fs; 267102cedc3SLeonid Yegoshin mips32_insn.r_format.rt = 268102cedc3SLeonid Yegoshin (insn.mm_fp4_format.cc << 2) + op; 269102cedc3SLeonid Yegoshin mips32_insn.r_format.rd = insn.mm_fp4_format.rt; 270102cedc3SLeonid Yegoshin mips32_insn.r_format.re = 0; 271102cedc3SLeonid Yegoshin mips32_insn.r_format.func = movc_op; 272102cedc3SLeonid Yegoshin break; 273102cedc3SLeonid Yegoshin case mm_fcvtd0_op: 274102cedc3SLeonid Yegoshin case mm_fcvtd1_op: 275102cedc3SLeonid Yegoshin case mm_fcvts0_op: 276102cedc3SLeonid Yegoshin case mm_fcvts1_op: 277102cedc3SLeonid Yegoshin if ((insn.mm_fp1_format.op & 0x7f) == 278102cedc3SLeonid Yegoshin mm_fcvtd0_op) { 279102cedc3SLeonid Yegoshin func = fcvtd_op; 280102cedc3SLeonid Yegoshin fmt = swl_format[insn.mm_fp3_format.fmt]; 281102cedc3SLeonid Yegoshin } else { 282102cedc3SLeonid Yegoshin func = fcvts_op; 283102cedc3SLeonid Yegoshin fmt = dwl_format[insn.mm_fp3_format.fmt]; 284102cedc3SLeonid Yegoshin } 285102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 286102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = fmt; 287102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 0; 288102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 289102cedc3SLeonid Yegoshin insn.mm_fp3_format.fs; 290102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 291102cedc3SLeonid Yegoshin insn.mm_fp3_format.rt; 292102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 293102cedc3SLeonid Yegoshin break; 294102cedc3SLeonid Yegoshin case mm_fmov0_op: 295102cedc3SLeonid Yegoshin case mm_fmov1_op: 296102cedc3SLeonid Yegoshin case mm_fabs0_op: 297102cedc3SLeonid Yegoshin case mm_fabs1_op: 298102cedc3SLeonid Yegoshin case mm_fneg0_op: 299102cedc3SLeonid Yegoshin case mm_fneg1_op: 300102cedc3SLeonid Yegoshin if ((insn.mm_fp1_format.op & 0x7f) == 301102cedc3SLeonid Yegoshin mm_fmov0_op) 302102cedc3SLeonid Yegoshin func = fmov_op; 303102cedc3SLeonid Yegoshin else if ((insn.mm_fp1_format.op & 0x7f) == 304102cedc3SLeonid Yegoshin mm_fabs0_op) 305102cedc3SLeonid Yegoshin func = fabs_op; 306102cedc3SLeonid Yegoshin else 307102cedc3SLeonid Yegoshin func = fneg_op; 308102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 309102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 310102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp3_format.fmt]; 311102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 0; 312102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 313102cedc3SLeonid Yegoshin insn.mm_fp3_format.fs; 314102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 315102cedc3SLeonid Yegoshin insn.mm_fp3_format.rt; 316102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 317102cedc3SLeonid Yegoshin break; 318102cedc3SLeonid Yegoshin case mm_ffloorl_op: 319102cedc3SLeonid Yegoshin case mm_ffloorw_op: 320102cedc3SLeonid Yegoshin case mm_fceill_op: 321102cedc3SLeonid Yegoshin case mm_fceilw_op: 322102cedc3SLeonid Yegoshin case mm_ftruncl_op: 323102cedc3SLeonid Yegoshin case mm_ftruncw_op: 324102cedc3SLeonid Yegoshin case mm_froundl_op: 325102cedc3SLeonid Yegoshin case mm_froundw_op: 326102cedc3SLeonid Yegoshin case mm_fcvtl_op: 327102cedc3SLeonid Yegoshin case mm_fcvtw_op: 328102cedc3SLeonid Yegoshin if (insn.mm_fp1_format.op == mm_ffloorl_op) 329102cedc3SLeonid Yegoshin func = ffloorl_op; 330102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_ffloorw_op) 331102cedc3SLeonid Yegoshin func = ffloor_op; 332102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_fceill_op) 333102cedc3SLeonid Yegoshin func = fceill_op; 334102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_fceilw_op) 335102cedc3SLeonid Yegoshin func = fceil_op; 336102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_ftruncl_op) 337102cedc3SLeonid Yegoshin func = ftruncl_op; 338102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_ftruncw_op) 339102cedc3SLeonid Yegoshin func = ftrunc_op; 340102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_froundl_op) 341102cedc3SLeonid Yegoshin func = froundl_op; 342102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_froundw_op) 343102cedc3SLeonid Yegoshin func = fround_op; 344102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_fcvtl_op) 345102cedc3SLeonid Yegoshin func = fcvtl_op; 346102cedc3SLeonid Yegoshin else 347102cedc3SLeonid Yegoshin func = fcvtw_op; 348102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 349102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 350102cedc3SLeonid Yegoshin sd_format[insn.mm_fp1_format.fmt]; 351102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 0; 352102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 353102cedc3SLeonid Yegoshin insn.mm_fp1_format.fs; 354102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 355102cedc3SLeonid Yegoshin insn.mm_fp1_format.rt; 356102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 357102cedc3SLeonid Yegoshin break; 358102cedc3SLeonid Yegoshin case mm_frsqrt_op: 359102cedc3SLeonid Yegoshin case mm_fsqrt_op: 360102cedc3SLeonid Yegoshin case mm_frecip_op: 361102cedc3SLeonid Yegoshin if (insn.mm_fp1_format.op == mm_frsqrt_op) 362102cedc3SLeonid Yegoshin func = frsqrt_op; 363102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_fsqrt_op) 364102cedc3SLeonid Yegoshin func = fsqrt_op; 365102cedc3SLeonid Yegoshin else 366102cedc3SLeonid Yegoshin func = frecip_op; 367102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 368102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 369102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp1_format.fmt]; 370102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 0; 371102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 372102cedc3SLeonid Yegoshin insn.mm_fp1_format.fs; 373102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 374102cedc3SLeonid Yegoshin insn.mm_fp1_format.rt; 375102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 376102cedc3SLeonid Yegoshin break; 377102cedc3SLeonid Yegoshin case mm_mfc1_op: 378102cedc3SLeonid Yegoshin case mm_mtc1_op: 379102cedc3SLeonid Yegoshin case mm_cfc1_op: 380102cedc3SLeonid Yegoshin case mm_ctc1_op: 3819355e59cSSteven J. Hill case mm_mfhc1_op: 3829355e59cSSteven J. Hill case mm_mthc1_op: 383102cedc3SLeonid Yegoshin if (insn.mm_fp1_format.op == mm_mfc1_op) 384102cedc3SLeonid Yegoshin op = mfc_op; 385102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_mtc1_op) 386102cedc3SLeonid Yegoshin op = mtc_op; 387102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_cfc1_op) 388102cedc3SLeonid Yegoshin op = cfc_op; 3899355e59cSSteven J. Hill else if (insn.mm_fp1_format.op == mm_ctc1_op) 390102cedc3SLeonid Yegoshin op = ctc_op; 3919355e59cSSteven J. Hill else if (insn.mm_fp1_format.op == mm_mfhc1_op) 3929355e59cSSteven J. Hill op = mfhc_op; 3939355e59cSSteven J. Hill else 3949355e59cSSteven J. Hill op = mthc_op; 395102cedc3SLeonid Yegoshin mips32_insn.fp1_format.opcode = cop1_op; 396102cedc3SLeonid Yegoshin mips32_insn.fp1_format.op = op; 397102cedc3SLeonid Yegoshin mips32_insn.fp1_format.rt = 398102cedc3SLeonid Yegoshin insn.mm_fp1_format.rt; 399102cedc3SLeonid Yegoshin mips32_insn.fp1_format.fs = 400102cedc3SLeonid Yegoshin insn.mm_fp1_format.fs; 401102cedc3SLeonid Yegoshin mips32_insn.fp1_format.fd = 0; 402102cedc3SLeonid Yegoshin mips32_insn.fp1_format.func = 0; 403102cedc3SLeonid Yegoshin break; 404102cedc3SLeonid Yegoshin default: 405102cedc3SLeonid Yegoshin return SIGILL; 406102cedc3SLeonid Yegoshin } 407102cedc3SLeonid Yegoshin break; 408102cedc3SLeonid Yegoshin case mm_32f_74_op: /* c.cond.fmt */ 409102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 410102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 411102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp4_format.fmt]; 412102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = insn.mm_fp4_format.rt; 413102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = insn.mm_fp4_format.fs; 414102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = insn.mm_fp4_format.cc << 2; 415102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = 416102cedc3SLeonid Yegoshin insn.mm_fp4_format.cond | MM_MIPS32_COND_FC; 417102cedc3SLeonid Yegoshin break; 418102cedc3SLeonid Yegoshin default: 419102cedc3SLeonid Yegoshin return SIGILL; 420102cedc3SLeonid Yegoshin } 421102cedc3SLeonid Yegoshin break; 422102cedc3SLeonid Yegoshin default: 423102cedc3SLeonid Yegoshin return SIGILL; 424102cedc3SLeonid Yegoshin } 425102cedc3SLeonid Yegoshin 426102cedc3SLeonid Yegoshin *insn_ptr = mips32_insn; 427102cedc3SLeonid Yegoshin return 0; 428102cedc3SLeonid Yegoshin } 429102cedc3SLeonid Yegoshin 4301da177e4SLinus Torvalds /* 4311da177e4SLinus Torvalds * Redundant with logic already in kernel/branch.c, 4321da177e4SLinus Torvalds * embedded in compute_return_epc. At some point, 4331da177e4SLinus Torvalds * a single subroutine should be used across both 4341da177e4SLinus Torvalds * modules. 4351da177e4SLinus Torvalds */ 436432c6bacSPaul Burton int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, 437102cedc3SLeonid Yegoshin unsigned long *contpc) 4381da177e4SLinus Torvalds { 439102cedc3SLeonid Yegoshin union mips_instruction insn = (union mips_instruction)dec_insn.insn; 440102cedc3SLeonid Yegoshin unsigned int fcr31; 441102cedc3SLeonid Yegoshin unsigned int bit = 0; 4428bcd84a4SDouglas Leung unsigned int bit0; 4438bcd84a4SDouglas Leung union fpureg *fpr; 444102cedc3SLeonid Yegoshin 445102cedc3SLeonid Yegoshin switch (insn.i_format.opcode) { 4461da177e4SLinus Torvalds case spec_op: 447102cedc3SLeonid Yegoshin switch (insn.r_format.func) { 4481da177e4SLinus Torvalds case jalr_op: 449ab4a92e6SPaul Burton if (insn.r_format.rd != 0) { 450102cedc3SLeonid Yegoshin regs->regs[insn.r_format.rd] = 451102cedc3SLeonid Yegoshin regs->cp0_epc + dec_insn.pc_inc + 452102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 453ab4a92e6SPaul Burton } 454102cedc3SLeonid Yegoshin /* Fall through */ 4551da177e4SLinus Torvalds case jr_op: 4565f9f41c4SMarkos Chandras /* For R6, JR already emulated in jalr_op */ 457143fefc8SMarkos Chandras if (NO_R6EMU && insn.r_format.func == jr_op) 4585f9f41c4SMarkos Chandras break; 459102cedc3SLeonid Yegoshin *contpc = regs->regs[insn.r_format.rs]; 4601da177e4SLinus Torvalds return 1; 4611da177e4SLinus Torvalds } 4621da177e4SLinus Torvalds break; 4631da177e4SLinus Torvalds case bcond_op: 464102cedc3SLeonid Yegoshin switch (insn.i_format.rt) { 4651da177e4SLinus Torvalds case bltzal_op: 4661da177e4SLinus Torvalds case bltzall_op: 467319824eaSMarkos Chandras if (NO_R6EMU && (insn.i_format.rs || 468319824eaSMarkos Chandras insn.i_format.rt == bltzall_op)) 469319824eaSMarkos Chandras break; 470319824eaSMarkos Chandras 471102cedc3SLeonid Yegoshin regs->regs[31] = regs->cp0_epc + 472102cedc3SLeonid Yegoshin dec_insn.pc_inc + 473102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 474102cedc3SLeonid Yegoshin /* Fall through */ 475102cedc3SLeonid Yegoshin case bltzl_op: 476319824eaSMarkos Chandras if (NO_R6EMU) 477319824eaSMarkos Chandras break; 478319824eaSMarkos Chandras case bltz_op: 479102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.i_format.rs] < 0) 480102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 481102cedc3SLeonid Yegoshin dec_insn.pc_inc + 482102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 483102cedc3SLeonid Yegoshin else 484102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 485102cedc3SLeonid Yegoshin dec_insn.pc_inc + 486102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 4871da177e4SLinus Torvalds return 1; 488102cedc3SLeonid Yegoshin case bgezal_op: 489102cedc3SLeonid Yegoshin case bgezall_op: 490319824eaSMarkos Chandras if (NO_R6EMU && (insn.i_format.rs || 491319824eaSMarkos Chandras insn.i_format.rt == bgezall_op)) 492319824eaSMarkos Chandras break; 493319824eaSMarkos Chandras 494102cedc3SLeonid Yegoshin regs->regs[31] = regs->cp0_epc + 495102cedc3SLeonid Yegoshin dec_insn.pc_inc + 496102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 497102cedc3SLeonid Yegoshin /* Fall through */ 498102cedc3SLeonid Yegoshin case bgezl_op: 499319824eaSMarkos Chandras if (NO_R6EMU) 500319824eaSMarkos Chandras break; 501319824eaSMarkos Chandras case bgez_op: 502102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.i_format.rs] >= 0) 503102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 504102cedc3SLeonid Yegoshin dec_insn.pc_inc + 505102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 506102cedc3SLeonid Yegoshin else 507102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 508102cedc3SLeonid Yegoshin dec_insn.pc_inc + 509102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 510102cedc3SLeonid Yegoshin return 1; 5111da177e4SLinus Torvalds } 5121da177e4SLinus Torvalds break; 5131da177e4SLinus Torvalds case jalx_op: 514102cedc3SLeonid Yegoshin set_isa16_mode(bit); 515102cedc3SLeonid Yegoshin case jal_op: 516102cedc3SLeonid Yegoshin regs->regs[31] = regs->cp0_epc + 517102cedc3SLeonid Yegoshin dec_insn.pc_inc + 518102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 519102cedc3SLeonid Yegoshin /* Fall through */ 520102cedc3SLeonid Yegoshin case j_op: 521102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + dec_insn.pc_inc; 522102cedc3SLeonid Yegoshin *contpc >>= 28; 523102cedc3SLeonid Yegoshin *contpc <<= 28; 524102cedc3SLeonid Yegoshin *contpc |= (insn.j_format.target << 2); 525102cedc3SLeonid Yegoshin /* Set microMIPS mode bit: XOR for jalx. */ 526102cedc3SLeonid Yegoshin *contpc ^= bit; 5271da177e4SLinus Torvalds return 1; 528102cedc3SLeonid Yegoshin case beql_op: 529319824eaSMarkos Chandras if (NO_R6EMU) 530319824eaSMarkos Chandras break; 531319824eaSMarkos Chandras case beq_op: 532102cedc3SLeonid Yegoshin if (regs->regs[insn.i_format.rs] == 533102cedc3SLeonid Yegoshin regs->regs[insn.i_format.rt]) 534102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 535102cedc3SLeonid Yegoshin dec_insn.pc_inc + 536102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 537102cedc3SLeonid Yegoshin else 538102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 539102cedc3SLeonid Yegoshin dec_insn.pc_inc + 540102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 541102cedc3SLeonid Yegoshin return 1; 542102cedc3SLeonid Yegoshin case bnel_op: 543319824eaSMarkos Chandras if (NO_R6EMU) 544319824eaSMarkos Chandras break; 545319824eaSMarkos Chandras case bne_op: 546102cedc3SLeonid Yegoshin if (regs->regs[insn.i_format.rs] != 547102cedc3SLeonid Yegoshin regs->regs[insn.i_format.rt]) 548102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 549102cedc3SLeonid Yegoshin dec_insn.pc_inc + 550102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 551102cedc3SLeonid Yegoshin else 552102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 553102cedc3SLeonid Yegoshin dec_insn.pc_inc + 554102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 555102cedc3SLeonid Yegoshin return 1; 556102cedc3SLeonid Yegoshin case blezl_op: 557e9d92d22SMarkos Chandras if (!insn.i_format.rt && NO_R6EMU) 558319824eaSMarkos Chandras break; 559319824eaSMarkos Chandras case blez_op: 560a8ff66f5SMarkos Chandras 561a8ff66f5SMarkos Chandras /* 562a8ff66f5SMarkos Chandras * Compact branches for R6 for the 563a8ff66f5SMarkos Chandras * blez and blezl opcodes. 564a8ff66f5SMarkos Chandras * BLEZ | rs = 0 | rt != 0 == BLEZALC 565a8ff66f5SMarkos Chandras * BLEZ | rs = rt != 0 == BGEZALC 566a8ff66f5SMarkos Chandras * BLEZ | rs != 0 | rt != 0 == BGEUC 567a8ff66f5SMarkos Chandras * BLEZL | rs = 0 | rt != 0 == BLEZC 568a8ff66f5SMarkos Chandras * BLEZL | rs = rt != 0 == BGEZC 569a8ff66f5SMarkos Chandras * BLEZL | rs != 0 | rt != 0 == BGEC 570a8ff66f5SMarkos Chandras * 571a8ff66f5SMarkos Chandras * For real BLEZ{,L}, rt is always 0. 572a8ff66f5SMarkos Chandras */ 573a8ff66f5SMarkos Chandras if (cpu_has_mips_r6 && insn.i_format.rt) { 574a8ff66f5SMarkos Chandras if ((insn.i_format.opcode == blez_op) && 575a8ff66f5SMarkos Chandras ((!insn.i_format.rs && insn.i_format.rt) || 576a8ff66f5SMarkos Chandras (insn.i_format.rs == insn.i_format.rt))) 577a8ff66f5SMarkos Chandras regs->regs[31] = regs->cp0_epc + 578a8ff66f5SMarkos Chandras dec_insn.pc_inc; 579a8ff66f5SMarkos Chandras *contpc = regs->cp0_epc + dec_insn.pc_inc + 580a8ff66f5SMarkos Chandras dec_insn.next_pc_inc; 581a8ff66f5SMarkos Chandras 582a8ff66f5SMarkos Chandras return 1; 583a8ff66f5SMarkos Chandras } 584102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.i_format.rs] <= 0) 585102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 586102cedc3SLeonid Yegoshin dec_insn.pc_inc + 587102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 588102cedc3SLeonid Yegoshin else 589102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 590102cedc3SLeonid Yegoshin dec_insn.pc_inc + 591102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 592102cedc3SLeonid Yegoshin return 1; 593102cedc3SLeonid Yegoshin case bgtzl_op: 594e9d92d22SMarkos Chandras if (!insn.i_format.rt && NO_R6EMU) 595319824eaSMarkos Chandras break; 596319824eaSMarkos Chandras case bgtz_op: 597f1b44067SMarkos Chandras /* 598f1b44067SMarkos Chandras * Compact branches for R6 for the 599f1b44067SMarkos Chandras * bgtz and bgtzl opcodes. 600f1b44067SMarkos Chandras * BGTZ | rs = 0 | rt != 0 == BGTZALC 601f1b44067SMarkos Chandras * BGTZ | rs = rt != 0 == BLTZALC 602f1b44067SMarkos Chandras * BGTZ | rs != 0 | rt != 0 == BLTUC 603f1b44067SMarkos Chandras * BGTZL | rs = 0 | rt != 0 == BGTZC 604f1b44067SMarkos Chandras * BGTZL | rs = rt != 0 == BLTZC 605f1b44067SMarkos Chandras * BGTZL | rs != 0 | rt != 0 == BLTC 606f1b44067SMarkos Chandras * 607f1b44067SMarkos Chandras * *ZALC varint for BGTZ &&& rt != 0 608f1b44067SMarkos Chandras * For real GTZ{,L}, rt is always 0. 609f1b44067SMarkos Chandras */ 610f1b44067SMarkos Chandras if (cpu_has_mips_r6 && insn.i_format.rt) { 611f1b44067SMarkos Chandras if ((insn.i_format.opcode == blez_op) && 612f1b44067SMarkos Chandras ((!insn.i_format.rs && insn.i_format.rt) || 613f1b44067SMarkos Chandras (insn.i_format.rs == insn.i_format.rt))) 614f1b44067SMarkos Chandras regs->regs[31] = regs->cp0_epc + 615f1b44067SMarkos Chandras dec_insn.pc_inc; 616f1b44067SMarkos Chandras *contpc = regs->cp0_epc + dec_insn.pc_inc + 617f1b44067SMarkos Chandras dec_insn.next_pc_inc; 618f1b44067SMarkos Chandras 619f1b44067SMarkos Chandras return 1; 620f1b44067SMarkos Chandras } 621f1b44067SMarkos Chandras 622102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.i_format.rs] > 0) 623102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 624102cedc3SLeonid Yegoshin dec_insn.pc_inc + 625102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 626102cedc3SLeonid Yegoshin else 627102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 628102cedc3SLeonid Yegoshin dec_insn.pc_inc + 629102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 630102cedc3SLeonid Yegoshin return 1; 6311b492600SPaul Burton case pop10_op: 6321b492600SPaul Burton case pop30_op: 633c893ce38SMarkos Chandras if (!cpu_has_mips_r6) 634c893ce38SMarkos Chandras break; 635c893ce38SMarkos Chandras if (insn.i_format.rt && !insn.i_format.rs) 636c893ce38SMarkos Chandras regs->regs[31] = regs->cp0_epc + 4; 637c893ce38SMarkos Chandras *contpc = regs->cp0_epc + dec_insn.pc_inc + 638c893ce38SMarkos Chandras dec_insn.next_pc_inc; 639c893ce38SMarkos Chandras 640c893ce38SMarkos Chandras return 1; 641c26d4219SDavid Daney #ifdef CONFIG_CPU_CAVIUM_OCTEON 642c26d4219SDavid Daney case lwc2_op: /* This is bbit0 on Octeon */ 643c26d4219SDavid Daney if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) == 0) 644c26d4219SDavid Daney *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); 645c26d4219SDavid Daney else 646c26d4219SDavid Daney *contpc = regs->cp0_epc + 8; 647c26d4219SDavid Daney return 1; 648c26d4219SDavid Daney case ldc2_op: /* This is bbit032 on Octeon */ 649c26d4219SDavid Daney if ((regs->regs[insn.i_format.rs] & (1ull<<(insn.i_format.rt + 32))) == 0) 650c26d4219SDavid Daney *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); 651c26d4219SDavid Daney else 652c26d4219SDavid Daney *contpc = regs->cp0_epc + 8; 653c26d4219SDavid Daney return 1; 654c26d4219SDavid Daney case swc2_op: /* This is bbit1 on Octeon */ 655c26d4219SDavid Daney if (regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) 656c26d4219SDavid Daney *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); 657c26d4219SDavid Daney else 658c26d4219SDavid Daney *contpc = regs->cp0_epc + 8; 659c26d4219SDavid Daney return 1; 660c26d4219SDavid Daney case sdc2_op: /* This is bbit132 on Octeon */ 661c26d4219SDavid Daney if (regs->regs[insn.i_format.rs] & (1ull<<(insn.i_format.rt + 32))) 662c26d4219SDavid Daney *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); 663c26d4219SDavid Daney else 664c26d4219SDavid Daney *contpc = regs->cp0_epc + 8; 665c26d4219SDavid Daney return 1; 6668467ca01SMarkos Chandras #else 6678467ca01SMarkos Chandras case bc6_op: 6688467ca01SMarkos Chandras /* 6698467ca01SMarkos Chandras * Only valid for MIPS R6 but we can still end up 6708467ca01SMarkos Chandras * here from a broken userland so just tell emulator 6718467ca01SMarkos Chandras * this is not a branch and let it break later on. 6728467ca01SMarkos Chandras */ 6738467ca01SMarkos Chandras if (!cpu_has_mips_r6) 6748467ca01SMarkos Chandras break; 6758467ca01SMarkos Chandras *contpc = regs->cp0_epc + dec_insn.pc_inc + 6768467ca01SMarkos Chandras dec_insn.next_pc_inc; 6778467ca01SMarkos Chandras 6788467ca01SMarkos Chandras return 1; 67984fef630SMarkos Chandras case balc6_op: 68084fef630SMarkos Chandras if (!cpu_has_mips_r6) 68184fef630SMarkos Chandras break; 68284fef630SMarkos Chandras regs->regs[31] = regs->cp0_epc + 4; 68384fef630SMarkos Chandras *contpc = regs->cp0_epc + dec_insn.pc_inc + 68484fef630SMarkos Chandras dec_insn.next_pc_inc; 68584fef630SMarkos Chandras 68684fef630SMarkos Chandras return 1; 6871c66b79bSPaul Burton case pop66_op: 68869b9a2fdSMarkos Chandras if (!cpu_has_mips_r6) 68969b9a2fdSMarkos Chandras break; 69069b9a2fdSMarkos Chandras *contpc = regs->cp0_epc + dec_insn.pc_inc + 69169b9a2fdSMarkos Chandras dec_insn.next_pc_inc; 69269b9a2fdSMarkos Chandras 69369b9a2fdSMarkos Chandras return 1; 6941c66b79bSPaul Burton case pop76_op: 69528d6f93dSMarkos Chandras if (!cpu_has_mips_r6) 69628d6f93dSMarkos Chandras break; 69728d6f93dSMarkos Chandras if (!insn.i_format.rs) 69828d6f93dSMarkos Chandras regs->regs[31] = regs->cp0_epc + 4; 69928d6f93dSMarkos Chandras *contpc = regs->cp0_epc + dec_insn.pc_inc + 70028d6f93dSMarkos Chandras dec_insn.next_pc_inc; 70128d6f93dSMarkos Chandras 70228d6f93dSMarkos Chandras return 1; 703c26d4219SDavid Daney #endif 7041da177e4SLinus Torvalds case cop0_op: 7051da177e4SLinus Torvalds case cop1_op: 706c8a34581SMarkos Chandras /* Need to check for R6 bc1nez and bc1eqz branches */ 707c8a34581SMarkos Chandras if (cpu_has_mips_r6 && 708c8a34581SMarkos Chandras ((insn.i_format.rs == bc1eqz_op) || 709c8a34581SMarkos Chandras (insn.i_format.rs == bc1nez_op))) { 710c8a34581SMarkos Chandras bit = 0; 7118bcd84a4SDouglas Leung fpr = ¤t->thread.fpu.fpr[insn.i_format.rt]; 7128bcd84a4SDouglas Leung bit0 = get_fpr32(fpr, 0) & 0x1; 713c8a34581SMarkos Chandras switch (insn.i_format.rs) { 714c8a34581SMarkos Chandras case bc1eqz_op: 7158bcd84a4SDouglas Leung bit = bit0 == 0; 716c8a34581SMarkos Chandras break; 717c8a34581SMarkos Chandras case bc1nez_op: 7188bcd84a4SDouglas Leung bit = bit0 != 0; 719c8a34581SMarkos Chandras break; 720c8a34581SMarkos Chandras } 721c8a34581SMarkos Chandras if (bit) 722c8a34581SMarkos Chandras *contpc = regs->cp0_epc + 723c8a34581SMarkos Chandras dec_insn.pc_inc + 724c8a34581SMarkos Chandras (insn.i_format.simmediate << 2); 725c8a34581SMarkos Chandras else 726c8a34581SMarkos Chandras *contpc = regs->cp0_epc + 727c8a34581SMarkos Chandras dec_insn.pc_inc + 728c8a34581SMarkos Chandras dec_insn.next_pc_inc; 729c8a34581SMarkos Chandras 730c8a34581SMarkos Chandras return 1; 731c8a34581SMarkos Chandras } 732c8a34581SMarkos Chandras /* R2/R6 compatible cop1 instruction. Fall through */ 7331da177e4SLinus Torvalds case cop2_op: 7341da177e4SLinus Torvalds case cop1x_op: 735102cedc3SLeonid Yegoshin if (insn.i_format.rs == bc_op) { 736102cedc3SLeonid Yegoshin preempt_disable(); 737102cedc3SLeonid Yegoshin if (is_fpu_owner()) 738842dfc11SManuel Lauss fcr31 = read_32bit_cp1_register(CP1_STATUS); 739102cedc3SLeonid Yegoshin else 740102cedc3SLeonid Yegoshin fcr31 = current->thread.fpu.fcr31; 741102cedc3SLeonid Yegoshin preempt_enable(); 742102cedc3SLeonid Yegoshin 743102cedc3SLeonid Yegoshin bit = (insn.i_format.rt >> 2); 744102cedc3SLeonid Yegoshin bit += (bit != 0); 745102cedc3SLeonid Yegoshin bit += 23; 746102cedc3SLeonid Yegoshin switch (insn.i_format.rt & 3) { 747102cedc3SLeonid Yegoshin case 0: /* bc1f */ 748102cedc3SLeonid Yegoshin case 2: /* bc1fl */ 749102cedc3SLeonid Yegoshin if (~fcr31 & (1 << bit)) 750102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 751102cedc3SLeonid Yegoshin dec_insn.pc_inc + 752102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 753102cedc3SLeonid Yegoshin else 754102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 755102cedc3SLeonid Yegoshin dec_insn.pc_inc + 756102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 757102cedc3SLeonid Yegoshin return 1; 758102cedc3SLeonid Yegoshin case 1: /* bc1t */ 759102cedc3SLeonid Yegoshin case 3: /* bc1tl */ 760102cedc3SLeonid Yegoshin if (fcr31 & (1 << bit)) 761102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 762102cedc3SLeonid Yegoshin dec_insn.pc_inc + 763102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 764102cedc3SLeonid Yegoshin else 765102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 766102cedc3SLeonid Yegoshin dec_insn.pc_inc + 767102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 7681da177e4SLinus Torvalds return 1; 7691da177e4SLinus Torvalds } 770102cedc3SLeonid Yegoshin } 771102cedc3SLeonid Yegoshin break; 772102cedc3SLeonid Yegoshin } 7731da177e4SLinus Torvalds return 0; 7741da177e4SLinus Torvalds } 7751da177e4SLinus Torvalds 7761da177e4SLinus Torvalds /* 7771da177e4SLinus Torvalds * In the Linux kernel, we support selection of FPR format on the 778da0bac33SDavid Daney * basis of the Status.FR bit. If an FPU is not present, the FR bit 779da0bac33SDavid Daney * is hardwired to zero, which would imply a 32-bit FPU even for 780597ce172SPaul Burton * 64-bit CPUs so we rather look at TIF_32BIT_FPREGS. 78151d943f0SRalf Baechle * FPU emu is slow and bulky and optimizing this function offers fairly 78251d943f0SRalf Baechle * sizeable benefits so we try to be clever and make this function return 78351d943f0SRalf Baechle * a constant whenever possible, that is on 64-bit kernels without O32 784597ce172SPaul Burton * compatibility enabled and on 32-bit without 64-bit FPU support. 7851da177e4SLinus Torvalds */ 786da0bac33SDavid Daney static inline int cop1_64bit(struct pt_regs *xcp) 787da0bac33SDavid Daney { 78897f2645fSMasahiro Yamada if (IS_ENABLED(CONFIG_64BIT) && !IS_ENABLED(CONFIG_MIPS32_O32)) 78951d943f0SRalf Baechle return 1; 79097f2645fSMasahiro Yamada else if (IS_ENABLED(CONFIG_32BIT) && 79197f2645fSMasahiro Yamada !IS_ENABLED(CONFIG_MIPS_O32_FP64_SUPPORT)) 792da0bac33SDavid Daney return 0; 79308a07904SRalf Baechle 794597ce172SPaul Burton return !test_thread_flag(TIF_32BIT_FPREGS); 795da0bac33SDavid Daney } 7961da177e4SLinus Torvalds 7974227a2d4SPaul Burton static inline bool hybrid_fprs(void) 7984227a2d4SPaul Burton { 7994227a2d4SPaul Burton return test_thread_flag(TIF_HYBRID_FPREGS); 8004227a2d4SPaul Burton } 8014227a2d4SPaul Burton 80247fa0c02SRalf Baechle #define SIFROMREG(si, x) \ 80347fa0c02SRalf Baechle do { \ 8044227a2d4SPaul Burton if (cop1_64bit(xcp) && !hybrid_fprs()) \ 805c8c0da6bSPaul Burton (si) = (int)get_fpr32(&ctx->fpr[x], 0); \ 806bbd426f5SPaul Burton else \ 807c8c0da6bSPaul Burton (si) = (int)get_fpr32(&ctx->fpr[(x) & ~1], (x) & 1); \ 808bbd426f5SPaul Burton } while (0) 809da0bac33SDavid Daney 81047fa0c02SRalf Baechle #define SITOREG(si, x) \ 81147fa0c02SRalf Baechle do { \ 8124227a2d4SPaul Burton if (cop1_64bit(xcp) && !hybrid_fprs()) { \ 813a58f85b5SAleksandar Markovic unsigned int i; \ 814bbd426f5SPaul Burton set_fpr32(&ctx->fpr[x], 0, si); \ 815ef1c47afSPaul Burton for (i = 1; i < ARRAY_SIZE(ctx->fpr[x].val32); i++) \ 816ef1c47afSPaul Burton set_fpr32(&ctx->fpr[x], i, 0); \ 817ef1c47afSPaul Burton } else { \ 818bbd426f5SPaul Burton set_fpr32(&ctx->fpr[(x) & ~1], (x) & 1, si); \ 819ef1c47afSPaul Burton } \ 820bbd426f5SPaul Burton } while (0) 8211da177e4SLinus Torvalds 822c8c0da6bSPaul Burton #define SIFROMHREG(si, x) ((si) = (int)get_fpr32(&ctx->fpr[x], 1)) 823ef1c47afSPaul Burton 82447fa0c02SRalf Baechle #define SITOHREG(si, x) \ 82547fa0c02SRalf Baechle do { \ 826a58f85b5SAleksandar Markovic unsigned int i; \ 827ef1c47afSPaul Burton set_fpr32(&ctx->fpr[x], 1, si); \ 828ef1c47afSPaul Burton for (i = 2; i < ARRAY_SIZE(ctx->fpr[x].val32); i++) \ 829ef1c47afSPaul Burton set_fpr32(&ctx->fpr[x], i, 0); \ 830ef1c47afSPaul Burton } while (0) 8311ac94400SLeonid Yegoshin 832bbd426f5SPaul Burton #define DIFROMREG(di, x) \ 8338535f2baSManuel Lauss ((di) = get_fpr64(&ctx->fpr[(x) & ~(cop1_64bit(xcp) ^ 1)], 0)) 834bbd426f5SPaul Burton 83547fa0c02SRalf Baechle #define DITOREG(di, x) \ 83647fa0c02SRalf Baechle do { \ 837a58f85b5SAleksandar Markovic unsigned int fpr, i; \ 8388535f2baSManuel Lauss fpr = (x) & ~(cop1_64bit(xcp) ^ 1); \ 839ef1c47afSPaul Burton set_fpr64(&ctx->fpr[fpr], 0, di); \ 840ef1c47afSPaul Burton for (i = 1; i < ARRAY_SIZE(ctx->fpr[x].val64); i++) \ 841ef1c47afSPaul Burton set_fpr64(&ctx->fpr[fpr], i, 0); \ 842ef1c47afSPaul Burton } while (0) 8431da177e4SLinus Torvalds 8441da177e4SLinus Torvalds #define SPFROMREG(sp, x) SIFROMREG((sp).bits, x) 8451da177e4SLinus Torvalds #define SPTOREG(sp, x) SITOREG((sp).bits, x) 8461da177e4SLinus Torvalds #define DPFROMREG(dp, x) DIFROMREG((dp).bits, x) 8471da177e4SLinus Torvalds #define DPTOREG(dp, x) DITOREG((dp).bits, x) 8481da177e4SLinus Torvalds 8491da177e4SLinus Torvalds /* 850d4f5b088SMaciej W. Rozycki * Emulate a CFC1 instruction. 851d4f5b088SMaciej W. Rozycki */ 852d4f5b088SMaciej W. Rozycki static inline void cop1_cfc(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 853d4f5b088SMaciej W. Rozycki mips_instruction ir) 854d4f5b088SMaciej W. Rozycki { 855c491cfa2SMaciej W. Rozycki u32 fcr31 = ctx->fcr31; 856c491cfa2SMaciej W. Rozycki u32 value = 0; 857d4f5b088SMaciej W. Rozycki 858c491cfa2SMaciej W. Rozycki switch (MIPSInst_RD(ir)) { 859c491cfa2SMaciej W. Rozycki case FPCREG_CSR: 860c491cfa2SMaciej W. Rozycki value = fcr31; 861d4f5b088SMaciej W. Rozycki pr_debug("%p gpr[%d]<-csr=%08x\n", 862c491cfa2SMaciej W. Rozycki (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 863c491cfa2SMaciej W. Rozycki break; 864c491cfa2SMaciej W. Rozycki 865c491cfa2SMaciej W. Rozycki case FPCREG_FENR: 866c491cfa2SMaciej W. Rozycki if (!cpu_has_mips_r) 867c491cfa2SMaciej W. Rozycki break; 868c491cfa2SMaciej W. Rozycki value = (fcr31 >> (FPU_CSR_FS_S - MIPS_FENR_FS_S)) & 869c491cfa2SMaciej W. Rozycki MIPS_FENR_FS; 870c491cfa2SMaciej W. Rozycki value |= fcr31 & (FPU_CSR_ALL_E | FPU_CSR_RM); 871c491cfa2SMaciej W. Rozycki pr_debug("%p gpr[%d]<-enr=%08x\n", 872c491cfa2SMaciej W. Rozycki (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 873c491cfa2SMaciej W. Rozycki break; 874c491cfa2SMaciej W. Rozycki 875c491cfa2SMaciej W. Rozycki case FPCREG_FEXR: 876c491cfa2SMaciej W. Rozycki if (!cpu_has_mips_r) 877c491cfa2SMaciej W. Rozycki break; 878c491cfa2SMaciej W. Rozycki value = fcr31 & (FPU_CSR_ALL_X | FPU_CSR_ALL_S); 879c491cfa2SMaciej W. Rozycki pr_debug("%p gpr[%d]<-exr=%08x\n", 880c491cfa2SMaciej W. Rozycki (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 881c491cfa2SMaciej W. Rozycki break; 882c491cfa2SMaciej W. Rozycki 883c491cfa2SMaciej W. Rozycki case FPCREG_FCCR: 884c491cfa2SMaciej W. Rozycki if (!cpu_has_mips_r) 885c491cfa2SMaciej W. Rozycki break; 886c491cfa2SMaciej W. Rozycki value = (fcr31 >> (FPU_CSR_COND_S - MIPS_FCCR_COND0_S)) & 887c491cfa2SMaciej W. Rozycki MIPS_FCCR_COND0; 888c491cfa2SMaciej W. Rozycki value |= (fcr31 >> (FPU_CSR_COND1_S - MIPS_FCCR_COND1_S)) & 889c491cfa2SMaciej W. Rozycki (MIPS_FCCR_CONDX & ~MIPS_FCCR_COND0); 890c491cfa2SMaciej W. Rozycki pr_debug("%p gpr[%d]<-ccr=%08x\n", 891c491cfa2SMaciej W. Rozycki (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 892c491cfa2SMaciej W. Rozycki break; 893c491cfa2SMaciej W. Rozycki 894c491cfa2SMaciej W. Rozycki case FPCREG_RID: 89503dce595SMaciej W. Rozycki value = boot_cpu_data.fpu_id; 896c491cfa2SMaciej W. Rozycki break; 897c491cfa2SMaciej W. Rozycki 898c491cfa2SMaciej W. Rozycki default: 899c491cfa2SMaciej W. Rozycki break; 900c491cfa2SMaciej W. Rozycki } 901c491cfa2SMaciej W. Rozycki 902d4f5b088SMaciej W. Rozycki if (MIPSInst_RT(ir)) 903d4f5b088SMaciej W. Rozycki xcp->regs[MIPSInst_RT(ir)] = value; 904d4f5b088SMaciej W. Rozycki } 905d4f5b088SMaciej W. Rozycki 906d4f5b088SMaciej W. Rozycki /* 907d4f5b088SMaciej W. Rozycki * Emulate a CTC1 instruction. 908d4f5b088SMaciej W. Rozycki */ 909d4f5b088SMaciej W. Rozycki static inline void cop1_ctc(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 910d4f5b088SMaciej W. Rozycki mips_instruction ir) 911d4f5b088SMaciej W. Rozycki { 912c491cfa2SMaciej W. Rozycki u32 fcr31 = ctx->fcr31; 913d4f5b088SMaciej W. Rozycki u32 value; 9149b26616cSMaciej W. Rozycki u32 mask; 915d4f5b088SMaciej W. Rozycki 916d4f5b088SMaciej W. Rozycki if (MIPSInst_RT(ir) == 0) 917d4f5b088SMaciej W. Rozycki value = 0; 918d4f5b088SMaciej W. Rozycki else 919d4f5b088SMaciej W. Rozycki value = xcp->regs[MIPSInst_RT(ir)]; 920d4f5b088SMaciej W. Rozycki 921c491cfa2SMaciej W. Rozycki switch (MIPSInst_RD(ir)) { 922c491cfa2SMaciej W. Rozycki case FPCREG_CSR: 923d4f5b088SMaciej W. Rozycki pr_debug("%p gpr[%d]->csr=%08x\n", 924c491cfa2SMaciej W. Rozycki (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 925d4f5b088SMaciej W. Rozycki 9269b26616cSMaciej W. Rozycki /* Preserve read-only bits. */ 92703dce595SMaciej W. Rozycki mask = boot_cpu_data.fpu_msk31; 9289b26616cSMaciej W. Rozycki fcr31 = (value & ~mask) | (fcr31 & mask); 929c491cfa2SMaciej W. Rozycki break; 930c491cfa2SMaciej W. Rozycki 931c491cfa2SMaciej W. Rozycki case FPCREG_FENR: 932c491cfa2SMaciej W. Rozycki if (!cpu_has_mips_r) 933c491cfa2SMaciej W. Rozycki break; 934c491cfa2SMaciej W. Rozycki pr_debug("%p gpr[%d]->enr=%08x\n", 935c491cfa2SMaciej W. Rozycki (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 936c491cfa2SMaciej W. Rozycki fcr31 &= ~(FPU_CSR_FS | FPU_CSR_ALL_E | FPU_CSR_RM); 937c491cfa2SMaciej W. Rozycki fcr31 |= (value << (FPU_CSR_FS_S - MIPS_FENR_FS_S)) & 938c491cfa2SMaciej W. Rozycki FPU_CSR_FS; 939c491cfa2SMaciej W. Rozycki fcr31 |= value & (FPU_CSR_ALL_E | FPU_CSR_RM); 940c491cfa2SMaciej W. Rozycki break; 941c491cfa2SMaciej W. Rozycki 942c491cfa2SMaciej W. Rozycki case FPCREG_FEXR: 943c491cfa2SMaciej W. Rozycki if (!cpu_has_mips_r) 944c491cfa2SMaciej W. Rozycki break; 945c491cfa2SMaciej W. Rozycki pr_debug("%p gpr[%d]->exr=%08x\n", 946c491cfa2SMaciej W. Rozycki (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 947c491cfa2SMaciej W. Rozycki fcr31 &= ~(FPU_CSR_ALL_X | FPU_CSR_ALL_S); 948c491cfa2SMaciej W. Rozycki fcr31 |= value & (FPU_CSR_ALL_X | FPU_CSR_ALL_S); 949c491cfa2SMaciej W. Rozycki break; 950c491cfa2SMaciej W. Rozycki 951c491cfa2SMaciej W. Rozycki case FPCREG_FCCR: 952c491cfa2SMaciej W. Rozycki if (!cpu_has_mips_r) 953c491cfa2SMaciej W. Rozycki break; 954c491cfa2SMaciej W. Rozycki pr_debug("%p gpr[%d]->ccr=%08x\n", 955c491cfa2SMaciej W. Rozycki (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 956c491cfa2SMaciej W. Rozycki fcr31 &= ~(FPU_CSR_CONDX | FPU_CSR_COND); 957c491cfa2SMaciej W. Rozycki fcr31 |= (value << (FPU_CSR_COND_S - MIPS_FCCR_COND0_S)) & 958c491cfa2SMaciej W. Rozycki FPU_CSR_COND; 959c491cfa2SMaciej W. Rozycki fcr31 |= (value << (FPU_CSR_COND1_S - MIPS_FCCR_COND1_S)) & 960c491cfa2SMaciej W. Rozycki FPU_CSR_CONDX; 961c491cfa2SMaciej W. Rozycki break; 962c491cfa2SMaciej W. Rozycki 963c491cfa2SMaciej W. Rozycki default: 964c491cfa2SMaciej W. Rozycki break; 965d4f5b088SMaciej W. Rozycki } 966c491cfa2SMaciej W. Rozycki 967c491cfa2SMaciej W. Rozycki ctx->fcr31 = fcr31; 968d4f5b088SMaciej W. Rozycki } 969d4f5b088SMaciej W. Rozycki 970d4f5b088SMaciej W. Rozycki /* 9711da177e4SLinus Torvalds * Emulate the single floating point instruction pointed at by EPC. 9721da177e4SLinus Torvalds * Two instructions if the instruction is in a branch delay slot. 9731da177e4SLinus Torvalds */ 9741da177e4SLinus Torvalds 975515b029dSDavid Daney static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 976445a58ceSPaul Burton struct mm_decoded_insn dec_insn, void __user **fault_addr) 9771da177e4SLinus Torvalds { 978102cedc3SLeonid Yegoshin unsigned long contpc = xcp->cp0_epc + dec_insn.pc_inc; 97993583e17SPaul Burton unsigned int cond, cbit, bit0; 9803f7cac41SRalf Baechle mips_instruction ir; 9813f7cac41SRalf Baechle int likely, pc_inc; 98293583e17SPaul Burton union fpureg *fpr; 9833f7cac41SRalf Baechle u32 __user *wva; 9843f7cac41SRalf Baechle u64 __user *dva; 9853f7cac41SRalf Baechle u32 wval; 9863f7cac41SRalf Baechle u64 dval; 9873f7cac41SRalf Baechle int sig; 9881da177e4SLinus Torvalds 98970e4c234SRalf Baechle /* 99070e4c234SRalf Baechle * These are giving gcc a gentle hint about what to expect in 99170e4c234SRalf Baechle * dec_inst in order to do better optimization. 99270e4c234SRalf Baechle */ 99370e4c234SRalf Baechle if (!cpu_has_mmips && dec_insn.micro_mips_mode) 99470e4c234SRalf Baechle unreachable(); 99570e4c234SRalf Baechle 9961da177e4SLinus Torvalds /* XXX NEC Vr54xx bug workaround */ 997e7e9cae5SRalf Baechle if (delay_slot(xcp)) { 998102cedc3SLeonid Yegoshin if (dec_insn.micro_mips_mode) { 999102cedc3SLeonid Yegoshin if (!mm_isBranchInstr(xcp, dec_insn, &contpc)) 1000e7e9cae5SRalf Baechle clear_delay_slot(xcp); 1001102cedc3SLeonid Yegoshin } else { 1002102cedc3SLeonid Yegoshin if (!isBranchInstr(xcp, dec_insn, &contpc)) 1003e7e9cae5SRalf Baechle clear_delay_slot(xcp); 1004102cedc3SLeonid Yegoshin } 1005102cedc3SLeonid Yegoshin } 10061da177e4SLinus Torvalds 1007e7e9cae5SRalf Baechle if (delay_slot(xcp)) { 10081da177e4SLinus Torvalds /* 10091da177e4SLinus Torvalds * The instruction to be emulated is in a branch delay slot 10101da177e4SLinus Torvalds * which means that we have to emulate the branch instruction 10111da177e4SLinus Torvalds * BEFORE we do the cop1 instruction. 10121da177e4SLinus Torvalds * 10131da177e4SLinus Torvalds * This branch could be a COP1 branch, but in that case we 10141da177e4SLinus Torvalds * would have had a trap for that instruction, and would not 10151da177e4SLinus Torvalds * come through this route. 10161da177e4SLinus Torvalds * 10171da177e4SLinus Torvalds * Linux MIPS branch emulator operates on context, updating the 10181da177e4SLinus Torvalds * cp0_epc. 10191da177e4SLinus Torvalds */ 1020102cedc3SLeonid Yegoshin ir = dec_insn.next_insn; /* process delay slot instr */ 1021102cedc3SLeonid Yegoshin pc_inc = dec_insn.next_pc_inc; 1022333d1f67SRalf Baechle } else { 1023102cedc3SLeonid Yegoshin ir = dec_insn.insn; /* process current instr */ 1024102cedc3SLeonid Yegoshin pc_inc = dec_insn.pc_inc; 1025102cedc3SLeonid Yegoshin } 1026102cedc3SLeonid Yegoshin 1027102cedc3SLeonid Yegoshin /* 1028102cedc3SLeonid Yegoshin * Since microMIPS FPU instructios are a subset of MIPS32 FPU 1029102cedc3SLeonid Yegoshin * instructions, we want to convert microMIPS FPU instructions 1030102cedc3SLeonid Yegoshin * into MIPS32 instructions so that we could reuse all of the 1031102cedc3SLeonid Yegoshin * FPU emulation code. 1032102cedc3SLeonid Yegoshin * 1033102cedc3SLeonid Yegoshin * NOTE: We cannot do this for branch instructions since they 1034102cedc3SLeonid Yegoshin * are not a subset. Example: Cannot emulate a 16-bit 1035102cedc3SLeonid Yegoshin * aligned target address with a MIPS32 instruction. 1036102cedc3SLeonid Yegoshin */ 1037102cedc3SLeonid Yegoshin if (dec_insn.micro_mips_mode) { 1038102cedc3SLeonid Yegoshin /* 1039102cedc3SLeonid Yegoshin * If next instruction is a 16-bit instruction, then it 1040102cedc3SLeonid Yegoshin * it cannot be a FPU instruction. This could happen 1041102cedc3SLeonid Yegoshin * since we can be called for non-FPU instructions. 1042102cedc3SLeonid Yegoshin */ 1043102cedc3SLeonid Yegoshin if ((pc_inc == 2) || 1044102cedc3SLeonid Yegoshin (microMIPS32_to_MIPS32((union mips_instruction *)&ir) 1045102cedc3SLeonid Yegoshin == SIGILL)) 1046102cedc3SLeonid Yegoshin return SIGILL; 10471da177e4SLinus Torvalds } 10481da177e4SLinus Torvalds 10491da177e4SLinus Torvalds emul: 1050a8b0ca17SPeter Zijlstra perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, xcp, 0); 1051b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(emulated); 10521da177e4SLinus Torvalds switch (MIPSInst_OPCODE(ir)) { 10533f7cac41SRalf Baechle case ldc1_op: 10543f7cac41SRalf Baechle dva = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] + 10551da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 1056b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(loads); 1057515b029dSDavid Daney 10583f7cac41SRalf Baechle if (!access_ok(VERIFY_READ, dva, sizeof(u64))) { 1059b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 10603f7cac41SRalf Baechle *fault_addr = dva; 10611da177e4SLinus Torvalds return SIGBUS; 10621da177e4SLinus Torvalds } 10633f7cac41SRalf Baechle if (__get_user(dval, dva)) { 1064515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 10653f7cac41SRalf Baechle *fault_addr = dva; 1066515b029dSDavid Daney return SIGSEGV; 1067515b029dSDavid Daney } 10683f7cac41SRalf Baechle DITOREG(dval, MIPSInst_RT(ir)); 10691da177e4SLinus Torvalds break; 10701da177e4SLinus Torvalds 10713f7cac41SRalf Baechle case sdc1_op: 10723f7cac41SRalf Baechle dva = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] + 10731da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 1074b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(stores); 10753f7cac41SRalf Baechle DIFROMREG(dval, MIPSInst_RT(ir)); 10763f7cac41SRalf Baechle if (!access_ok(VERIFY_WRITE, dva, sizeof(u64))) { 1077b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 10783f7cac41SRalf Baechle *fault_addr = dva; 10791da177e4SLinus Torvalds return SIGBUS; 10801da177e4SLinus Torvalds } 10813f7cac41SRalf Baechle if (__put_user(dval, dva)) { 1082515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 10833f7cac41SRalf Baechle *fault_addr = dva; 1084515b029dSDavid Daney return SIGSEGV; 1085515b029dSDavid Daney } 10861da177e4SLinus Torvalds break; 10871da177e4SLinus Torvalds 10883f7cac41SRalf Baechle case lwc1_op: 10893f7cac41SRalf Baechle wva = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] + 10901da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 1091b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(loads); 10923f7cac41SRalf Baechle if (!access_ok(VERIFY_READ, wva, sizeof(u32))) { 1093b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 10943f7cac41SRalf Baechle *fault_addr = wva; 10951da177e4SLinus Torvalds return SIGBUS; 10961da177e4SLinus Torvalds } 10973f7cac41SRalf Baechle if (__get_user(wval, wva)) { 1098515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 10993f7cac41SRalf Baechle *fault_addr = wva; 1100515b029dSDavid Daney return SIGSEGV; 1101515b029dSDavid Daney } 11023f7cac41SRalf Baechle SITOREG(wval, MIPSInst_RT(ir)); 11031da177e4SLinus Torvalds break; 11041da177e4SLinus Torvalds 11053f7cac41SRalf Baechle case swc1_op: 11063f7cac41SRalf Baechle wva = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] + 11071da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 1108b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(stores); 11093f7cac41SRalf Baechle SIFROMREG(wval, MIPSInst_RT(ir)); 11103f7cac41SRalf Baechle if (!access_ok(VERIFY_WRITE, wva, sizeof(u32))) { 1111b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 11123f7cac41SRalf Baechle *fault_addr = wva; 11131da177e4SLinus Torvalds return SIGBUS; 11141da177e4SLinus Torvalds } 11153f7cac41SRalf Baechle if (__put_user(wval, wva)) { 1116515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 11173f7cac41SRalf Baechle *fault_addr = wva; 1118515b029dSDavid Daney return SIGSEGV; 1119515b029dSDavid Daney } 11201da177e4SLinus Torvalds break; 11211da177e4SLinus Torvalds 11221da177e4SLinus Torvalds case cop1_op: 11231da177e4SLinus Torvalds switch (MIPSInst_RS(ir)) { 11241da177e4SLinus Torvalds case dmfc_op: 112508a07904SRalf Baechle if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) 112608a07904SRalf Baechle return SIGILL; 112708a07904SRalf Baechle 11281da177e4SLinus Torvalds /* copregister fs -> gpr[rt] */ 11291da177e4SLinus Torvalds if (MIPSInst_RT(ir) != 0) { 11301da177e4SLinus Torvalds DIFROMREG(xcp->regs[MIPSInst_RT(ir)], 11311da177e4SLinus Torvalds MIPSInst_RD(ir)); 11321da177e4SLinus Torvalds } 11331da177e4SLinus Torvalds break; 11341da177e4SLinus Torvalds 11351da177e4SLinus Torvalds case dmtc_op: 113608a07904SRalf Baechle if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) 113708a07904SRalf Baechle return SIGILL; 113808a07904SRalf Baechle 11391da177e4SLinus Torvalds /* copregister fs <- rt */ 11401da177e4SLinus Torvalds DITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); 11411da177e4SLinus Torvalds break; 11421da177e4SLinus Torvalds 11431ac94400SLeonid Yegoshin case mfhc_op: 1144e8f80cc1SMarkos Chandras if (!cpu_has_mips_r2_r6) 114570f743d1SMaciej W. Rozycki return SIGILL; 11461ac94400SLeonid Yegoshin 11471ac94400SLeonid Yegoshin /* copregister rd -> gpr[rt] */ 11481ac94400SLeonid Yegoshin if (MIPSInst_RT(ir) != 0) { 11491ac94400SLeonid Yegoshin SIFROMHREG(xcp->regs[MIPSInst_RT(ir)], 11501ac94400SLeonid Yegoshin MIPSInst_RD(ir)); 11511ac94400SLeonid Yegoshin } 11521ac94400SLeonid Yegoshin break; 11531ac94400SLeonid Yegoshin 11541ac94400SLeonid Yegoshin case mthc_op: 1155e8f80cc1SMarkos Chandras if (!cpu_has_mips_r2_r6) 115670f743d1SMaciej W. Rozycki return SIGILL; 11571ac94400SLeonid Yegoshin 11581ac94400SLeonid Yegoshin /* copregister rd <- gpr[rt] */ 11591ac94400SLeonid Yegoshin SITOHREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); 11601ac94400SLeonid Yegoshin break; 11611ac94400SLeonid Yegoshin 11621da177e4SLinus Torvalds case mfc_op: 11631da177e4SLinus Torvalds /* copregister rd -> gpr[rt] */ 11641da177e4SLinus Torvalds if (MIPSInst_RT(ir) != 0) { 11651da177e4SLinus Torvalds SIFROMREG(xcp->regs[MIPSInst_RT(ir)], 11661da177e4SLinus Torvalds MIPSInst_RD(ir)); 11671da177e4SLinus Torvalds } 11681da177e4SLinus Torvalds break; 11691da177e4SLinus Torvalds 11701da177e4SLinus Torvalds case mtc_op: 11711da177e4SLinus Torvalds /* copregister rd <- rt */ 11721da177e4SLinus Torvalds SITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); 11731da177e4SLinus Torvalds break; 11741da177e4SLinus Torvalds 11753f7cac41SRalf Baechle case cfc_op: 11761da177e4SLinus Torvalds /* cop control register rd -> gpr[rt] */ 1177d4f5b088SMaciej W. Rozycki cop1_cfc(xcp, ctx, ir); 11781da177e4SLinus Torvalds break; 11791da177e4SLinus Torvalds 11803f7cac41SRalf Baechle case ctc_op: 11811da177e4SLinus Torvalds /* copregister rd <- rt */ 1182d4f5b088SMaciej W. Rozycki cop1_ctc(xcp, ctx, ir); 11831da177e4SLinus Torvalds if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 11841da177e4SLinus Torvalds return SIGFPE; 11851da177e4SLinus Torvalds } 11861da177e4SLinus Torvalds break; 11871da177e4SLinus Torvalds 1188c909ca71SMarkos Chandras case bc1eqz_op: 1189c909ca71SMarkos Chandras case bc1nez_op: 1190c909ca71SMarkos Chandras if (!cpu_has_mips_r6 || delay_slot(xcp)) 1191c909ca71SMarkos Chandras return SIGILL; 1192c909ca71SMarkos Chandras 1193*61100500SAleksandar Markovic likely = 0; 1194*61100500SAleksandar Markovic cond = 0; 119593583e17SPaul Burton fpr = ¤t->thread.fpu.fpr[MIPSInst_RT(ir)]; 119693583e17SPaul Burton bit0 = get_fpr32(fpr, 0) & 0x1; 1197c909ca71SMarkos Chandras switch (MIPSInst_RS(ir)) { 1198c909ca71SMarkos Chandras case bc1eqz_op: 1199454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(bc1eqz); 120093583e17SPaul Burton cond = bit0 == 0; 1201c909ca71SMarkos Chandras break; 1202c909ca71SMarkos Chandras case bc1nez_op: 1203454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(bc1nez); 120493583e17SPaul Burton cond = bit0 != 0; 1205c909ca71SMarkos Chandras break; 1206c909ca71SMarkos Chandras } 1207c909ca71SMarkos Chandras goto branch_common; 1208c909ca71SMarkos Chandras 12093f7cac41SRalf Baechle case bc_op: 1210e7e9cae5SRalf Baechle if (delay_slot(xcp)) 12111da177e4SLinus Torvalds return SIGILL; 12121da177e4SLinus Torvalds 121308a07904SRalf Baechle if (cpu_has_mips_4_5_r) 121408a07904SRalf Baechle cbit = fpucondbit[MIPSInst_RT(ir) >> 2]; 121508a07904SRalf Baechle else 121608a07904SRalf Baechle cbit = FPU_CSR_COND; 121708a07904SRalf Baechle cond = ctx->fcr31 & cbit; 121808a07904SRalf Baechle 12193f7cac41SRalf Baechle likely = 0; 12201da177e4SLinus Torvalds switch (MIPSInst_RT(ir) & 3) { 12211da177e4SLinus Torvalds case bcfl_op: 12222d83fea7SMaciej W. Rozycki if (cpu_has_mips_2_3_4_5_r) 12231da177e4SLinus Torvalds likely = 1; 12242d83fea7SMaciej W. Rozycki /* Fall through */ 12251da177e4SLinus Torvalds case bcf_op: 12261da177e4SLinus Torvalds cond = !cond; 12271da177e4SLinus Torvalds break; 12281da177e4SLinus Torvalds case bctl_op: 12292d83fea7SMaciej W. Rozycki if (cpu_has_mips_2_3_4_5_r) 12301da177e4SLinus Torvalds likely = 1; 12312d83fea7SMaciej W. Rozycki /* Fall through */ 12321da177e4SLinus Torvalds case bct_op: 12331da177e4SLinus Torvalds break; 12341da177e4SLinus Torvalds } 1235c909ca71SMarkos Chandras branch_common: 1236ae5f3f5bSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(branches); 1237e7e9cae5SRalf Baechle set_delay_slot(xcp); 12381da177e4SLinus Torvalds if (cond) { 12393f7cac41SRalf Baechle /* 12403f7cac41SRalf Baechle * Branch taken: emulate dslot instruction 12411da177e4SLinus Torvalds */ 12429ab4471cSMaciej W. Rozycki unsigned long bcpc; 12439ab4471cSMaciej W. Rozycki 12449ab4471cSMaciej W. Rozycki /* 12459ab4471cSMaciej W. Rozycki * Remember EPC at the branch to point back 12469ab4471cSMaciej W. Rozycki * at so that any delay-slot instruction 12479ab4471cSMaciej W. Rozycki * signal is not silently ignored. 12489ab4471cSMaciej W. Rozycki */ 12499ab4471cSMaciej W. Rozycki bcpc = xcp->cp0_epc; 1250102cedc3SLeonid Yegoshin xcp->cp0_epc += dec_insn.pc_inc; 12511da177e4SLinus Torvalds 1252102cedc3SLeonid Yegoshin contpc = MIPSInst_SIMM(ir); 1253102cedc3SLeonid Yegoshin ir = dec_insn.next_insn; 1254102cedc3SLeonid Yegoshin if (dec_insn.micro_mips_mode) { 1255102cedc3SLeonid Yegoshin contpc = (xcp->cp0_epc + (contpc << 1)); 1256102cedc3SLeonid Yegoshin 1257102cedc3SLeonid Yegoshin /* If 16-bit instruction, not FPU. */ 1258102cedc3SLeonid Yegoshin if ((dec_insn.next_pc_inc == 2) || 1259102cedc3SLeonid Yegoshin (microMIPS32_to_MIPS32((union mips_instruction *)&ir) == SIGILL)) { 1260102cedc3SLeonid Yegoshin 1261102cedc3SLeonid Yegoshin /* 1262102cedc3SLeonid Yegoshin * Since this instruction will 1263102cedc3SLeonid Yegoshin * be put on the stack with 1264102cedc3SLeonid Yegoshin * 32-bit words, get around 1265102cedc3SLeonid Yegoshin * this problem by putting a 1266102cedc3SLeonid Yegoshin * NOP16 as the second one. 1267102cedc3SLeonid Yegoshin */ 1268102cedc3SLeonid Yegoshin if (dec_insn.next_pc_inc == 2) 1269102cedc3SLeonid Yegoshin ir = (ir & (~0xffff)) | MM_NOP16; 1270102cedc3SLeonid Yegoshin 1271102cedc3SLeonid Yegoshin /* 1272102cedc3SLeonid Yegoshin * Single step the non-CP1 1273102cedc3SLeonid Yegoshin * instruction in the dslot. 1274102cedc3SLeonid Yegoshin */ 12759ab4471cSMaciej W. Rozycki sig = mips_dsemul(xcp, ir, 1276432c6bacSPaul Burton bcpc, contpc); 1277e4553573SMaciej W. Rozycki if (sig < 0) 1278e4553573SMaciej W. Rozycki break; 12799ab4471cSMaciej W. Rozycki if (sig) 12809ab4471cSMaciej W. Rozycki xcp->cp0_epc = bcpc; 12819ab4471cSMaciej W. Rozycki /* 12829ab4471cSMaciej W. Rozycki * SIGILL forces out of 12839ab4471cSMaciej W. Rozycki * the emulation loop. 12849ab4471cSMaciej W. Rozycki */ 12859ab4471cSMaciej W. Rozycki return sig ? sig : SIGILL; 1286515b029dSDavid Daney } 1287102cedc3SLeonid Yegoshin } else 1288102cedc3SLeonid Yegoshin contpc = (xcp->cp0_epc + (contpc << 2)); 12891da177e4SLinus Torvalds 12901da177e4SLinus Torvalds switch (MIPSInst_OPCODE(ir)) { 12911da177e4SLinus Torvalds case lwc1_op: 12921da177e4SLinus Torvalds case swc1_op: 129308a07904SRalf Baechle goto emul; 12943f7cac41SRalf Baechle 12951da177e4SLinus Torvalds case ldc1_op: 12961da177e4SLinus Torvalds case sdc1_op: 12972d83fea7SMaciej W. Rozycki if (cpu_has_mips_2_3_4_5_r) 129808a07904SRalf Baechle goto emul; 129908a07904SRalf Baechle 13009ab4471cSMaciej W. Rozycki goto bc_sigill; 13013f7cac41SRalf Baechle 13021da177e4SLinus Torvalds case cop1_op: 130308a07904SRalf Baechle goto emul; 13043f7cac41SRalf Baechle 13051da177e4SLinus Torvalds case cop1x_op: 13062d83fea7SMaciej W. Rozycki if (cpu_has_mips_4_5_64_r2_r6) 13071da177e4SLinus Torvalds /* its one of ours */ 13081da177e4SLinus Torvalds goto emul; 130908a07904SRalf Baechle 13109ab4471cSMaciej W. Rozycki goto bc_sigill; 13113f7cac41SRalf Baechle 13121da177e4SLinus Torvalds case spec_op: 13132d83fea7SMaciej W. Rozycki switch (MIPSInst_FUNC(ir)) { 13142d83fea7SMaciej W. Rozycki case movc_op: 13152d83fea7SMaciej W. Rozycki if (cpu_has_mips_4_5_r) 13161da177e4SLinus Torvalds goto emul; 13172d83fea7SMaciej W. Rozycki 13189ab4471cSMaciej W. Rozycki goto bc_sigill; 13192d83fea7SMaciej W. Rozycki } 13201da177e4SLinus Torvalds break; 13219ab4471cSMaciej W. Rozycki 13229ab4471cSMaciej W. Rozycki bc_sigill: 13239ab4471cSMaciej W. Rozycki xcp->cp0_epc = bcpc; 13249ab4471cSMaciej W. Rozycki return SIGILL; 13251da177e4SLinus Torvalds } 13261da177e4SLinus Torvalds 13271da177e4SLinus Torvalds /* 13281da177e4SLinus Torvalds * Single step the non-cp1 13291da177e4SLinus Torvalds * instruction in the dslot 13301da177e4SLinus Torvalds */ 1331432c6bacSPaul Burton sig = mips_dsemul(xcp, ir, bcpc, contpc); 1332e4553573SMaciej W. Rozycki if (sig < 0) 1333e4553573SMaciej W. Rozycki break; 13349ab4471cSMaciej W. Rozycki if (sig) 13359ab4471cSMaciej W. Rozycki xcp->cp0_epc = bcpc; 13369ab4471cSMaciej W. Rozycki /* SIGILL forces out of the emulation loop. */ 13379ab4471cSMaciej W. Rozycki return sig ? sig : SIGILL; 13383f7cac41SRalf Baechle } else if (likely) { /* branch not taken */ 13391da177e4SLinus Torvalds /* 13401da177e4SLinus Torvalds * branch likely nullifies 13411da177e4SLinus Torvalds * dslot if not taken 13421da177e4SLinus Torvalds */ 1343102cedc3SLeonid Yegoshin xcp->cp0_epc += dec_insn.pc_inc; 1344102cedc3SLeonid Yegoshin contpc += dec_insn.pc_inc; 13451da177e4SLinus Torvalds /* 13461da177e4SLinus Torvalds * else continue & execute 13471da177e4SLinus Torvalds * dslot as normal insn 13481da177e4SLinus Torvalds */ 13491da177e4SLinus Torvalds } 13501da177e4SLinus Torvalds break; 13511da177e4SLinus Torvalds 13521da177e4SLinus Torvalds default: 13531da177e4SLinus Torvalds if (!(MIPSInst_RS(ir) & 0x10)) 13541da177e4SLinus Torvalds return SIGILL; 13551da177e4SLinus Torvalds 13561da177e4SLinus Torvalds /* a real fpu computation instruction */ 13578904d5b1SAleksandar Markovic sig = fpu_emu(xcp, ctx, ir); 13588904d5b1SAleksandar Markovic if (sig) 13591da177e4SLinus Torvalds return sig; 13601da177e4SLinus Torvalds } 13611da177e4SLinus Torvalds break; 13621da177e4SLinus Torvalds 13633f7cac41SRalf Baechle case cop1x_op: 13642d83fea7SMaciej W. Rozycki if (!cpu_has_mips_4_5_64_r2_r6) 136508a07904SRalf Baechle return SIGILL; 136608a07904SRalf Baechle 136708a07904SRalf Baechle sig = fpux_emu(xcp, ctx, ir, fault_addr); 1368515b029dSDavid Daney if (sig) 13691da177e4SLinus Torvalds return sig; 13701da177e4SLinus Torvalds break; 13711da177e4SLinus Torvalds 13721da177e4SLinus Torvalds case spec_op: 137308a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 137408a07904SRalf Baechle return SIGILL; 137508a07904SRalf Baechle 13761da177e4SLinus Torvalds if (MIPSInst_FUNC(ir) != movc_op) 13771da177e4SLinus Torvalds return SIGILL; 13781da177e4SLinus Torvalds cond = fpucondbit[MIPSInst_RT(ir) >> 2]; 13791da177e4SLinus Torvalds if (((ctx->fcr31 & cond) != 0) == ((MIPSInst_RT(ir) & 1) != 0)) 13801da177e4SLinus Torvalds xcp->regs[MIPSInst_RD(ir)] = 13811da177e4SLinus Torvalds xcp->regs[MIPSInst_RS(ir)]; 13821da177e4SLinus Torvalds break; 13831da177e4SLinus Torvalds default: 13841da177e4SLinus Torvalds return SIGILL; 13851da177e4SLinus Torvalds } 13861da177e4SLinus Torvalds 13871da177e4SLinus Torvalds /* we did it !! */ 1388e70dfc10SAtsushi Nemoto xcp->cp0_epc = contpc; 1389e7e9cae5SRalf Baechle clear_delay_slot(xcp); 1390333d1f67SRalf Baechle 13911da177e4SLinus Torvalds return 0; 13921da177e4SLinus Torvalds } 13931da177e4SLinus Torvalds 13941da177e4SLinus Torvalds /* 13951da177e4SLinus Torvalds * Conversion table from MIPS compare ops 48-63 13961da177e4SLinus Torvalds * cond = ieee754dp_cmp(x,y,IEEE754_UN,sig); 13971da177e4SLinus Torvalds */ 13981da177e4SLinus Torvalds static const unsigned char cmptab[8] = { 13991da177e4SLinus Torvalds 0, /* cmp_0 (sig) cmp_sf */ 14001da177e4SLinus Torvalds IEEE754_CUN, /* cmp_un (sig) cmp_ngle */ 14011da177e4SLinus Torvalds IEEE754_CEQ, /* cmp_eq (sig) cmp_seq */ 14021da177e4SLinus Torvalds IEEE754_CEQ | IEEE754_CUN, /* cmp_ueq (sig) cmp_ngl */ 14031da177e4SLinus Torvalds IEEE754_CLT, /* cmp_olt (sig) cmp_lt */ 14041da177e4SLinus Torvalds IEEE754_CLT | IEEE754_CUN, /* cmp_ult (sig) cmp_nge */ 14051da177e4SLinus Torvalds IEEE754_CLT | IEEE754_CEQ, /* cmp_ole (sig) cmp_le */ 14061da177e4SLinus Torvalds IEEE754_CLT | IEEE754_CEQ | IEEE754_CUN, /* cmp_ule (sig) cmp_ngt */ 14071da177e4SLinus Torvalds }; 14081da177e4SLinus Torvalds 1409f8c3c671SMarkos Chandras static const unsigned char negative_cmptab[8] = { 1410f8c3c671SMarkos Chandras 0, /* Reserved */ 1411f8c3c671SMarkos Chandras IEEE754_CLT | IEEE754_CGT | IEEE754_CEQ, 1412f8c3c671SMarkos Chandras IEEE754_CLT | IEEE754_CGT | IEEE754_CUN, 1413f8c3c671SMarkos Chandras IEEE754_CLT | IEEE754_CGT, 1414f8c3c671SMarkos Chandras /* Reserved */ 1415f8c3c671SMarkos Chandras }; 1416f8c3c671SMarkos Chandras 14171da177e4SLinus Torvalds 14181da177e4SLinus Torvalds /* 14191da177e4SLinus Torvalds * Additional MIPS4 instructions 14201da177e4SLinus Torvalds */ 14211da177e4SLinus Torvalds 14221da177e4SLinus Torvalds #define DEF3OP(name, p, f1, f2, f3) \ 142347fa0c02SRalf Baechle static union ieee754##p fpemu_##p##_##name(union ieee754##p r, \ 142447fa0c02SRalf Baechle union ieee754##p s, union ieee754##p t) \ 14251da177e4SLinus Torvalds { \ 1426cd21dfcfSRalf Baechle struct _ieee754_csr ieee754_csr_save; \ 14271da177e4SLinus Torvalds s = f1(s, t); \ 14281da177e4SLinus Torvalds ieee754_csr_save = ieee754_csr; \ 14291da177e4SLinus Torvalds s = f2(s, r); \ 14301da177e4SLinus Torvalds ieee754_csr_save.cx |= ieee754_csr.cx; \ 14311da177e4SLinus Torvalds ieee754_csr_save.sx |= ieee754_csr.sx; \ 14321da177e4SLinus Torvalds s = f3(s); \ 14331da177e4SLinus Torvalds ieee754_csr.cx |= ieee754_csr_save.cx; \ 14341da177e4SLinus Torvalds ieee754_csr.sx |= ieee754_csr_save.sx; \ 14351da177e4SLinus Torvalds return s; \ 14361da177e4SLinus Torvalds } 14371da177e4SLinus Torvalds 14382209bcb1SRalf Baechle static union ieee754dp fpemu_dp_recip(union ieee754dp d) 14391da177e4SLinus Torvalds { 14401da177e4SLinus Torvalds return ieee754dp_div(ieee754dp_one(0), d); 14411da177e4SLinus Torvalds } 14421da177e4SLinus Torvalds 14432209bcb1SRalf Baechle static union ieee754dp fpemu_dp_rsqrt(union ieee754dp d) 14441da177e4SLinus Torvalds { 14451da177e4SLinus Torvalds return ieee754dp_div(ieee754dp_one(0), ieee754dp_sqrt(d)); 14461da177e4SLinus Torvalds } 14471da177e4SLinus Torvalds 14482209bcb1SRalf Baechle static union ieee754sp fpemu_sp_recip(union ieee754sp s) 14491da177e4SLinus Torvalds { 14501da177e4SLinus Torvalds return ieee754sp_div(ieee754sp_one(0), s); 14511da177e4SLinus Torvalds } 14521da177e4SLinus Torvalds 14532209bcb1SRalf Baechle static union ieee754sp fpemu_sp_rsqrt(union ieee754sp s) 14541da177e4SLinus Torvalds { 14551da177e4SLinus Torvalds return ieee754sp_div(ieee754sp_one(0), ieee754sp_sqrt(s)); 14561da177e4SLinus Torvalds } 14571da177e4SLinus Torvalds 14581da177e4SLinus Torvalds DEF3OP(madd, sp, ieee754sp_mul, ieee754sp_add, ); 14591da177e4SLinus Torvalds DEF3OP(msub, sp, ieee754sp_mul, ieee754sp_sub, ); 14601da177e4SLinus Torvalds DEF3OP(nmadd, sp, ieee754sp_mul, ieee754sp_add, ieee754sp_neg); 14611da177e4SLinus Torvalds DEF3OP(nmsub, sp, ieee754sp_mul, ieee754sp_sub, ieee754sp_neg); 14621da177e4SLinus Torvalds DEF3OP(madd, dp, ieee754dp_mul, ieee754dp_add, ); 14631da177e4SLinus Torvalds DEF3OP(msub, dp, ieee754dp_mul, ieee754dp_sub, ); 14641da177e4SLinus Torvalds DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg); 14651da177e4SLinus Torvalds DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg); 14661da177e4SLinus Torvalds 1467eae89076SAtsushi Nemoto static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 1468445a58ceSPaul Burton mips_instruction ir, void __user **fault_addr) 14691da177e4SLinus Torvalds { 1470a58f85b5SAleksandar Markovic unsigned int rcsr = 0; /* resulting csr */ 14711da177e4SLinus Torvalds 1472b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(cp1xops); 14731da177e4SLinus Torvalds 14741da177e4SLinus Torvalds switch (MIPSInst_FMA_FFMT(ir)) { 14751da177e4SLinus Torvalds case s_fmt:{ /* 0 */ 14761da177e4SLinus Torvalds 14772209bcb1SRalf Baechle union ieee754sp(*handler) (union ieee754sp, union ieee754sp, union ieee754sp); 14782209bcb1SRalf Baechle union ieee754sp fd, fr, fs, ft; 14793fccc015SRalf Baechle u32 __user *va; 14801da177e4SLinus Torvalds u32 val; 14811da177e4SLinus Torvalds 14821da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 14831da177e4SLinus Torvalds case lwxc1_op: 14843fccc015SRalf Baechle va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 14851da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 14861da177e4SLinus Torvalds 1487b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(loads); 1488515b029dSDavid Daney if (!access_ok(VERIFY_READ, va, sizeof(u32))) { 1489b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1490515b029dSDavid Daney *fault_addr = va; 14911da177e4SLinus Torvalds return SIGBUS; 14921da177e4SLinus Torvalds } 1493515b029dSDavid Daney if (__get_user(val, va)) { 1494515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1495515b029dSDavid Daney *fault_addr = va; 1496515b029dSDavid Daney return SIGSEGV; 1497515b029dSDavid Daney } 14981da177e4SLinus Torvalds SITOREG(val, MIPSInst_FD(ir)); 14991da177e4SLinus Torvalds break; 15001da177e4SLinus Torvalds 15011da177e4SLinus Torvalds case swxc1_op: 15023fccc015SRalf Baechle va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 15031da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 15041da177e4SLinus Torvalds 1505b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(stores); 15061da177e4SLinus Torvalds 15071da177e4SLinus Torvalds SIFROMREG(val, MIPSInst_FS(ir)); 1508515b029dSDavid Daney if (!access_ok(VERIFY_WRITE, va, sizeof(u32))) { 1509515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1510515b029dSDavid Daney *fault_addr = va; 1511515b029dSDavid Daney return SIGBUS; 1512515b029dSDavid Daney } 15131da177e4SLinus Torvalds if (put_user(val, va)) { 1514b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1515515b029dSDavid Daney *fault_addr = va; 1516515b029dSDavid Daney return SIGSEGV; 15171da177e4SLinus Torvalds } 15181da177e4SLinus Torvalds break; 15191da177e4SLinus Torvalds 15201da177e4SLinus Torvalds case madd_s_op: 15211da177e4SLinus Torvalds handler = fpemu_sp_madd; 15221da177e4SLinus Torvalds goto scoptop; 15231da177e4SLinus Torvalds case msub_s_op: 15241da177e4SLinus Torvalds handler = fpemu_sp_msub; 15251da177e4SLinus Torvalds goto scoptop; 15261da177e4SLinus Torvalds case nmadd_s_op: 15271da177e4SLinus Torvalds handler = fpemu_sp_nmadd; 15281da177e4SLinus Torvalds goto scoptop; 15291da177e4SLinus Torvalds case nmsub_s_op: 15301da177e4SLinus Torvalds handler = fpemu_sp_nmsub; 15311da177e4SLinus Torvalds goto scoptop; 15321da177e4SLinus Torvalds 15331da177e4SLinus Torvalds scoptop: 15341da177e4SLinus Torvalds SPFROMREG(fr, MIPSInst_FR(ir)); 15351da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 15361da177e4SLinus Torvalds SPFROMREG(ft, MIPSInst_FT(ir)); 15371da177e4SLinus Torvalds fd = (*handler) (fr, fs, ft); 15381da177e4SLinus Torvalds SPTOREG(fd, MIPSInst_FD(ir)); 15391da177e4SLinus Torvalds 15401da177e4SLinus Torvalds copcsr: 1541c4103526SDeng-Cheng Zhu if (ieee754_cxtest(IEEE754_INEXACT)) { 1542c4103526SDeng-Cheng Zhu MIPS_FPU_EMU_INC_STATS(ieee754_inexact); 15431da177e4SLinus Torvalds rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S; 1544c4103526SDeng-Cheng Zhu } 1545c4103526SDeng-Cheng Zhu if (ieee754_cxtest(IEEE754_UNDERFLOW)) { 1546c4103526SDeng-Cheng Zhu MIPS_FPU_EMU_INC_STATS(ieee754_underflow); 15471da177e4SLinus Torvalds rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S; 1548c4103526SDeng-Cheng Zhu } 1549c4103526SDeng-Cheng Zhu if (ieee754_cxtest(IEEE754_OVERFLOW)) { 1550c4103526SDeng-Cheng Zhu MIPS_FPU_EMU_INC_STATS(ieee754_overflow); 15511da177e4SLinus Torvalds rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S; 1552c4103526SDeng-Cheng Zhu } 1553c4103526SDeng-Cheng Zhu if (ieee754_cxtest(IEEE754_INVALID_OPERATION)) { 1554c4103526SDeng-Cheng Zhu MIPS_FPU_EMU_INC_STATS(ieee754_invalidop); 15551da177e4SLinus Torvalds rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S; 1556c4103526SDeng-Cheng Zhu } 15571da177e4SLinus Torvalds 15581da177e4SLinus Torvalds ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr; 15591da177e4SLinus Torvalds if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 15603f7cac41SRalf Baechle /*printk ("SIGFPE: FPU csr = %08x\n", 15611da177e4SLinus Torvalds ctx->fcr31); */ 15621da177e4SLinus Torvalds return SIGFPE; 15631da177e4SLinus Torvalds } 15641da177e4SLinus Torvalds 15651da177e4SLinus Torvalds break; 15661da177e4SLinus Torvalds 15671da177e4SLinus Torvalds default: 15681da177e4SLinus Torvalds return SIGILL; 15691da177e4SLinus Torvalds } 15701da177e4SLinus Torvalds break; 15711da177e4SLinus Torvalds } 15721da177e4SLinus Torvalds 15731da177e4SLinus Torvalds case d_fmt:{ /* 1 */ 15742209bcb1SRalf Baechle union ieee754dp(*handler) (union ieee754dp, union ieee754dp, union ieee754dp); 15752209bcb1SRalf Baechle union ieee754dp fd, fr, fs, ft; 15763fccc015SRalf Baechle u64 __user *va; 15771da177e4SLinus Torvalds u64 val; 15781da177e4SLinus Torvalds 15791da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 15801da177e4SLinus Torvalds case ldxc1_op: 15813fccc015SRalf Baechle va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 15821da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 15831da177e4SLinus Torvalds 1584b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(loads); 1585515b029dSDavid Daney if (!access_ok(VERIFY_READ, va, sizeof(u64))) { 1586b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1587515b029dSDavid Daney *fault_addr = va; 15881da177e4SLinus Torvalds return SIGBUS; 15891da177e4SLinus Torvalds } 1590515b029dSDavid Daney if (__get_user(val, va)) { 1591515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1592515b029dSDavid Daney *fault_addr = va; 1593515b029dSDavid Daney return SIGSEGV; 1594515b029dSDavid Daney } 15951da177e4SLinus Torvalds DITOREG(val, MIPSInst_FD(ir)); 15961da177e4SLinus Torvalds break; 15971da177e4SLinus Torvalds 15981da177e4SLinus Torvalds case sdxc1_op: 15993fccc015SRalf Baechle va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 16001da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 16011da177e4SLinus Torvalds 1602b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(stores); 16031da177e4SLinus Torvalds DIFROMREG(val, MIPSInst_FS(ir)); 1604515b029dSDavid Daney if (!access_ok(VERIFY_WRITE, va, sizeof(u64))) { 1605b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1606515b029dSDavid Daney *fault_addr = va; 16071da177e4SLinus Torvalds return SIGBUS; 16081da177e4SLinus Torvalds } 1609515b029dSDavid Daney if (__put_user(val, va)) { 1610515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1611515b029dSDavid Daney *fault_addr = va; 1612515b029dSDavid Daney return SIGSEGV; 1613515b029dSDavid Daney } 16141da177e4SLinus Torvalds break; 16151da177e4SLinus Torvalds 16161da177e4SLinus Torvalds case madd_d_op: 16171da177e4SLinus Torvalds handler = fpemu_dp_madd; 16181da177e4SLinus Torvalds goto dcoptop; 16191da177e4SLinus Torvalds case msub_d_op: 16201da177e4SLinus Torvalds handler = fpemu_dp_msub; 16211da177e4SLinus Torvalds goto dcoptop; 16221da177e4SLinus Torvalds case nmadd_d_op: 16231da177e4SLinus Torvalds handler = fpemu_dp_nmadd; 16241da177e4SLinus Torvalds goto dcoptop; 16251da177e4SLinus Torvalds case nmsub_d_op: 16261da177e4SLinus Torvalds handler = fpemu_dp_nmsub; 16271da177e4SLinus Torvalds goto dcoptop; 16281da177e4SLinus Torvalds 16291da177e4SLinus Torvalds dcoptop: 16301da177e4SLinus Torvalds DPFROMREG(fr, MIPSInst_FR(ir)); 16311da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 16321da177e4SLinus Torvalds DPFROMREG(ft, MIPSInst_FT(ir)); 16331da177e4SLinus Torvalds fd = (*handler) (fr, fs, ft); 16341da177e4SLinus Torvalds DPTOREG(fd, MIPSInst_FD(ir)); 16351da177e4SLinus Torvalds goto copcsr; 16361da177e4SLinus Torvalds 16371da177e4SLinus Torvalds default: 16381da177e4SLinus Torvalds return SIGILL; 16391da177e4SLinus Torvalds } 16401da177e4SLinus Torvalds break; 16411da177e4SLinus Torvalds } 16421da177e4SLinus Torvalds 164351061b88SDeng-Cheng Zhu case 0x3: 164451061b88SDeng-Cheng Zhu if (MIPSInst_FUNC(ir) != pfetch_op) 16451da177e4SLinus Torvalds return SIGILL; 164651061b88SDeng-Cheng Zhu 16471da177e4SLinus Torvalds /* ignore prefx operation */ 16481da177e4SLinus Torvalds break; 16491da177e4SLinus Torvalds 16501da177e4SLinus Torvalds default: 16511da177e4SLinus Torvalds return SIGILL; 16521da177e4SLinus Torvalds } 16531da177e4SLinus Torvalds 16541da177e4SLinus Torvalds return 0; 16551da177e4SLinus Torvalds } 16561da177e4SLinus Torvalds 16571da177e4SLinus Torvalds 16581da177e4SLinus Torvalds 16591da177e4SLinus Torvalds /* 16601da177e4SLinus Torvalds * Emulate a single COP1 arithmetic instruction. 16611da177e4SLinus Torvalds */ 1662eae89076SAtsushi Nemoto static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 16631da177e4SLinus Torvalds mips_instruction ir) 16641da177e4SLinus Torvalds { 16651da177e4SLinus Torvalds int rfmt; /* resulting format */ 1666a58f85b5SAleksandar Markovic unsigned int rcsr = 0; /* resulting csr */ 16673f7cac41SRalf Baechle unsigned int oldrm; 16683f7cac41SRalf Baechle unsigned int cbit; 1669a58f85b5SAleksandar Markovic unsigned int cond; 16701da177e4SLinus Torvalds union { 16712209bcb1SRalf Baechle union ieee754dp d; 16722209bcb1SRalf Baechle union ieee754sp s; 16731da177e4SLinus Torvalds int w; 16741da177e4SLinus Torvalds s64 l; 16751da177e4SLinus Torvalds } rv; /* resulting value */ 16763f7cac41SRalf Baechle u64 bits; 16771da177e4SLinus Torvalds 1678b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(cp1ops); 16791da177e4SLinus Torvalds switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) { 16801da177e4SLinus Torvalds case s_fmt: { /* 0 */ 16811da177e4SLinus Torvalds union { 16822209bcb1SRalf Baechle union ieee754sp(*b) (union ieee754sp, union ieee754sp); 16832209bcb1SRalf Baechle union ieee754sp(*u) (union ieee754sp); 16841da177e4SLinus Torvalds } handler; 16854b820d95SPaul Burton union ieee754sp fd, fs, ft; 16861da177e4SLinus Torvalds 16871da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 16881da177e4SLinus Torvalds /* binary ops */ 16891da177e4SLinus Torvalds case fadd_op: 1690454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(add_s); 16911da177e4SLinus Torvalds handler.b = ieee754sp_add; 16921da177e4SLinus Torvalds goto scopbop; 16931da177e4SLinus Torvalds case fsub_op: 1694454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(sub_s); 16951da177e4SLinus Torvalds handler.b = ieee754sp_sub; 16961da177e4SLinus Torvalds goto scopbop; 16971da177e4SLinus Torvalds case fmul_op: 1698454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(mul_s); 16991da177e4SLinus Torvalds handler.b = ieee754sp_mul; 17001da177e4SLinus Torvalds goto scopbop; 17011da177e4SLinus Torvalds case fdiv_op: 1702454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(div_s); 17031da177e4SLinus Torvalds handler.b = ieee754sp_div; 17041da177e4SLinus Torvalds goto scopbop; 17051da177e4SLinus Torvalds 17061da177e4SLinus Torvalds /* unary ops */ 17071da177e4SLinus Torvalds case fsqrt_op: 17082d83fea7SMaciej W. Rozycki if (!cpu_has_mips_2_3_4_5_r) 170908a07904SRalf Baechle return SIGILL; 171008a07904SRalf Baechle 1711454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(sqrt_s); 17121da177e4SLinus Torvalds handler.u = ieee754sp_sqrt; 17131da177e4SLinus Torvalds goto scopuop; 17143f7cac41SRalf Baechle 171508a07904SRalf Baechle /* 171608a07904SRalf Baechle * Note that on some MIPS IV implementations such as the 171708a07904SRalf Baechle * R5000 and R8000 the FSQRT and FRECIP instructions do not 171808a07904SRalf Baechle * achieve full IEEE-754 accuracy - however this emulator does. 171908a07904SRalf Baechle */ 17201da177e4SLinus Torvalds case frsqrt_op: 17212d83fea7SMaciej W. Rozycki if (!cpu_has_mips_4_5_64_r2_r6) 172208a07904SRalf Baechle return SIGILL; 172308a07904SRalf Baechle 1724454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(rsqrt_s); 17251da177e4SLinus Torvalds handler.u = fpemu_sp_rsqrt; 17261da177e4SLinus Torvalds goto scopuop; 17273f7cac41SRalf Baechle 17281da177e4SLinus Torvalds case frecip_op: 17292d83fea7SMaciej W. Rozycki if (!cpu_has_mips_4_5_64_r2_r6) 173008a07904SRalf Baechle return SIGILL; 173108a07904SRalf Baechle 1732454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(recip_s); 17331da177e4SLinus Torvalds handler.u = fpemu_sp_recip; 17341da177e4SLinus Torvalds goto scopuop; 173508a07904SRalf Baechle 17361da177e4SLinus Torvalds case fmovc_op: 173708a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 173808a07904SRalf Baechle return SIGILL; 173908a07904SRalf Baechle 17401da177e4SLinus Torvalds cond = fpucondbit[MIPSInst_FT(ir) >> 2]; 17411da177e4SLinus Torvalds if (((ctx->fcr31 & cond) != 0) != 17421da177e4SLinus Torvalds ((MIPSInst_FT(ir) & 1) != 0)) 17431da177e4SLinus Torvalds return 0; 17441da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 17451da177e4SLinus Torvalds break; 17463f7cac41SRalf Baechle 17471da177e4SLinus Torvalds case fmovz_op: 174808a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 174908a07904SRalf Baechle return SIGILL; 175008a07904SRalf Baechle 17511da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] != 0) 17521da177e4SLinus Torvalds return 0; 17531da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 17541da177e4SLinus Torvalds break; 17553f7cac41SRalf Baechle 17561da177e4SLinus Torvalds case fmovn_op: 175708a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 175808a07904SRalf Baechle return SIGILL; 175908a07904SRalf Baechle 17601da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] == 0) 17611da177e4SLinus Torvalds return 0; 17621da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 17631da177e4SLinus Torvalds break; 17643f7cac41SRalf Baechle 176567613f02SMarkos Chandras case fseleqz_op: 176667613f02SMarkos Chandras if (!cpu_has_mips_r6) 176767613f02SMarkos Chandras return SIGILL; 176867613f02SMarkos Chandras 1769454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(seleqz_s); 177067613f02SMarkos Chandras SPFROMREG(rv.s, MIPSInst_FT(ir)); 177167613f02SMarkos Chandras if (rv.w & 0x1) 177267613f02SMarkos Chandras rv.w = 0; 177367613f02SMarkos Chandras else 177467613f02SMarkos Chandras SPFROMREG(rv.s, MIPSInst_FS(ir)); 177567613f02SMarkos Chandras break; 177667613f02SMarkos Chandras 1777130fe357SMarkos Chandras case fselnez_op: 1778130fe357SMarkos Chandras if (!cpu_has_mips_r6) 1779130fe357SMarkos Chandras return SIGILL; 1780130fe357SMarkos Chandras 1781454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(selnez_s); 1782130fe357SMarkos Chandras SPFROMREG(rv.s, MIPSInst_FT(ir)); 1783130fe357SMarkos Chandras if (rv.w & 0x1) 1784130fe357SMarkos Chandras SPFROMREG(rv.s, MIPSInst_FS(ir)); 1785130fe357SMarkos Chandras else 1786130fe357SMarkos Chandras rv.w = 0; 1787130fe357SMarkos Chandras break; 1788130fe357SMarkos Chandras 1789e24c3becSMarkos Chandras case fmaddf_op: { 1790e24c3becSMarkos Chandras union ieee754sp ft, fs, fd; 1791e24c3becSMarkos Chandras 1792e24c3becSMarkos Chandras if (!cpu_has_mips_r6) 1793e24c3becSMarkos Chandras return SIGILL; 1794e24c3becSMarkos Chandras 1795454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(maddf_s); 1796e24c3becSMarkos Chandras SPFROMREG(ft, MIPSInst_FT(ir)); 1797e24c3becSMarkos Chandras SPFROMREG(fs, MIPSInst_FS(ir)); 1798e24c3becSMarkos Chandras SPFROMREG(fd, MIPSInst_FD(ir)); 1799e24c3becSMarkos Chandras rv.s = ieee754sp_maddf(fd, fs, ft); 1800409fcaceSAleksandar Markovic goto copcsr; 1801e24c3becSMarkos Chandras } 1802e24c3becSMarkos Chandras 180383d43305SMarkos Chandras case fmsubf_op: { 180483d43305SMarkos Chandras union ieee754sp ft, fs, fd; 180583d43305SMarkos Chandras 180683d43305SMarkos Chandras if (!cpu_has_mips_r6) 180783d43305SMarkos Chandras return SIGILL; 180883d43305SMarkos Chandras 1809454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(msubf_s); 181083d43305SMarkos Chandras SPFROMREG(ft, MIPSInst_FT(ir)); 181183d43305SMarkos Chandras SPFROMREG(fs, MIPSInst_FS(ir)); 181283d43305SMarkos Chandras SPFROMREG(fd, MIPSInst_FD(ir)); 181383d43305SMarkos Chandras rv.s = ieee754sp_msubf(fd, fs, ft); 1814409fcaceSAleksandar Markovic goto copcsr; 181583d43305SMarkos Chandras } 181683d43305SMarkos Chandras 1817400bd2e4SMarkos Chandras case frint_op: { 1818400bd2e4SMarkos Chandras union ieee754sp fs; 1819400bd2e4SMarkos Chandras 1820400bd2e4SMarkos Chandras if (!cpu_has_mips_r6) 1821400bd2e4SMarkos Chandras return SIGILL; 1822400bd2e4SMarkos Chandras 1823454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(rint_s); 1824400bd2e4SMarkos Chandras SPFROMREG(fs, MIPSInst_FS(ir)); 18253ec404d8SAleksandar Markovic rv.s = ieee754sp_rint(fs); 1826400bd2e4SMarkos Chandras goto copcsr; 1827400bd2e4SMarkos Chandras } 1828400bd2e4SMarkos Chandras 182938db37baSMarkos Chandras case fclass_op: { 183038db37baSMarkos Chandras union ieee754sp fs; 183138db37baSMarkos Chandras 183238db37baSMarkos Chandras if (!cpu_has_mips_r6) 183338db37baSMarkos Chandras return SIGILL; 183438db37baSMarkos Chandras 1835454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(class_s); 183638db37baSMarkos Chandras SPFROMREG(fs, MIPSInst_FS(ir)); 183738db37baSMarkos Chandras rv.w = ieee754sp_2008class(fs); 183838db37baSMarkos Chandras rfmt = w_fmt; 1839409fcaceSAleksandar Markovic goto copcsr; 184038db37baSMarkos Chandras } 184138db37baSMarkos Chandras 18424e9561b2SMarkos Chandras case fmin_op: { 18434e9561b2SMarkos Chandras union ieee754sp fs, ft; 18444e9561b2SMarkos Chandras 18454e9561b2SMarkos Chandras if (!cpu_has_mips_r6) 18464e9561b2SMarkos Chandras return SIGILL; 18474e9561b2SMarkos Chandras 1848454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(min_s); 18494e9561b2SMarkos Chandras SPFROMREG(ft, MIPSInst_FT(ir)); 18504e9561b2SMarkos Chandras SPFROMREG(fs, MIPSInst_FS(ir)); 18514e9561b2SMarkos Chandras rv.s = ieee754sp_fmin(fs, ft); 1852409fcaceSAleksandar Markovic goto copcsr; 18534e9561b2SMarkos Chandras } 18544e9561b2SMarkos Chandras 18554e9561b2SMarkos Chandras case fmina_op: { 18564e9561b2SMarkos Chandras union ieee754sp fs, ft; 18574e9561b2SMarkos Chandras 18584e9561b2SMarkos Chandras if (!cpu_has_mips_r6) 18594e9561b2SMarkos Chandras return SIGILL; 18604e9561b2SMarkos Chandras 1861454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(mina_s); 18624e9561b2SMarkos Chandras SPFROMREG(ft, MIPSInst_FT(ir)); 18634e9561b2SMarkos Chandras SPFROMREG(fs, MIPSInst_FS(ir)); 18644e9561b2SMarkos Chandras rv.s = ieee754sp_fmina(fs, ft); 1865409fcaceSAleksandar Markovic goto copcsr; 18664e9561b2SMarkos Chandras } 18674e9561b2SMarkos Chandras 1868a79f5f9bSMarkos Chandras case fmax_op: { 1869a79f5f9bSMarkos Chandras union ieee754sp fs, ft; 1870a79f5f9bSMarkos Chandras 1871a79f5f9bSMarkos Chandras if (!cpu_has_mips_r6) 1872a79f5f9bSMarkos Chandras return SIGILL; 1873a79f5f9bSMarkos Chandras 1874454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(max_s); 1875a79f5f9bSMarkos Chandras SPFROMREG(ft, MIPSInst_FT(ir)); 1876a79f5f9bSMarkos Chandras SPFROMREG(fs, MIPSInst_FS(ir)); 1877a79f5f9bSMarkos Chandras rv.s = ieee754sp_fmax(fs, ft); 1878409fcaceSAleksandar Markovic goto copcsr; 1879a79f5f9bSMarkos Chandras } 1880a79f5f9bSMarkos Chandras 1881a79f5f9bSMarkos Chandras case fmaxa_op: { 1882a79f5f9bSMarkos Chandras union ieee754sp fs, ft; 1883a79f5f9bSMarkos Chandras 1884a79f5f9bSMarkos Chandras if (!cpu_has_mips_r6) 1885a79f5f9bSMarkos Chandras return SIGILL; 1886a79f5f9bSMarkos Chandras 1887454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(maxa_s); 1888a79f5f9bSMarkos Chandras SPFROMREG(ft, MIPSInst_FT(ir)); 1889a79f5f9bSMarkos Chandras SPFROMREG(fs, MIPSInst_FS(ir)); 1890a79f5f9bSMarkos Chandras rv.s = ieee754sp_fmaxa(fs, ft); 1891409fcaceSAleksandar Markovic goto copcsr; 1892a79f5f9bSMarkos Chandras } 1893a79f5f9bSMarkos Chandras 18941da177e4SLinus Torvalds case fabs_op: 1895454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(abs_s); 18961da177e4SLinus Torvalds handler.u = ieee754sp_abs; 18971da177e4SLinus Torvalds goto scopuop; 18983f7cac41SRalf Baechle 18991da177e4SLinus Torvalds case fneg_op: 1900454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(neg_s); 19011da177e4SLinus Torvalds handler.u = ieee754sp_neg; 19021da177e4SLinus Torvalds goto scopuop; 19033f7cac41SRalf Baechle 19041da177e4SLinus Torvalds case fmov_op: 19051da177e4SLinus Torvalds /* an easy one */ 1906454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(mov_s); 19071da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 19081da177e4SLinus Torvalds goto copcsr; 19091da177e4SLinus Torvalds 19101da177e4SLinus Torvalds /* binary op on handler */ 19111da177e4SLinus Torvalds scopbop: 19121da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 19131da177e4SLinus Torvalds SPFROMREG(ft, MIPSInst_FT(ir)); 19141da177e4SLinus Torvalds 19151da177e4SLinus Torvalds rv.s = (*handler.b) (fs, ft); 19161da177e4SLinus Torvalds goto copcsr; 19171da177e4SLinus Torvalds scopuop: 19181da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 19191da177e4SLinus Torvalds rv.s = (*handler.u) (fs); 19201da177e4SLinus Torvalds goto copcsr; 19211da177e4SLinus Torvalds copcsr: 1922c4103526SDeng-Cheng Zhu if (ieee754_cxtest(IEEE754_INEXACT)) { 1923c4103526SDeng-Cheng Zhu MIPS_FPU_EMU_INC_STATS(ieee754_inexact); 19241da177e4SLinus Torvalds rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S; 1925c4103526SDeng-Cheng Zhu } 1926c4103526SDeng-Cheng Zhu if (ieee754_cxtest(IEEE754_UNDERFLOW)) { 1927c4103526SDeng-Cheng Zhu MIPS_FPU_EMU_INC_STATS(ieee754_underflow); 19281da177e4SLinus Torvalds rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S; 1929c4103526SDeng-Cheng Zhu } 1930c4103526SDeng-Cheng Zhu if (ieee754_cxtest(IEEE754_OVERFLOW)) { 1931c4103526SDeng-Cheng Zhu MIPS_FPU_EMU_INC_STATS(ieee754_overflow); 19321da177e4SLinus Torvalds rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S; 1933c4103526SDeng-Cheng Zhu } 1934c4103526SDeng-Cheng Zhu if (ieee754_cxtest(IEEE754_ZERO_DIVIDE)) { 1935c4103526SDeng-Cheng Zhu MIPS_FPU_EMU_INC_STATS(ieee754_zerodiv); 19361da177e4SLinus Torvalds rcsr |= FPU_CSR_DIV_X | FPU_CSR_DIV_S; 1937c4103526SDeng-Cheng Zhu } 1938c4103526SDeng-Cheng Zhu if (ieee754_cxtest(IEEE754_INVALID_OPERATION)) { 1939c4103526SDeng-Cheng Zhu MIPS_FPU_EMU_INC_STATS(ieee754_invalidop); 19401da177e4SLinus Torvalds rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S; 1941c4103526SDeng-Cheng Zhu } 19421da177e4SLinus Torvalds break; 19431da177e4SLinus Torvalds 19441da177e4SLinus Torvalds /* unary conv ops */ 19451da177e4SLinus Torvalds case fcvts_op: 19461da177e4SLinus Torvalds return SIGILL; /* not defined */ 19471da177e4SLinus Torvalds 19483f7cac41SRalf Baechle case fcvtd_op: 1949454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_d_s); 19501da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 19511da177e4SLinus Torvalds rv.d = ieee754dp_fsp(fs); 19521da177e4SLinus Torvalds rfmt = d_fmt; 19531da177e4SLinus Torvalds goto copcsr; 19541da177e4SLinus Torvalds 19553f7cac41SRalf Baechle case fcvtw_op: 1956454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_w_s); 19571da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 19581da177e4SLinus Torvalds rv.w = ieee754sp_tint(fs); 19591da177e4SLinus Torvalds rfmt = w_fmt; 19601da177e4SLinus Torvalds goto copcsr; 19611da177e4SLinus Torvalds 19621da177e4SLinus Torvalds case fround_op: 19631da177e4SLinus Torvalds case ftrunc_op: 19641da177e4SLinus Torvalds case fceil_op: 19653f7cac41SRalf Baechle case ffloor_op: 19662d83fea7SMaciej W. Rozycki if (!cpu_has_mips_2_3_4_5_r) 196708a07904SRalf Baechle return SIGILL; 196808a07904SRalf Baechle 1969454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == fceil_op) 1970454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(ceil_w_s); 1971454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == ffloor_op) 1972454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(floor_w_s); 1973454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == fround_op) 1974454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(round_w_s); 1975454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == ftrunc_op) 1976454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(trunc_w_s); 1977454854acSAleksandar Markovic 19783f7cac41SRalf Baechle oldrm = ieee754_csr.rm; 19791da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 19802cfcf8a8SMaciej W. Rozycki ieee754_csr.rm = MIPSInst_FUNC(ir); 19811da177e4SLinus Torvalds rv.w = ieee754sp_tint(fs); 19821da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 19831da177e4SLinus Torvalds rfmt = w_fmt; 19841da177e4SLinus Torvalds goto copcsr; 19851da177e4SLinus Torvalds 19864b820d95SPaul Burton case fsel_op: 19874b820d95SPaul Burton if (!cpu_has_mips_r6) 19884b820d95SPaul Burton return SIGILL; 19894b820d95SPaul Burton 1990454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(sel_s); 19914b820d95SPaul Burton SPFROMREG(fd, MIPSInst_FD(ir)); 19924b820d95SPaul Burton if (fd.bits & 0x1) 19934b820d95SPaul Burton SPFROMREG(rv.s, MIPSInst_FT(ir)); 19944b820d95SPaul Burton else 19954b820d95SPaul Burton SPFROMREG(rv.s, MIPSInst_FS(ir)); 19964b820d95SPaul Burton break; 19974b820d95SPaul Burton 19983f7cac41SRalf Baechle case fcvtl_op: 19992d83fea7SMaciej W. Rozycki if (!cpu_has_mips_3_4_5_64_r2_r6) 200008a07904SRalf Baechle return SIGILL; 200108a07904SRalf Baechle 2002454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_l_s); 20031da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 20041da177e4SLinus Torvalds rv.l = ieee754sp_tlong(fs); 20051da177e4SLinus Torvalds rfmt = l_fmt; 20061da177e4SLinus Torvalds goto copcsr; 20071da177e4SLinus Torvalds 20081da177e4SLinus Torvalds case froundl_op: 20091da177e4SLinus Torvalds case ftruncl_op: 20101da177e4SLinus Torvalds case fceill_op: 20113f7cac41SRalf Baechle case ffloorl_op: 20122d83fea7SMaciej W. Rozycki if (!cpu_has_mips_3_4_5_64_r2_r6) 201308a07904SRalf Baechle return SIGILL; 201408a07904SRalf Baechle 2015454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == fceill_op) 2016454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(ceil_l_s); 2017454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == ffloorl_op) 2018454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(floor_l_s); 2019454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == froundl_op) 2020454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(round_l_s); 2021454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == ftruncl_op) 2022454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(trunc_l_s); 2023454854acSAleksandar Markovic 20243f7cac41SRalf Baechle oldrm = ieee754_csr.rm; 20251da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 20262cfcf8a8SMaciej W. Rozycki ieee754_csr.rm = MIPSInst_FUNC(ir); 20271da177e4SLinus Torvalds rv.l = ieee754sp_tlong(fs); 20281da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 20291da177e4SLinus Torvalds rfmt = l_fmt; 20301da177e4SLinus Torvalds goto copcsr; 20311da177e4SLinus Torvalds 20321da177e4SLinus Torvalds default: 2033f8c3c671SMarkos Chandras if (!NO_R6EMU && MIPSInst_FUNC(ir) >= fcmp_op) { 2034a58f85b5SAleksandar Markovic unsigned int cmpop; 20352209bcb1SRalf Baechle union ieee754sp fs, ft; 20361da177e4SLinus Torvalds 2037a58f85b5SAleksandar Markovic cmpop = MIPSInst_FUNC(ir) - fcmp_op; 20381da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 20391da177e4SLinus Torvalds SPFROMREG(ft, MIPSInst_FT(ir)); 20401da177e4SLinus Torvalds rv.w = ieee754sp_cmp(fs, ft, 20411da177e4SLinus Torvalds cmptab[cmpop & 0x7], cmpop & 0x8); 20421da177e4SLinus Torvalds rfmt = -1; 20431da177e4SLinus Torvalds if ((cmpop & 0x8) && ieee754_cxtest 20441da177e4SLinus Torvalds (IEEE754_INVALID_OPERATION)) 20451da177e4SLinus Torvalds rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 20461da177e4SLinus Torvalds else 20471da177e4SLinus Torvalds goto copcsr; 20481da177e4SLinus Torvalds 20493f7cac41SRalf Baechle } else 20501da177e4SLinus Torvalds return SIGILL; 20511da177e4SLinus Torvalds break; 20521da177e4SLinus Torvalds } 20531da177e4SLinus Torvalds break; 20541da177e4SLinus Torvalds } 20551da177e4SLinus Torvalds 20561da177e4SLinus Torvalds case d_fmt: { 20574b820d95SPaul Burton union ieee754dp fd, fs, ft; 20581da177e4SLinus Torvalds union { 20592209bcb1SRalf Baechle union ieee754dp(*b) (union ieee754dp, union ieee754dp); 20602209bcb1SRalf Baechle union ieee754dp(*u) (union ieee754dp); 20611da177e4SLinus Torvalds } handler; 20621da177e4SLinus Torvalds 20631da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 20641da177e4SLinus Torvalds /* binary ops */ 20651da177e4SLinus Torvalds case fadd_op: 2066454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(add_d); 20671da177e4SLinus Torvalds handler.b = ieee754dp_add; 20681da177e4SLinus Torvalds goto dcopbop; 20691da177e4SLinus Torvalds case fsub_op: 2070454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(sub_d); 20711da177e4SLinus Torvalds handler.b = ieee754dp_sub; 20721da177e4SLinus Torvalds goto dcopbop; 20731da177e4SLinus Torvalds case fmul_op: 2074454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(mul_d); 20751da177e4SLinus Torvalds handler.b = ieee754dp_mul; 20761da177e4SLinus Torvalds goto dcopbop; 20771da177e4SLinus Torvalds case fdiv_op: 2078454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(div_d); 20791da177e4SLinus Torvalds handler.b = ieee754dp_div; 20801da177e4SLinus Torvalds goto dcopbop; 20811da177e4SLinus Torvalds 20821da177e4SLinus Torvalds /* unary ops */ 20831da177e4SLinus Torvalds case fsqrt_op: 208408a07904SRalf Baechle if (!cpu_has_mips_2_3_4_5_r) 208508a07904SRalf Baechle return SIGILL; 208608a07904SRalf Baechle 2087454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(sqrt_d); 20881da177e4SLinus Torvalds handler.u = ieee754dp_sqrt; 20891da177e4SLinus Torvalds goto dcopuop; 209008a07904SRalf Baechle /* 209108a07904SRalf Baechle * Note that on some MIPS IV implementations such as the 209208a07904SRalf Baechle * R5000 and R8000 the FSQRT and FRECIP instructions do not 209308a07904SRalf Baechle * achieve full IEEE-754 accuracy - however this emulator does. 209408a07904SRalf Baechle */ 20951da177e4SLinus Torvalds case frsqrt_op: 20962d83fea7SMaciej W. Rozycki if (!cpu_has_mips_4_5_64_r2_r6) 209708a07904SRalf Baechle return SIGILL; 209808a07904SRalf Baechle 2099454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(rsqrt_d); 21001da177e4SLinus Torvalds handler.u = fpemu_dp_rsqrt; 21011da177e4SLinus Torvalds goto dcopuop; 21021da177e4SLinus Torvalds case frecip_op: 21032d83fea7SMaciej W. Rozycki if (!cpu_has_mips_4_5_64_r2_r6) 210408a07904SRalf Baechle return SIGILL; 210508a07904SRalf Baechle 2106454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(recip_d); 21071da177e4SLinus Torvalds handler.u = fpemu_dp_recip; 21081da177e4SLinus Torvalds goto dcopuop; 21091da177e4SLinus Torvalds case fmovc_op: 211008a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 211108a07904SRalf Baechle return SIGILL; 211208a07904SRalf Baechle 21131da177e4SLinus Torvalds cond = fpucondbit[MIPSInst_FT(ir) >> 2]; 21141da177e4SLinus Torvalds if (((ctx->fcr31 & cond) != 0) != 21151da177e4SLinus Torvalds ((MIPSInst_FT(ir) & 1) != 0)) 21161da177e4SLinus Torvalds return 0; 21171da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 21181da177e4SLinus Torvalds break; 21191da177e4SLinus Torvalds case fmovz_op: 212008a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 212108a07904SRalf Baechle return SIGILL; 212208a07904SRalf Baechle 21231da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] != 0) 21241da177e4SLinus Torvalds return 0; 21251da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 21261da177e4SLinus Torvalds break; 21271da177e4SLinus Torvalds case fmovn_op: 212808a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 212908a07904SRalf Baechle return SIGILL; 213008a07904SRalf Baechle 21311da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] == 0) 21321da177e4SLinus Torvalds return 0; 21331da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 21341da177e4SLinus Torvalds break; 213567613f02SMarkos Chandras 213667613f02SMarkos Chandras case fseleqz_op: 213767613f02SMarkos Chandras if (!cpu_has_mips_r6) 213867613f02SMarkos Chandras return SIGILL; 213967613f02SMarkos Chandras 2140454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(seleqz_d); 214167613f02SMarkos Chandras DPFROMREG(rv.d, MIPSInst_FT(ir)); 214267613f02SMarkos Chandras if (rv.l & 0x1) 214367613f02SMarkos Chandras rv.l = 0; 214467613f02SMarkos Chandras else 214567613f02SMarkos Chandras DPFROMREG(rv.d, MIPSInst_FS(ir)); 214667613f02SMarkos Chandras break; 214767613f02SMarkos Chandras 2148130fe357SMarkos Chandras case fselnez_op: 2149130fe357SMarkos Chandras if (!cpu_has_mips_r6) 2150130fe357SMarkos Chandras return SIGILL; 2151130fe357SMarkos Chandras 2152454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(selnez_d); 2153130fe357SMarkos Chandras DPFROMREG(rv.d, MIPSInst_FT(ir)); 2154130fe357SMarkos Chandras if (rv.l & 0x1) 2155130fe357SMarkos Chandras DPFROMREG(rv.d, MIPSInst_FS(ir)); 2156130fe357SMarkos Chandras else 2157130fe357SMarkos Chandras rv.l = 0; 2158130fe357SMarkos Chandras break; 2159130fe357SMarkos Chandras 2160e24c3becSMarkos Chandras case fmaddf_op: { 2161e24c3becSMarkos Chandras union ieee754dp ft, fs, fd; 2162e24c3becSMarkos Chandras 2163e24c3becSMarkos Chandras if (!cpu_has_mips_r6) 2164e24c3becSMarkos Chandras return SIGILL; 2165e24c3becSMarkos Chandras 2166454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(maddf_d); 2167e24c3becSMarkos Chandras DPFROMREG(ft, MIPSInst_FT(ir)); 2168e24c3becSMarkos Chandras DPFROMREG(fs, MIPSInst_FS(ir)); 2169e24c3becSMarkos Chandras DPFROMREG(fd, MIPSInst_FD(ir)); 2170e24c3becSMarkos Chandras rv.d = ieee754dp_maddf(fd, fs, ft); 2171409fcaceSAleksandar Markovic goto copcsr; 2172e24c3becSMarkos Chandras } 2173e24c3becSMarkos Chandras 217483d43305SMarkos Chandras case fmsubf_op: { 217583d43305SMarkos Chandras union ieee754dp ft, fs, fd; 217683d43305SMarkos Chandras 217783d43305SMarkos Chandras if (!cpu_has_mips_r6) 217883d43305SMarkos Chandras return SIGILL; 217983d43305SMarkos Chandras 2180454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(msubf_d); 218183d43305SMarkos Chandras DPFROMREG(ft, MIPSInst_FT(ir)); 218283d43305SMarkos Chandras DPFROMREG(fs, MIPSInst_FS(ir)); 218383d43305SMarkos Chandras DPFROMREG(fd, MIPSInst_FD(ir)); 218483d43305SMarkos Chandras rv.d = ieee754dp_msubf(fd, fs, ft); 2185409fcaceSAleksandar Markovic goto copcsr; 218683d43305SMarkos Chandras } 218783d43305SMarkos Chandras 2188400bd2e4SMarkos Chandras case frint_op: { 2189400bd2e4SMarkos Chandras union ieee754dp fs; 2190400bd2e4SMarkos Chandras 2191400bd2e4SMarkos Chandras if (!cpu_has_mips_r6) 2192400bd2e4SMarkos Chandras return SIGILL; 2193400bd2e4SMarkos Chandras 2194454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(rint_d); 2195400bd2e4SMarkos Chandras DPFROMREG(fs, MIPSInst_FS(ir)); 21963ec404d8SAleksandar Markovic rv.d = ieee754dp_rint(fs); 2197400bd2e4SMarkos Chandras goto copcsr; 2198400bd2e4SMarkos Chandras } 2199400bd2e4SMarkos Chandras 220038db37baSMarkos Chandras case fclass_op: { 220138db37baSMarkos Chandras union ieee754dp fs; 220238db37baSMarkos Chandras 220338db37baSMarkos Chandras if (!cpu_has_mips_r6) 220438db37baSMarkos Chandras return SIGILL; 220538db37baSMarkos Chandras 2206454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(class_d); 220738db37baSMarkos Chandras DPFROMREG(fs, MIPSInst_FS(ir)); 2208e1231dd6SAleksandar Markovic rv.l = ieee754dp_2008class(fs); 2209e1231dd6SAleksandar Markovic rfmt = l_fmt; 2210409fcaceSAleksandar Markovic goto copcsr; 221138db37baSMarkos Chandras } 221238db37baSMarkos Chandras 22134e9561b2SMarkos Chandras case fmin_op: { 22144e9561b2SMarkos Chandras union ieee754dp fs, ft; 22154e9561b2SMarkos Chandras 22164e9561b2SMarkos Chandras if (!cpu_has_mips_r6) 22174e9561b2SMarkos Chandras return SIGILL; 22184e9561b2SMarkos Chandras 2219454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(min_d); 22204e9561b2SMarkos Chandras DPFROMREG(ft, MIPSInst_FT(ir)); 22214e9561b2SMarkos Chandras DPFROMREG(fs, MIPSInst_FS(ir)); 22224e9561b2SMarkos Chandras rv.d = ieee754dp_fmin(fs, ft); 2223409fcaceSAleksandar Markovic goto copcsr; 22244e9561b2SMarkos Chandras } 22254e9561b2SMarkos Chandras 22264e9561b2SMarkos Chandras case fmina_op: { 22274e9561b2SMarkos Chandras union ieee754dp fs, ft; 22284e9561b2SMarkos Chandras 22294e9561b2SMarkos Chandras if (!cpu_has_mips_r6) 22304e9561b2SMarkos Chandras return SIGILL; 22314e9561b2SMarkos Chandras 2232454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(mina_d); 22334e9561b2SMarkos Chandras DPFROMREG(ft, MIPSInst_FT(ir)); 22344e9561b2SMarkos Chandras DPFROMREG(fs, MIPSInst_FS(ir)); 22354e9561b2SMarkos Chandras rv.d = ieee754dp_fmina(fs, ft); 2236409fcaceSAleksandar Markovic goto copcsr; 22374e9561b2SMarkos Chandras } 22384e9561b2SMarkos Chandras 2239a79f5f9bSMarkos Chandras case fmax_op: { 2240a79f5f9bSMarkos Chandras union ieee754dp fs, ft; 2241a79f5f9bSMarkos Chandras 2242a79f5f9bSMarkos Chandras if (!cpu_has_mips_r6) 2243a79f5f9bSMarkos Chandras return SIGILL; 2244a79f5f9bSMarkos Chandras 2245454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(max_d); 2246a79f5f9bSMarkos Chandras DPFROMREG(ft, MIPSInst_FT(ir)); 2247a79f5f9bSMarkos Chandras DPFROMREG(fs, MIPSInst_FS(ir)); 2248a79f5f9bSMarkos Chandras rv.d = ieee754dp_fmax(fs, ft); 2249409fcaceSAleksandar Markovic goto copcsr; 2250a79f5f9bSMarkos Chandras } 2251a79f5f9bSMarkos Chandras 2252a79f5f9bSMarkos Chandras case fmaxa_op: { 2253a79f5f9bSMarkos Chandras union ieee754dp fs, ft; 2254a79f5f9bSMarkos Chandras 2255a79f5f9bSMarkos Chandras if (!cpu_has_mips_r6) 2256a79f5f9bSMarkos Chandras return SIGILL; 2257a79f5f9bSMarkos Chandras 2258454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(maxa_d); 2259a79f5f9bSMarkos Chandras DPFROMREG(ft, MIPSInst_FT(ir)); 2260a79f5f9bSMarkos Chandras DPFROMREG(fs, MIPSInst_FS(ir)); 2261a79f5f9bSMarkos Chandras rv.d = ieee754dp_fmaxa(fs, ft); 2262409fcaceSAleksandar Markovic goto copcsr; 2263a79f5f9bSMarkos Chandras } 2264a79f5f9bSMarkos Chandras 22651da177e4SLinus Torvalds case fabs_op: 2266454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(abs_d); 22671da177e4SLinus Torvalds handler.u = ieee754dp_abs; 22681da177e4SLinus Torvalds goto dcopuop; 22691da177e4SLinus Torvalds 22701da177e4SLinus Torvalds case fneg_op: 2271454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(neg_d); 22721da177e4SLinus Torvalds handler.u = ieee754dp_neg; 22731da177e4SLinus Torvalds goto dcopuop; 22741da177e4SLinus Torvalds 22751da177e4SLinus Torvalds case fmov_op: 22761da177e4SLinus Torvalds /* an easy one */ 2277454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(mov_d); 22781da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 22791da177e4SLinus Torvalds goto copcsr; 22801da177e4SLinus Torvalds 22811da177e4SLinus Torvalds /* binary op on handler */ 22823f7cac41SRalf Baechle dcopbop: 22831da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 22841da177e4SLinus Torvalds DPFROMREG(ft, MIPSInst_FT(ir)); 22851da177e4SLinus Torvalds 22861da177e4SLinus Torvalds rv.d = (*handler.b) (fs, ft); 22871da177e4SLinus Torvalds goto copcsr; 22883f7cac41SRalf Baechle dcopuop: 22891da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 22901da177e4SLinus Torvalds rv.d = (*handler.u) (fs); 22911da177e4SLinus Torvalds goto copcsr; 22921da177e4SLinus Torvalds 22933f7cac41SRalf Baechle /* 22943f7cac41SRalf Baechle * unary conv ops 22953f7cac41SRalf Baechle */ 22963f7cac41SRalf Baechle case fcvts_op: 2297454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_s_d); 22981da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 22991da177e4SLinus Torvalds rv.s = ieee754sp_fdp(fs); 23001da177e4SLinus Torvalds rfmt = s_fmt; 23011da177e4SLinus Torvalds goto copcsr; 23023f7cac41SRalf Baechle 23031da177e4SLinus Torvalds case fcvtd_op: 23041da177e4SLinus Torvalds return SIGILL; /* not defined */ 23051da177e4SLinus Torvalds 23063f7cac41SRalf Baechle case fcvtw_op: 2307454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_w_d); 23081da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 23091da177e4SLinus Torvalds rv.w = ieee754dp_tint(fs); /* wrong */ 23101da177e4SLinus Torvalds rfmt = w_fmt; 23111da177e4SLinus Torvalds goto copcsr; 23121da177e4SLinus Torvalds 23131da177e4SLinus Torvalds case fround_op: 23141da177e4SLinus Torvalds case ftrunc_op: 23151da177e4SLinus Torvalds case fceil_op: 23163f7cac41SRalf Baechle case ffloor_op: 231708a07904SRalf Baechle if (!cpu_has_mips_2_3_4_5_r) 231808a07904SRalf Baechle return SIGILL; 231908a07904SRalf Baechle 2320454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == fceil_op) 2321454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(ceil_w_d); 2322454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == ffloor_op) 2323454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(floor_w_d); 2324454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == fround_op) 2325454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(round_w_d); 2326454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == ftrunc_op) 2327454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(trunc_w_d); 2328454854acSAleksandar Markovic 23293f7cac41SRalf Baechle oldrm = ieee754_csr.rm; 23301da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 23312cfcf8a8SMaciej W. Rozycki ieee754_csr.rm = MIPSInst_FUNC(ir); 23321da177e4SLinus Torvalds rv.w = ieee754dp_tint(fs); 23331da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 23341da177e4SLinus Torvalds rfmt = w_fmt; 23351da177e4SLinus Torvalds goto copcsr; 23361da177e4SLinus Torvalds 23374b820d95SPaul Burton case fsel_op: 23384b820d95SPaul Burton if (!cpu_has_mips_r6) 23394b820d95SPaul Burton return SIGILL; 23404b820d95SPaul Burton 2341454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(sel_d); 23424b820d95SPaul Burton DPFROMREG(fd, MIPSInst_FD(ir)); 23434b820d95SPaul Burton if (fd.bits & 0x1) 23444b820d95SPaul Burton DPFROMREG(rv.d, MIPSInst_FT(ir)); 23454b820d95SPaul Burton else 23464b820d95SPaul Burton DPFROMREG(rv.d, MIPSInst_FS(ir)); 23474b820d95SPaul Burton break; 23484b820d95SPaul Burton 23493f7cac41SRalf Baechle case fcvtl_op: 23502d83fea7SMaciej W. Rozycki if (!cpu_has_mips_3_4_5_64_r2_r6) 235108a07904SRalf Baechle return SIGILL; 235208a07904SRalf Baechle 2353454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_l_d); 23541da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 23551da177e4SLinus Torvalds rv.l = ieee754dp_tlong(fs); 23561da177e4SLinus Torvalds rfmt = l_fmt; 23571da177e4SLinus Torvalds goto copcsr; 23581da177e4SLinus Torvalds 23591da177e4SLinus Torvalds case froundl_op: 23601da177e4SLinus Torvalds case ftruncl_op: 23611da177e4SLinus Torvalds case fceill_op: 23623f7cac41SRalf Baechle case ffloorl_op: 23632d83fea7SMaciej W. Rozycki if (!cpu_has_mips_3_4_5_64_r2_r6) 236408a07904SRalf Baechle return SIGILL; 236508a07904SRalf Baechle 2366454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == fceill_op) 2367454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(ceil_l_d); 2368454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == ffloorl_op) 2369454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(floor_l_d); 2370454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == froundl_op) 2371454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(round_l_d); 2372454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == ftruncl_op) 2373454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(trunc_l_d); 2374454854acSAleksandar Markovic 23753f7cac41SRalf Baechle oldrm = ieee754_csr.rm; 23761da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 23772cfcf8a8SMaciej W. Rozycki ieee754_csr.rm = MIPSInst_FUNC(ir); 23781da177e4SLinus Torvalds rv.l = ieee754dp_tlong(fs); 23791da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 23801da177e4SLinus Torvalds rfmt = l_fmt; 23811da177e4SLinus Torvalds goto copcsr; 23821da177e4SLinus Torvalds 23831da177e4SLinus Torvalds default: 2384f8c3c671SMarkos Chandras if (!NO_R6EMU && MIPSInst_FUNC(ir) >= fcmp_op) { 2385a58f85b5SAleksandar Markovic unsigned int cmpop; 23862209bcb1SRalf Baechle union ieee754dp fs, ft; 23871da177e4SLinus Torvalds 2388a58f85b5SAleksandar Markovic cmpop = MIPSInst_FUNC(ir) - fcmp_op; 23891da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 23901da177e4SLinus Torvalds DPFROMREG(ft, MIPSInst_FT(ir)); 23911da177e4SLinus Torvalds rv.w = ieee754dp_cmp(fs, ft, 23921da177e4SLinus Torvalds cmptab[cmpop & 0x7], cmpop & 0x8); 23931da177e4SLinus Torvalds rfmt = -1; 23941da177e4SLinus Torvalds if ((cmpop & 0x8) 23951da177e4SLinus Torvalds && 23961da177e4SLinus Torvalds ieee754_cxtest 23971da177e4SLinus Torvalds (IEEE754_INVALID_OPERATION)) 23981da177e4SLinus Torvalds rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 23991da177e4SLinus Torvalds else 24001da177e4SLinus Torvalds goto copcsr; 24011da177e4SLinus Torvalds 24021da177e4SLinus Torvalds } 24031da177e4SLinus Torvalds else { 24041da177e4SLinus Torvalds return SIGILL; 24051da177e4SLinus Torvalds } 24061da177e4SLinus Torvalds break; 24071da177e4SLinus Torvalds } 24081da177e4SLinus Torvalds break; 2409bbdd8147SMarkos Chandras } 24101da177e4SLinus Torvalds 2411bbdd8147SMarkos Chandras case w_fmt: { 2412bbdd8147SMarkos Chandras union ieee754dp fs; 2413bbdd8147SMarkos Chandras 24141da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 24151da177e4SLinus Torvalds case fcvts_op: 24161da177e4SLinus Torvalds /* convert word to single precision real */ 2417454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_s_w); 24181da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 24191da177e4SLinus Torvalds rv.s = ieee754sp_fint(fs.bits); 24201da177e4SLinus Torvalds rfmt = s_fmt; 24211da177e4SLinus Torvalds goto copcsr; 24221da177e4SLinus Torvalds case fcvtd_op: 24231da177e4SLinus Torvalds /* convert word to double precision real */ 2424454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_d_w); 24251da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 24261da177e4SLinus Torvalds rv.d = ieee754dp_fint(fs.bits); 24271da177e4SLinus Torvalds rfmt = d_fmt; 24281da177e4SLinus Torvalds goto copcsr; 2429f8c3c671SMarkos Chandras default: { 2430f8c3c671SMarkos Chandras /* Emulating the new CMP.condn.fmt R6 instruction */ 2431f8c3c671SMarkos Chandras #define CMPOP_MASK 0x7 2432f8c3c671SMarkos Chandras #define SIGN_BIT (0x1 << 3) 2433f8c3c671SMarkos Chandras #define PREDICATE_BIT (0x1 << 4) 2434f8c3c671SMarkos Chandras 2435f8c3c671SMarkos Chandras int cmpop = MIPSInst_FUNC(ir) & CMPOP_MASK; 2436f8c3c671SMarkos Chandras int sig = MIPSInst_FUNC(ir) & SIGN_BIT; 2437f8c3c671SMarkos Chandras union ieee754sp fs, ft; 2438f8c3c671SMarkos Chandras 2439f8c3c671SMarkos Chandras /* This is an R6 only instruction */ 2440f8c3c671SMarkos Chandras if (!cpu_has_mips_r6 || 2441f8c3c671SMarkos Chandras (MIPSInst_FUNC(ir) & 0x20)) 2442f8c3c671SMarkos Chandras return SIGILL; 2443f8c3c671SMarkos Chandras 2444454854acSAleksandar Markovic if (!sig) { 2445454854acSAleksandar Markovic if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { 2446454854acSAleksandar Markovic switch (cmpop) { 2447454854acSAleksandar Markovic case 0: 2448454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_af_s); 2449454854acSAleksandar Markovic break; 2450454854acSAleksandar Markovic case 1: 2451454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_un_s); 2452454854acSAleksandar Markovic break; 2453454854acSAleksandar Markovic case 2: 2454454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_eq_s); 2455454854acSAleksandar Markovic break; 2456454854acSAleksandar Markovic case 3: 2457454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_ueq_s); 2458454854acSAleksandar Markovic break; 2459454854acSAleksandar Markovic case 4: 2460454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_lt_s); 2461454854acSAleksandar Markovic break; 2462454854acSAleksandar Markovic case 5: 2463454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_ult_s); 2464454854acSAleksandar Markovic break; 2465454854acSAleksandar Markovic case 6: 2466454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_le_s); 2467454854acSAleksandar Markovic break; 2468454854acSAleksandar Markovic case 7: 2469454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_ule_s); 2470454854acSAleksandar Markovic break; 2471454854acSAleksandar Markovic } 2472454854acSAleksandar Markovic } else { 2473454854acSAleksandar Markovic switch (cmpop) { 2474454854acSAleksandar Markovic case 1: 2475454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_or_s); 2476454854acSAleksandar Markovic break; 2477454854acSAleksandar Markovic case 2: 2478454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_une_s); 2479454854acSAleksandar Markovic break; 2480454854acSAleksandar Markovic case 3: 2481454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_ne_s); 2482454854acSAleksandar Markovic break; 2483454854acSAleksandar Markovic } 2484454854acSAleksandar Markovic } 2485454854acSAleksandar Markovic } else { 2486454854acSAleksandar Markovic if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { 2487454854acSAleksandar Markovic switch (cmpop) { 2488454854acSAleksandar Markovic case 0: 2489454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_saf_s); 2490454854acSAleksandar Markovic break; 2491454854acSAleksandar Markovic case 1: 2492454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sun_s); 2493454854acSAleksandar Markovic break; 2494454854acSAleksandar Markovic case 2: 2495454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_seq_s); 2496454854acSAleksandar Markovic break; 2497454854acSAleksandar Markovic case 3: 2498454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sueq_s); 2499454854acSAleksandar Markovic break; 2500454854acSAleksandar Markovic case 4: 2501454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_slt_s); 2502454854acSAleksandar Markovic break; 2503454854acSAleksandar Markovic case 5: 2504454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sult_s); 2505454854acSAleksandar Markovic break; 2506454854acSAleksandar Markovic case 6: 2507454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sle_s); 2508454854acSAleksandar Markovic break; 2509454854acSAleksandar Markovic case 7: 2510454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sule_s); 2511454854acSAleksandar Markovic break; 2512454854acSAleksandar Markovic } 2513454854acSAleksandar Markovic } else { 2514454854acSAleksandar Markovic switch (cmpop) { 2515454854acSAleksandar Markovic case 1: 2516454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sor_s); 2517454854acSAleksandar Markovic break; 2518454854acSAleksandar Markovic case 2: 2519454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sune_s); 2520454854acSAleksandar Markovic break; 2521454854acSAleksandar Markovic case 3: 2522454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sne_s); 2523454854acSAleksandar Markovic break; 2524454854acSAleksandar Markovic } 2525454854acSAleksandar Markovic } 2526454854acSAleksandar Markovic } 2527454854acSAleksandar Markovic 2528f8c3c671SMarkos Chandras /* fmt is w_fmt for single precision so fix it */ 2529f8c3c671SMarkos Chandras rfmt = s_fmt; 2530f8c3c671SMarkos Chandras /* default to false */ 2531f8c3c671SMarkos Chandras rv.w = 0; 2532f8c3c671SMarkos Chandras 2533f8c3c671SMarkos Chandras /* CMP.condn.S */ 2534f8c3c671SMarkos Chandras SPFROMREG(fs, MIPSInst_FS(ir)); 2535f8c3c671SMarkos Chandras SPFROMREG(ft, MIPSInst_FT(ir)); 2536f8c3c671SMarkos Chandras 2537f8c3c671SMarkos Chandras /* positive predicates */ 2538f8c3c671SMarkos Chandras if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { 2539f8c3c671SMarkos Chandras if (ieee754sp_cmp(fs, ft, cmptab[cmpop], 2540f8c3c671SMarkos Chandras sig)) 2541f8c3c671SMarkos Chandras rv.w = -1; /* true, all 1s */ 2542f8c3c671SMarkos Chandras if ((sig) && 2543f8c3c671SMarkos Chandras ieee754_cxtest(IEEE754_INVALID_OPERATION)) 2544f8c3c671SMarkos Chandras rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 2545f8c3c671SMarkos Chandras else 2546f8c3c671SMarkos Chandras goto copcsr; 2547f8c3c671SMarkos Chandras } else { 2548f8c3c671SMarkos Chandras /* negative predicates */ 2549f8c3c671SMarkos Chandras switch (cmpop) { 2550f8c3c671SMarkos Chandras case 1: 2551f8c3c671SMarkos Chandras case 2: 2552f8c3c671SMarkos Chandras case 3: 2553f8c3c671SMarkos Chandras if (ieee754sp_cmp(fs, ft, 2554f8c3c671SMarkos Chandras negative_cmptab[cmpop], 2555f8c3c671SMarkos Chandras sig)) 2556f8c3c671SMarkos Chandras rv.w = -1; /* true, all 1s */ 2557f8c3c671SMarkos Chandras if (sig && 2558f8c3c671SMarkos Chandras ieee754_cxtest(IEEE754_INVALID_OPERATION)) 2559f8c3c671SMarkos Chandras rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 2560f8c3c671SMarkos Chandras else 2561f8c3c671SMarkos Chandras goto copcsr; 2562f8c3c671SMarkos Chandras break; 25631da177e4SLinus Torvalds default: 2564f8c3c671SMarkos Chandras /* Reserved R6 ops */ 25651da177e4SLinus Torvalds return SIGILL; 25661da177e4SLinus Torvalds } 2567f8c3c671SMarkos Chandras } 25681da177e4SLinus Torvalds break; 25691da177e4SLinus Torvalds } 2570f8c3c671SMarkos Chandras } 25711ff8560aSAleksandar Markovic break; 2572f8c3c671SMarkos Chandras } 25731da177e4SLinus Torvalds 25743f7cac41SRalf Baechle case l_fmt: 257508a07904SRalf Baechle 25762d83fea7SMaciej W. Rozycki if (!cpu_has_mips_3_4_5_64_r2_r6) 257708a07904SRalf Baechle return SIGILL; 257808a07904SRalf Baechle 2579bbd426f5SPaul Burton DIFROMREG(bits, MIPSInst_FS(ir)); 2580bbd426f5SPaul Burton 25811da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 25821da177e4SLinus Torvalds case fcvts_op: 25831da177e4SLinus Torvalds /* convert long to single precision real */ 2584454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_s_l); 2585bbd426f5SPaul Burton rv.s = ieee754sp_flong(bits); 25861da177e4SLinus Torvalds rfmt = s_fmt; 25871da177e4SLinus Torvalds goto copcsr; 25881da177e4SLinus Torvalds case fcvtd_op: 25891da177e4SLinus Torvalds /* convert long to double precision real */ 2590454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_d_l); 2591bbd426f5SPaul Burton rv.d = ieee754dp_flong(bits); 25921da177e4SLinus Torvalds rfmt = d_fmt; 25931da177e4SLinus Torvalds goto copcsr; 2594f8c3c671SMarkos Chandras default: { 2595f8c3c671SMarkos Chandras /* Emulating the new CMP.condn.fmt R6 instruction */ 2596f8c3c671SMarkos Chandras int cmpop = MIPSInst_FUNC(ir) & CMPOP_MASK; 2597f8c3c671SMarkos Chandras int sig = MIPSInst_FUNC(ir) & SIGN_BIT; 2598f8c3c671SMarkos Chandras union ieee754dp fs, ft; 2599f8c3c671SMarkos Chandras 2600f8c3c671SMarkos Chandras if (!cpu_has_mips_r6 || 2601f8c3c671SMarkos Chandras (MIPSInst_FUNC(ir) & 0x20)) 2602f8c3c671SMarkos Chandras return SIGILL; 2603f8c3c671SMarkos Chandras 2604454854acSAleksandar Markovic if (!sig) { 2605454854acSAleksandar Markovic if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { 2606454854acSAleksandar Markovic switch (cmpop) { 2607454854acSAleksandar Markovic case 0: 2608454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_af_d); 2609454854acSAleksandar Markovic break; 2610454854acSAleksandar Markovic case 1: 2611454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_un_d); 2612454854acSAleksandar Markovic break; 2613454854acSAleksandar Markovic case 2: 2614454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_eq_d); 2615454854acSAleksandar Markovic break; 2616454854acSAleksandar Markovic case 3: 2617454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_ueq_d); 2618454854acSAleksandar Markovic break; 2619454854acSAleksandar Markovic case 4: 2620454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_lt_d); 2621454854acSAleksandar Markovic break; 2622454854acSAleksandar Markovic case 5: 2623454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_ult_d); 2624454854acSAleksandar Markovic break; 2625454854acSAleksandar Markovic case 6: 2626454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_le_d); 2627454854acSAleksandar Markovic break; 2628454854acSAleksandar Markovic case 7: 2629454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_ule_d); 2630454854acSAleksandar Markovic break; 2631454854acSAleksandar Markovic } 2632454854acSAleksandar Markovic } else { 2633454854acSAleksandar Markovic switch (cmpop) { 2634454854acSAleksandar Markovic case 1: 2635454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_or_d); 2636454854acSAleksandar Markovic break; 2637454854acSAleksandar Markovic case 2: 2638454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_une_d); 2639454854acSAleksandar Markovic break; 2640454854acSAleksandar Markovic case 3: 2641454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_ne_d); 2642454854acSAleksandar Markovic break; 2643454854acSAleksandar Markovic } 2644454854acSAleksandar Markovic } 2645454854acSAleksandar Markovic } else { 2646454854acSAleksandar Markovic if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { 2647454854acSAleksandar Markovic switch (cmpop) { 2648454854acSAleksandar Markovic case 0: 2649454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_saf_d); 2650454854acSAleksandar Markovic break; 2651454854acSAleksandar Markovic case 1: 2652454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sun_d); 2653454854acSAleksandar Markovic break; 2654454854acSAleksandar Markovic case 2: 2655454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_seq_d); 2656454854acSAleksandar Markovic break; 2657454854acSAleksandar Markovic case 3: 2658454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sueq_d); 2659454854acSAleksandar Markovic break; 2660454854acSAleksandar Markovic case 4: 2661454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_slt_d); 2662454854acSAleksandar Markovic break; 2663454854acSAleksandar Markovic case 5: 2664454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sult_d); 2665454854acSAleksandar Markovic break; 2666454854acSAleksandar Markovic case 6: 2667454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sle_d); 2668454854acSAleksandar Markovic break; 2669454854acSAleksandar Markovic case 7: 2670454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sule_d); 2671454854acSAleksandar Markovic break; 2672454854acSAleksandar Markovic } 2673454854acSAleksandar Markovic } else { 2674454854acSAleksandar Markovic switch (cmpop) { 2675454854acSAleksandar Markovic case 1: 2676454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sor_d); 2677454854acSAleksandar Markovic break; 2678454854acSAleksandar Markovic case 2: 2679454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sune_d); 2680454854acSAleksandar Markovic break; 2681454854acSAleksandar Markovic case 3: 2682454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sne_d); 2683454854acSAleksandar Markovic break; 2684454854acSAleksandar Markovic } 2685454854acSAleksandar Markovic } 2686454854acSAleksandar Markovic } 2687454854acSAleksandar Markovic 2688f8c3c671SMarkos Chandras /* fmt is l_fmt for double precision so fix it */ 2689f8c3c671SMarkos Chandras rfmt = d_fmt; 2690f8c3c671SMarkos Chandras /* default to false */ 2691f8c3c671SMarkos Chandras rv.l = 0; 2692f8c3c671SMarkos Chandras 2693f8c3c671SMarkos Chandras /* CMP.condn.D */ 2694f8c3c671SMarkos Chandras DPFROMREG(fs, MIPSInst_FS(ir)); 2695f8c3c671SMarkos Chandras DPFROMREG(ft, MIPSInst_FT(ir)); 2696f8c3c671SMarkos Chandras 2697f8c3c671SMarkos Chandras /* positive predicates */ 2698f8c3c671SMarkos Chandras if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { 2699f8c3c671SMarkos Chandras if (ieee754dp_cmp(fs, ft, 2700f8c3c671SMarkos Chandras cmptab[cmpop], sig)) 2701f8c3c671SMarkos Chandras rv.l = -1LL; /* true, all 1s */ 2702f8c3c671SMarkos Chandras if (sig && 2703f8c3c671SMarkos Chandras ieee754_cxtest(IEEE754_INVALID_OPERATION)) 2704f8c3c671SMarkos Chandras rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 2705f8c3c671SMarkos Chandras else 2706f8c3c671SMarkos Chandras goto copcsr; 2707f8c3c671SMarkos Chandras } else { 2708f8c3c671SMarkos Chandras /* negative predicates */ 2709f8c3c671SMarkos Chandras switch (cmpop) { 2710f8c3c671SMarkos Chandras case 1: 2711f8c3c671SMarkos Chandras case 2: 2712f8c3c671SMarkos Chandras case 3: 2713f8c3c671SMarkos Chandras if (ieee754dp_cmp(fs, ft, 2714f8c3c671SMarkos Chandras negative_cmptab[cmpop], 2715f8c3c671SMarkos Chandras sig)) 2716f8c3c671SMarkos Chandras rv.l = -1LL; /* true, all 1s */ 2717f8c3c671SMarkos Chandras if (sig && 2718f8c3c671SMarkos Chandras ieee754_cxtest(IEEE754_INVALID_OPERATION)) 2719f8c3c671SMarkos Chandras rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 2720f8c3c671SMarkos Chandras else 2721f8c3c671SMarkos Chandras goto copcsr; 2722f8c3c671SMarkos Chandras break; 27231da177e4SLinus Torvalds default: 2724f8c3c671SMarkos Chandras /* Reserved R6 ops */ 27251da177e4SLinus Torvalds return SIGILL; 27261da177e4SLinus Torvalds } 2727f8c3c671SMarkos Chandras } 27281da177e4SLinus Torvalds break; 2729f8c3c671SMarkos Chandras } 2730f8c3c671SMarkos Chandras } 27311ff8560aSAleksandar Markovic break; 27321ff8560aSAleksandar Markovic 27331da177e4SLinus Torvalds default: 27341da177e4SLinus Torvalds return SIGILL; 27351da177e4SLinus Torvalds } 27361da177e4SLinus Torvalds 27371da177e4SLinus Torvalds /* 27381da177e4SLinus Torvalds * Update the fpu CSR register for this operation. 27391da177e4SLinus Torvalds * If an exception is required, generate a tidy SIGFPE exception, 27401da177e4SLinus Torvalds * without updating the result register. 27411da177e4SLinus Torvalds * Note: cause exception bits do not accumulate, they are rewritten 27421da177e4SLinus Torvalds * for each op; only the flag/sticky bits accumulate. 27431da177e4SLinus Torvalds */ 27441da177e4SLinus Torvalds ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr; 27451da177e4SLinus Torvalds if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 27463f7cac41SRalf Baechle /*printk ("SIGFPE: FPU csr = %08x\n",ctx->fcr31); */ 27471da177e4SLinus Torvalds return SIGFPE; 27481da177e4SLinus Torvalds } 27491da177e4SLinus Torvalds 27501da177e4SLinus Torvalds /* 27511da177e4SLinus Torvalds * Now we can safely write the result back to the register file. 27521da177e4SLinus Torvalds */ 27531da177e4SLinus Torvalds switch (rfmt) { 275408a07904SRalf Baechle case -1: 275508a07904SRalf Baechle 275608a07904SRalf Baechle if (cpu_has_mips_4_5_r) 2757c3b9b945SRob Kendrick cbit = fpucondbit[MIPSInst_FD(ir) >> 2]; 27581da177e4SLinus Torvalds else 275908a07904SRalf Baechle cbit = FPU_CSR_COND; 276008a07904SRalf Baechle if (rv.w) 276108a07904SRalf Baechle ctx->fcr31 |= cbit; 276208a07904SRalf Baechle else 276308a07904SRalf Baechle ctx->fcr31 &= ~cbit; 27641da177e4SLinus Torvalds break; 276508a07904SRalf Baechle 27661da177e4SLinus Torvalds case d_fmt: 27671da177e4SLinus Torvalds DPTOREG(rv.d, MIPSInst_FD(ir)); 27681da177e4SLinus Torvalds break; 27691da177e4SLinus Torvalds case s_fmt: 27701da177e4SLinus Torvalds SPTOREG(rv.s, MIPSInst_FD(ir)); 27711da177e4SLinus Torvalds break; 27721da177e4SLinus Torvalds case w_fmt: 27731da177e4SLinus Torvalds SITOREG(rv.w, MIPSInst_FD(ir)); 27741da177e4SLinus Torvalds break; 27751da177e4SLinus Torvalds case l_fmt: 27762d83fea7SMaciej W. Rozycki if (!cpu_has_mips_3_4_5_64_r2_r6) 277708a07904SRalf Baechle return SIGILL; 277808a07904SRalf Baechle 27791da177e4SLinus Torvalds DITOREG(rv.l, MIPSInst_FD(ir)); 27801da177e4SLinus Torvalds break; 27811da177e4SLinus Torvalds default: 27821da177e4SLinus Torvalds return SIGILL; 27831da177e4SLinus Torvalds } 27841da177e4SLinus Torvalds 27851da177e4SLinus Torvalds return 0; 27861da177e4SLinus Torvalds } 27871da177e4SLinus Torvalds 278813769ebaSMaciej W. Rozycki /* 278913769ebaSMaciej W. Rozycki * Emulate FPU instructions. 279013769ebaSMaciej W. Rozycki * 279113769ebaSMaciej W. Rozycki * If we use FPU hardware, then we have been typically called to handle 279213769ebaSMaciej W. Rozycki * an unimplemented operation, such as where an operand is a NaN or 279313769ebaSMaciej W. Rozycki * denormalized. In that case exit the emulation loop after a single 279413769ebaSMaciej W. Rozycki * iteration so as to let hardware execute any subsequent instructions. 279513769ebaSMaciej W. Rozycki * 279613769ebaSMaciej W. Rozycki * If we have no FPU hardware or it has been disabled, then continue 279713769ebaSMaciej W. Rozycki * emulating floating-point instructions until one of these conditions 279813769ebaSMaciej W. Rozycki * has occurred: 279913769ebaSMaciej W. Rozycki * 280013769ebaSMaciej W. Rozycki * - a non-FPU instruction has been encountered, 280113769ebaSMaciej W. Rozycki * 280213769ebaSMaciej W. Rozycki * - an attempt to emulate has ended with a signal, 280313769ebaSMaciej W. Rozycki * 280413769ebaSMaciej W. Rozycki * - the ISA mode has been switched. 280513769ebaSMaciej W. Rozycki * 280613769ebaSMaciej W. Rozycki * We need to terminate the emulation loop if we got switched to the 280713769ebaSMaciej W. Rozycki * MIPS16 mode, whether supported or not, so that we do not attempt 280813769ebaSMaciej W. Rozycki * to emulate a MIPS16 instruction as a regular MIPS FPU instruction. 280913769ebaSMaciej W. Rozycki * Similarly if we got switched to the microMIPS mode and only the 281013769ebaSMaciej W. Rozycki * regular MIPS mode is supported, so that we do not attempt to emulate 281113769ebaSMaciej W. Rozycki * a microMIPS instruction as a regular MIPS FPU instruction. Or if 281213769ebaSMaciej W. Rozycki * we got switched to the regular MIPS mode and only the microMIPS mode 281313769ebaSMaciej W. Rozycki * is supported, so that we do not attempt to emulate a regular MIPS 281413769ebaSMaciej W. Rozycki * instruction that should cause an Address Error exception instead. 281513769ebaSMaciej W. Rozycki * For simplicity we always terminate upon an ISA mode switch. 281613769ebaSMaciej W. Rozycki */ 2817e04582b7SAtsushi Nemoto int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 2818445a58ceSPaul Burton int has_fpu, void __user **fault_addr) 28191da177e4SLinus Torvalds { 2820333d1f67SRalf Baechle unsigned long oldepc, prevepc; 2821102cedc3SLeonid Yegoshin struct mm_decoded_insn dec_insn; 2822102cedc3SLeonid Yegoshin u16 instr[4]; 2823102cedc3SLeonid Yegoshin u16 *instr_ptr; 28241da177e4SLinus Torvalds int sig = 0; 28251da177e4SLinus Torvalds 28261da177e4SLinus Torvalds oldepc = xcp->cp0_epc; 28271da177e4SLinus Torvalds do { 28281da177e4SLinus Torvalds prevepc = xcp->cp0_epc; 28291da177e4SLinus Torvalds 2830102cedc3SLeonid Yegoshin if (get_isa16_mode(prevepc) && cpu_has_mmips) { 2831102cedc3SLeonid Yegoshin /* 2832102cedc3SLeonid Yegoshin * Get next 2 microMIPS instructions and convert them 2833102cedc3SLeonid Yegoshin * into 32-bit instructions. 2834102cedc3SLeonid Yegoshin */ 2835102cedc3SLeonid Yegoshin if ((get_user(instr[0], (u16 __user *)msk_isa16_mode(xcp->cp0_epc))) || 2836102cedc3SLeonid Yegoshin (get_user(instr[1], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 2))) || 2837102cedc3SLeonid Yegoshin (get_user(instr[2], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 4))) || 2838102cedc3SLeonid Yegoshin (get_user(instr[3], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 6)))) { 2839b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 28401da177e4SLinus Torvalds return SIGBUS; 28411da177e4SLinus Torvalds } 2842102cedc3SLeonid Yegoshin instr_ptr = instr; 2843102cedc3SLeonid Yegoshin 2844102cedc3SLeonid Yegoshin /* Get first instruction. */ 2845102cedc3SLeonid Yegoshin if (mm_insn_16bit(*instr_ptr)) { 2846102cedc3SLeonid Yegoshin /* Duplicate the half-word. */ 2847102cedc3SLeonid Yegoshin dec_insn.insn = (*instr_ptr << 16) | 2848102cedc3SLeonid Yegoshin (*instr_ptr); 2849102cedc3SLeonid Yegoshin /* 16-bit instruction. */ 2850102cedc3SLeonid Yegoshin dec_insn.pc_inc = 2; 2851102cedc3SLeonid Yegoshin instr_ptr += 1; 2852102cedc3SLeonid Yegoshin } else { 2853102cedc3SLeonid Yegoshin dec_insn.insn = (*instr_ptr << 16) | 2854102cedc3SLeonid Yegoshin *(instr_ptr+1); 2855102cedc3SLeonid Yegoshin /* 32-bit instruction. */ 2856102cedc3SLeonid Yegoshin dec_insn.pc_inc = 4; 2857102cedc3SLeonid Yegoshin instr_ptr += 2; 2858515b029dSDavid Daney } 2859102cedc3SLeonid Yegoshin /* Get second instruction. */ 2860102cedc3SLeonid Yegoshin if (mm_insn_16bit(*instr_ptr)) { 2861102cedc3SLeonid Yegoshin /* Duplicate the half-word. */ 2862102cedc3SLeonid Yegoshin dec_insn.next_insn = (*instr_ptr << 16) | 2863102cedc3SLeonid Yegoshin (*instr_ptr); 2864102cedc3SLeonid Yegoshin /* 16-bit instruction. */ 2865102cedc3SLeonid Yegoshin dec_insn.next_pc_inc = 2; 2866102cedc3SLeonid Yegoshin } else { 2867102cedc3SLeonid Yegoshin dec_insn.next_insn = (*instr_ptr << 16) | 2868102cedc3SLeonid Yegoshin *(instr_ptr+1); 2869102cedc3SLeonid Yegoshin /* 32-bit instruction. */ 2870102cedc3SLeonid Yegoshin dec_insn.next_pc_inc = 4; 2871102cedc3SLeonid Yegoshin } 2872102cedc3SLeonid Yegoshin dec_insn.micro_mips_mode = 1; 2873102cedc3SLeonid Yegoshin } else { 2874102cedc3SLeonid Yegoshin if ((get_user(dec_insn.insn, 2875102cedc3SLeonid Yegoshin (mips_instruction __user *) xcp->cp0_epc)) || 2876102cedc3SLeonid Yegoshin (get_user(dec_insn.next_insn, 2877102cedc3SLeonid Yegoshin (mips_instruction __user *)(xcp->cp0_epc+4)))) { 2878102cedc3SLeonid Yegoshin MIPS_FPU_EMU_INC_STATS(errors); 2879102cedc3SLeonid Yegoshin return SIGBUS; 2880102cedc3SLeonid Yegoshin } 2881102cedc3SLeonid Yegoshin dec_insn.pc_inc = 4; 2882102cedc3SLeonid Yegoshin dec_insn.next_pc_inc = 4; 2883102cedc3SLeonid Yegoshin dec_insn.micro_mips_mode = 0; 2884102cedc3SLeonid Yegoshin } 2885102cedc3SLeonid Yegoshin 2886102cedc3SLeonid Yegoshin if ((dec_insn.insn == 0) || 2887102cedc3SLeonid Yegoshin ((dec_insn.pc_inc == 2) && 2888102cedc3SLeonid Yegoshin ((dec_insn.insn & 0xffff) == MM_NOP16))) 2889102cedc3SLeonid Yegoshin xcp->cp0_epc += dec_insn.pc_inc; /* Skip NOPs */ 28901da177e4SLinus Torvalds else { 2891cd21dfcfSRalf Baechle /* 28922cfcf8a8SMaciej W. Rozycki * The 'ieee754_csr' is an alias of ctx->fcr31. 28932cfcf8a8SMaciej W. Rozycki * No need to copy ctx->fcr31 to ieee754_csr. 2894cd21dfcfSRalf Baechle */ 2895102cedc3SLeonid Yegoshin sig = cop1Emulate(xcp, ctx, dec_insn, fault_addr); 28961da177e4SLinus Torvalds } 28971da177e4SLinus Torvalds 2898e04582b7SAtsushi Nemoto if (has_fpu) 28991da177e4SLinus Torvalds break; 29001da177e4SLinus Torvalds if (sig) 29011da177e4SLinus Torvalds break; 290213769ebaSMaciej W. Rozycki /* 290313769ebaSMaciej W. Rozycki * We have to check for the ISA bit explicitly here, 290413769ebaSMaciej W. Rozycki * because `get_isa16_mode' may return 0 if support 290513769ebaSMaciej W. Rozycki * for code compression has been globally disabled, 290613769ebaSMaciej W. Rozycki * or otherwise we may produce the wrong signal or 290713769ebaSMaciej W. Rozycki * even proceed successfully where we must not. 290813769ebaSMaciej W. Rozycki */ 290913769ebaSMaciej W. Rozycki if ((xcp->cp0_epc ^ prevepc) & 0x1) 291013769ebaSMaciej W. Rozycki break; 29111da177e4SLinus Torvalds 29121da177e4SLinus Torvalds cond_resched(); 29131da177e4SLinus Torvalds } while (xcp->cp0_epc > prevepc); 29141da177e4SLinus Torvalds 29151da177e4SLinus Torvalds /* SIGILL indicates a non-fpu instruction */ 29161da177e4SLinus Torvalds if (sig == SIGILL && xcp->cp0_epc != oldepc) 29173f7cac41SRalf Baechle /* but if EPC has advanced, then ignore it */ 29181da177e4SLinus Torvalds sig = 0; 29191da177e4SLinus Torvalds 29201da177e4SLinus Torvalds return sig; 29211da177e4SLinus Torvalds } 2922