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> 3808a07904SRalf Baechle #include <linux/kconfig.h> 3985c51c51SRalf Baechle #include <linux/percpu-defs.h> 407f788d2dSDeng-Cheng Zhu #include <linux/perf_event.h> 411da177e4SLinus Torvalds 42cd8ee345SRalf Baechle #include <asm/branch.h> 431da177e4SLinus Torvalds #include <asm/inst.h> 441da177e4SLinus Torvalds #include <asm/ptrace.h> 451da177e4SLinus Torvalds #include <asm/signal.h> 46cd8ee345SRalf Baechle #include <asm/uaccess.h> 47cd8ee345SRalf Baechle 48cd8ee345SRalf Baechle #include <asm/processor.h> 491da177e4SLinus Torvalds #include <asm/fpu_emulator.h> 50102cedc3SLeonid Yegoshin #include <asm/fpu.h> 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds #include "ieee754.h" 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds /* Function which emulates a floating point instruction. */ 551da177e4SLinus Torvalds 56eae89076SAtsushi Nemoto static int fpu_emu(struct pt_regs *, struct mips_fpu_struct *, 571da177e4SLinus Torvalds mips_instruction); 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds static int fpux_emu(struct pt_regs *, 60515b029dSDavid Daney struct mips_fpu_struct *, mips_instruction, void *__user *); 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds /* Control registers */ 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds #define FPCREG_RID 0 /* $0 = revision id */ 651da177e4SLinus Torvalds #define FPCREG_CSR 31 /* $31 = csr */ 661da177e4SLinus Torvalds 6795e8f634SShane McDonald /* Determine rounding mode from the RM bits of the FCSR */ 6895e8f634SShane McDonald #define modeindex(v) ((v) & FPU_CSR_RM) 6995e8f634SShane McDonald 701da177e4SLinus Torvalds /* convert condition code register number to csr bit */ 711da177e4SLinus Torvalds static const unsigned int fpucondbit[8] = { 721da177e4SLinus Torvalds FPU_CSR_COND0, 731da177e4SLinus Torvalds FPU_CSR_COND1, 741da177e4SLinus Torvalds FPU_CSR_COND2, 751da177e4SLinus Torvalds FPU_CSR_COND3, 761da177e4SLinus Torvalds FPU_CSR_COND4, 771da177e4SLinus Torvalds FPU_CSR_COND5, 781da177e4SLinus Torvalds FPU_CSR_COND6, 791da177e4SLinus Torvalds FPU_CSR_COND7 801da177e4SLinus Torvalds }; 811da177e4SLinus Torvalds 82102cedc3SLeonid Yegoshin /* (microMIPS) Convert certain microMIPS instructions to MIPS32 format. */ 83102cedc3SLeonid Yegoshin static const int sd_format[] = {16, 17, 0, 0, 0, 0, 0, 0}; 84102cedc3SLeonid Yegoshin static const int sdps_format[] = {16, 17, 22, 0, 0, 0, 0, 0}; 85102cedc3SLeonid Yegoshin static const int dwl_format[] = {17, 20, 21, 0, 0, 0, 0, 0}; 86102cedc3SLeonid Yegoshin static const int swl_format[] = {16, 20, 21, 0, 0, 0, 0, 0}; 87102cedc3SLeonid Yegoshin 88102cedc3SLeonid Yegoshin /* 89102cedc3SLeonid Yegoshin * This functions translates a 32-bit microMIPS instruction 90102cedc3SLeonid Yegoshin * into a 32-bit MIPS32 instruction. Returns 0 on success 91102cedc3SLeonid Yegoshin * and SIGILL otherwise. 92102cedc3SLeonid Yegoshin */ 93102cedc3SLeonid Yegoshin static int microMIPS32_to_MIPS32(union mips_instruction *insn_ptr) 94102cedc3SLeonid Yegoshin { 95102cedc3SLeonid Yegoshin union mips_instruction insn = *insn_ptr; 96102cedc3SLeonid Yegoshin union mips_instruction mips32_insn = insn; 97102cedc3SLeonid Yegoshin int func, fmt, op; 98102cedc3SLeonid Yegoshin 99102cedc3SLeonid Yegoshin switch (insn.mm_i_format.opcode) { 100102cedc3SLeonid Yegoshin case mm_ldc132_op: 101102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.opcode = ldc1_op; 102102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 103102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 104102cedc3SLeonid Yegoshin break; 105102cedc3SLeonid Yegoshin case mm_lwc132_op: 106102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.opcode = lwc1_op; 107102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 108102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 109102cedc3SLeonid Yegoshin break; 110102cedc3SLeonid Yegoshin case mm_sdc132_op: 111102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.opcode = sdc1_op; 112102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 113102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 114102cedc3SLeonid Yegoshin break; 115102cedc3SLeonid Yegoshin case mm_swc132_op: 116102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.opcode = swc1_op; 117102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 118102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 119102cedc3SLeonid Yegoshin break; 120102cedc3SLeonid Yegoshin case mm_pool32i_op: 121102cedc3SLeonid Yegoshin /* NOTE: offset is << by 1 if in microMIPS mode. */ 122102cedc3SLeonid Yegoshin if ((insn.mm_i_format.rt == mm_bc1f_op) || 123102cedc3SLeonid Yegoshin (insn.mm_i_format.rt == mm_bc1t_op)) { 124102cedc3SLeonid Yegoshin mips32_insn.fb_format.opcode = cop1_op; 125102cedc3SLeonid Yegoshin mips32_insn.fb_format.bc = bc_op; 126102cedc3SLeonid Yegoshin mips32_insn.fb_format.flag = 127102cedc3SLeonid Yegoshin (insn.mm_i_format.rt == mm_bc1t_op) ? 1 : 0; 128102cedc3SLeonid Yegoshin } else 129102cedc3SLeonid Yegoshin return SIGILL; 130102cedc3SLeonid Yegoshin break; 131102cedc3SLeonid Yegoshin case mm_pool32f_op: 132102cedc3SLeonid Yegoshin switch (insn.mm_fp0_format.func) { 133102cedc3SLeonid Yegoshin case mm_32f_01_op: 134102cedc3SLeonid Yegoshin case mm_32f_11_op: 135102cedc3SLeonid Yegoshin case mm_32f_02_op: 136102cedc3SLeonid Yegoshin case mm_32f_12_op: 137102cedc3SLeonid Yegoshin case mm_32f_41_op: 138102cedc3SLeonid Yegoshin case mm_32f_51_op: 139102cedc3SLeonid Yegoshin case mm_32f_42_op: 140102cedc3SLeonid Yegoshin case mm_32f_52_op: 141102cedc3SLeonid Yegoshin op = insn.mm_fp0_format.func; 142102cedc3SLeonid Yegoshin if (op == mm_32f_01_op) 143102cedc3SLeonid Yegoshin func = madd_s_op; 144102cedc3SLeonid Yegoshin else if (op == mm_32f_11_op) 145102cedc3SLeonid Yegoshin func = madd_d_op; 146102cedc3SLeonid Yegoshin else if (op == mm_32f_02_op) 147102cedc3SLeonid Yegoshin func = nmadd_s_op; 148102cedc3SLeonid Yegoshin else if (op == mm_32f_12_op) 149102cedc3SLeonid Yegoshin func = nmadd_d_op; 150102cedc3SLeonid Yegoshin else if (op == mm_32f_41_op) 151102cedc3SLeonid Yegoshin func = msub_s_op; 152102cedc3SLeonid Yegoshin else if (op == mm_32f_51_op) 153102cedc3SLeonid Yegoshin func = msub_d_op; 154102cedc3SLeonid Yegoshin else if (op == mm_32f_42_op) 155102cedc3SLeonid Yegoshin func = nmsub_s_op; 156102cedc3SLeonid Yegoshin else 157102cedc3SLeonid Yegoshin func = nmsub_d_op; 158102cedc3SLeonid Yegoshin mips32_insn.fp6_format.opcode = cop1x_op; 159102cedc3SLeonid Yegoshin mips32_insn.fp6_format.fr = insn.mm_fp6_format.fr; 160102cedc3SLeonid Yegoshin mips32_insn.fp6_format.ft = insn.mm_fp6_format.ft; 161102cedc3SLeonid Yegoshin mips32_insn.fp6_format.fs = insn.mm_fp6_format.fs; 162102cedc3SLeonid Yegoshin mips32_insn.fp6_format.fd = insn.mm_fp6_format.fd; 163102cedc3SLeonid Yegoshin mips32_insn.fp6_format.func = func; 164102cedc3SLeonid Yegoshin break; 165102cedc3SLeonid Yegoshin case mm_32f_10_op: 166102cedc3SLeonid Yegoshin func = -1; /* Invalid */ 167102cedc3SLeonid Yegoshin op = insn.mm_fp5_format.op & 0x7; 168102cedc3SLeonid Yegoshin if (op == mm_ldxc1_op) 169102cedc3SLeonid Yegoshin func = ldxc1_op; 170102cedc3SLeonid Yegoshin else if (op == mm_sdxc1_op) 171102cedc3SLeonid Yegoshin func = sdxc1_op; 172102cedc3SLeonid Yegoshin else if (op == mm_lwxc1_op) 173102cedc3SLeonid Yegoshin func = lwxc1_op; 174102cedc3SLeonid Yegoshin else if (op == mm_swxc1_op) 175102cedc3SLeonid Yegoshin func = swxc1_op; 176102cedc3SLeonid Yegoshin 177102cedc3SLeonid Yegoshin if (func != -1) { 178102cedc3SLeonid Yegoshin mips32_insn.r_format.opcode = cop1x_op; 179102cedc3SLeonid Yegoshin mips32_insn.r_format.rs = 180102cedc3SLeonid Yegoshin insn.mm_fp5_format.base; 181102cedc3SLeonid Yegoshin mips32_insn.r_format.rt = 182102cedc3SLeonid Yegoshin insn.mm_fp5_format.index; 183102cedc3SLeonid Yegoshin mips32_insn.r_format.rd = 0; 184102cedc3SLeonid Yegoshin mips32_insn.r_format.re = insn.mm_fp5_format.fd; 185102cedc3SLeonid Yegoshin mips32_insn.r_format.func = func; 186102cedc3SLeonid Yegoshin } else 187102cedc3SLeonid Yegoshin return SIGILL; 188102cedc3SLeonid Yegoshin break; 189102cedc3SLeonid Yegoshin case mm_32f_40_op: 190102cedc3SLeonid Yegoshin op = -1; /* Invalid */ 191102cedc3SLeonid Yegoshin if (insn.mm_fp2_format.op == mm_fmovt_op) 192102cedc3SLeonid Yegoshin op = 1; 193102cedc3SLeonid Yegoshin else if (insn.mm_fp2_format.op == mm_fmovf_op) 194102cedc3SLeonid Yegoshin op = 0; 195102cedc3SLeonid Yegoshin if (op != -1) { 196102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 197102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 198102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp2_format.fmt]; 199102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 200102cedc3SLeonid Yegoshin (insn.mm_fp2_format.cc<<2) + op; 201102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 202102cedc3SLeonid Yegoshin insn.mm_fp2_format.fs; 203102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 204102cedc3SLeonid Yegoshin insn.mm_fp2_format.fd; 205102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = fmovc_op; 206102cedc3SLeonid Yegoshin } else 207102cedc3SLeonid Yegoshin return SIGILL; 208102cedc3SLeonid Yegoshin break; 209102cedc3SLeonid Yegoshin case mm_32f_60_op: 210102cedc3SLeonid Yegoshin func = -1; /* Invalid */ 211102cedc3SLeonid Yegoshin if (insn.mm_fp0_format.op == mm_fadd_op) 212102cedc3SLeonid Yegoshin func = fadd_op; 213102cedc3SLeonid Yegoshin else if (insn.mm_fp0_format.op == mm_fsub_op) 214102cedc3SLeonid Yegoshin func = fsub_op; 215102cedc3SLeonid Yegoshin else if (insn.mm_fp0_format.op == mm_fmul_op) 216102cedc3SLeonid Yegoshin func = fmul_op; 217102cedc3SLeonid Yegoshin else if (insn.mm_fp0_format.op == mm_fdiv_op) 218102cedc3SLeonid Yegoshin func = fdiv_op; 219102cedc3SLeonid Yegoshin if (func != -1) { 220102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 221102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 222102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp0_format.fmt]; 223102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 224102cedc3SLeonid Yegoshin insn.mm_fp0_format.ft; 225102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 226102cedc3SLeonid Yegoshin insn.mm_fp0_format.fs; 227102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 228102cedc3SLeonid Yegoshin insn.mm_fp0_format.fd; 229102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 230102cedc3SLeonid Yegoshin } else 231102cedc3SLeonid Yegoshin return SIGILL; 232102cedc3SLeonid Yegoshin break; 233102cedc3SLeonid Yegoshin case mm_32f_70_op: 234102cedc3SLeonid Yegoshin func = -1; /* Invalid */ 235102cedc3SLeonid Yegoshin if (insn.mm_fp0_format.op == mm_fmovn_op) 236102cedc3SLeonid Yegoshin func = fmovn_op; 237102cedc3SLeonid Yegoshin else if (insn.mm_fp0_format.op == mm_fmovz_op) 238102cedc3SLeonid Yegoshin func = fmovz_op; 239102cedc3SLeonid Yegoshin if (func != -1) { 240102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 241102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 242102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp0_format.fmt]; 243102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 244102cedc3SLeonid Yegoshin insn.mm_fp0_format.ft; 245102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 246102cedc3SLeonid Yegoshin insn.mm_fp0_format.fs; 247102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 248102cedc3SLeonid Yegoshin insn.mm_fp0_format.fd; 249102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 250102cedc3SLeonid Yegoshin } else 251102cedc3SLeonid Yegoshin return SIGILL; 252102cedc3SLeonid Yegoshin break; 253102cedc3SLeonid Yegoshin case mm_32f_73_op: /* POOL32FXF */ 254102cedc3SLeonid Yegoshin switch (insn.mm_fp1_format.op) { 255102cedc3SLeonid Yegoshin case mm_movf0_op: 256102cedc3SLeonid Yegoshin case mm_movf1_op: 257102cedc3SLeonid Yegoshin case mm_movt0_op: 258102cedc3SLeonid Yegoshin case mm_movt1_op: 259102cedc3SLeonid Yegoshin if ((insn.mm_fp1_format.op & 0x7f) == 260102cedc3SLeonid Yegoshin mm_movf0_op) 261102cedc3SLeonid Yegoshin op = 0; 262102cedc3SLeonid Yegoshin else 263102cedc3SLeonid Yegoshin op = 1; 264102cedc3SLeonid Yegoshin mips32_insn.r_format.opcode = spec_op; 265102cedc3SLeonid Yegoshin mips32_insn.r_format.rs = insn.mm_fp4_format.fs; 266102cedc3SLeonid Yegoshin mips32_insn.r_format.rt = 267102cedc3SLeonid Yegoshin (insn.mm_fp4_format.cc << 2) + op; 268102cedc3SLeonid Yegoshin mips32_insn.r_format.rd = insn.mm_fp4_format.rt; 269102cedc3SLeonid Yegoshin mips32_insn.r_format.re = 0; 270102cedc3SLeonid Yegoshin mips32_insn.r_format.func = movc_op; 271102cedc3SLeonid Yegoshin break; 272102cedc3SLeonid Yegoshin case mm_fcvtd0_op: 273102cedc3SLeonid Yegoshin case mm_fcvtd1_op: 274102cedc3SLeonid Yegoshin case mm_fcvts0_op: 275102cedc3SLeonid Yegoshin case mm_fcvts1_op: 276102cedc3SLeonid Yegoshin if ((insn.mm_fp1_format.op & 0x7f) == 277102cedc3SLeonid Yegoshin mm_fcvtd0_op) { 278102cedc3SLeonid Yegoshin func = fcvtd_op; 279102cedc3SLeonid Yegoshin fmt = swl_format[insn.mm_fp3_format.fmt]; 280102cedc3SLeonid Yegoshin } else { 281102cedc3SLeonid Yegoshin func = fcvts_op; 282102cedc3SLeonid Yegoshin fmt = dwl_format[insn.mm_fp3_format.fmt]; 283102cedc3SLeonid Yegoshin } 284102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 285102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = fmt; 286102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 0; 287102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 288102cedc3SLeonid Yegoshin insn.mm_fp3_format.fs; 289102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 290102cedc3SLeonid Yegoshin insn.mm_fp3_format.rt; 291102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 292102cedc3SLeonid Yegoshin break; 293102cedc3SLeonid Yegoshin case mm_fmov0_op: 294102cedc3SLeonid Yegoshin case mm_fmov1_op: 295102cedc3SLeonid Yegoshin case mm_fabs0_op: 296102cedc3SLeonid Yegoshin case mm_fabs1_op: 297102cedc3SLeonid Yegoshin case mm_fneg0_op: 298102cedc3SLeonid Yegoshin case mm_fneg1_op: 299102cedc3SLeonid Yegoshin if ((insn.mm_fp1_format.op & 0x7f) == 300102cedc3SLeonid Yegoshin mm_fmov0_op) 301102cedc3SLeonid Yegoshin func = fmov_op; 302102cedc3SLeonid Yegoshin else if ((insn.mm_fp1_format.op & 0x7f) == 303102cedc3SLeonid Yegoshin mm_fabs0_op) 304102cedc3SLeonid Yegoshin func = fabs_op; 305102cedc3SLeonid Yegoshin else 306102cedc3SLeonid Yegoshin func = fneg_op; 307102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 308102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 309102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp3_format.fmt]; 310102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 0; 311102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 312102cedc3SLeonid Yegoshin insn.mm_fp3_format.fs; 313102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 314102cedc3SLeonid Yegoshin insn.mm_fp3_format.rt; 315102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 316102cedc3SLeonid Yegoshin break; 317102cedc3SLeonid Yegoshin case mm_ffloorl_op: 318102cedc3SLeonid Yegoshin case mm_ffloorw_op: 319102cedc3SLeonid Yegoshin case mm_fceill_op: 320102cedc3SLeonid Yegoshin case mm_fceilw_op: 321102cedc3SLeonid Yegoshin case mm_ftruncl_op: 322102cedc3SLeonid Yegoshin case mm_ftruncw_op: 323102cedc3SLeonid Yegoshin case mm_froundl_op: 324102cedc3SLeonid Yegoshin case mm_froundw_op: 325102cedc3SLeonid Yegoshin case mm_fcvtl_op: 326102cedc3SLeonid Yegoshin case mm_fcvtw_op: 327102cedc3SLeonid Yegoshin if (insn.mm_fp1_format.op == mm_ffloorl_op) 328102cedc3SLeonid Yegoshin func = ffloorl_op; 329102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_ffloorw_op) 330102cedc3SLeonid Yegoshin func = ffloor_op; 331102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_fceill_op) 332102cedc3SLeonid Yegoshin func = fceill_op; 333102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_fceilw_op) 334102cedc3SLeonid Yegoshin func = fceil_op; 335102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_ftruncl_op) 336102cedc3SLeonid Yegoshin func = ftruncl_op; 337102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_ftruncw_op) 338102cedc3SLeonid Yegoshin func = ftrunc_op; 339102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_froundl_op) 340102cedc3SLeonid Yegoshin func = froundl_op; 341102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_froundw_op) 342102cedc3SLeonid Yegoshin func = fround_op; 343102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_fcvtl_op) 344102cedc3SLeonid Yegoshin func = fcvtl_op; 345102cedc3SLeonid Yegoshin else 346102cedc3SLeonid Yegoshin func = fcvtw_op; 347102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 348102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 349102cedc3SLeonid Yegoshin sd_format[insn.mm_fp1_format.fmt]; 350102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 0; 351102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 352102cedc3SLeonid Yegoshin insn.mm_fp1_format.fs; 353102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 354102cedc3SLeonid Yegoshin insn.mm_fp1_format.rt; 355102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 356102cedc3SLeonid Yegoshin break; 357102cedc3SLeonid Yegoshin case mm_frsqrt_op: 358102cedc3SLeonid Yegoshin case mm_fsqrt_op: 359102cedc3SLeonid Yegoshin case mm_frecip_op: 360102cedc3SLeonid Yegoshin if (insn.mm_fp1_format.op == mm_frsqrt_op) 361102cedc3SLeonid Yegoshin func = frsqrt_op; 362102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_fsqrt_op) 363102cedc3SLeonid Yegoshin func = fsqrt_op; 364102cedc3SLeonid Yegoshin else 365102cedc3SLeonid Yegoshin func = frecip_op; 366102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 367102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 368102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp1_format.fmt]; 369102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 0; 370102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 371102cedc3SLeonid Yegoshin insn.mm_fp1_format.fs; 372102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 373102cedc3SLeonid Yegoshin insn.mm_fp1_format.rt; 374102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 375102cedc3SLeonid Yegoshin break; 376102cedc3SLeonid Yegoshin case mm_mfc1_op: 377102cedc3SLeonid Yegoshin case mm_mtc1_op: 378102cedc3SLeonid Yegoshin case mm_cfc1_op: 379102cedc3SLeonid Yegoshin case mm_ctc1_op: 3809355e59cSSteven J. Hill case mm_mfhc1_op: 3819355e59cSSteven J. Hill case mm_mthc1_op: 382102cedc3SLeonid Yegoshin if (insn.mm_fp1_format.op == mm_mfc1_op) 383102cedc3SLeonid Yegoshin op = mfc_op; 384102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_mtc1_op) 385102cedc3SLeonid Yegoshin op = mtc_op; 386102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_cfc1_op) 387102cedc3SLeonid Yegoshin op = cfc_op; 3889355e59cSSteven J. Hill else if (insn.mm_fp1_format.op == mm_ctc1_op) 389102cedc3SLeonid Yegoshin op = ctc_op; 3909355e59cSSteven J. Hill else if (insn.mm_fp1_format.op == mm_mfhc1_op) 3919355e59cSSteven J. Hill op = mfhc_op; 3929355e59cSSteven J. Hill else 3939355e59cSSteven J. Hill op = mthc_op; 394102cedc3SLeonid Yegoshin mips32_insn.fp1_format.opcode = cop1_op; 395102cedc3SLeonid Yegoshin mips32_insn.fp1_format.op = op; 396102cedc3SLeonid Yegoshin mips32_insn.fp1_format.rt = 397102cedc3SLeonid Yegoshin insn.mm_fp1_format.rt; 398102cedc3SLeonid Yegoshin mips32_insn.fp1_format.fs = 399102cedc3SLeonid Yegoshin insn.mm_fp1_format.fs; 400102cedc3SLeonid Yegoshin mips32_insn.fp1_format.fd = 0; 401102cedc3SLeonid Yegoshin mips32_insn.fp1_format.func = 0; 402102cedc3SLeonid Yegoshin break; 403102cedc3SLeonid Yegoshin default: 404102cedc3SLeonid Yegoshin return SIGILL; 405102cedc3SLeonid Yegoshin } 406102cedc3SLeonid Yegoshin break; 407102cedc3SLeonid Yegoshin case mm_32f_74_op: /* c.cond.fmt */ 408102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 409102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 410102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp4_format.fmt]; 411102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = insn.mm_fp4_format.rt; 412102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = insn.mm_fp4_format.fs; 413102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = insn.mm_fp4_format.cc << 2; 414102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = 415102cedc3SLeonid Yegoshin insn.mm_fp4_format.cond | MM_MIPS32_COND_FC; 416102cedc3SLeonid Yegoshin break; 417102cedc3SLeonid Yegoshin default: 418102cedc3SLeonid Yegoshin return SIGILL; 419102cedc3SLeonid Yegoshin } 420102cedc3SLeonid Yegoshin break; 421102cedc3SLeonid Yegoshin default: 422102cedc3SLeonid Yegoshin return SIGILL; 423102cedc3SLeonid Yegoshin } 424102cedc3SLeonid Yegoshin 425102cedc3SLeonid Yegoshin *insn_ptr = mips32_insn; 426102cedc3SLeonid Yegoshin return 0; 427102cedc3SLeonid Yegoshin } 428102cedc3SLeonid Yegoshin 4291da177e4SLinus Torvalds /* 4301da177e4SLinus Torvalds * Redundant with logic already in kernel/branch.c, 4311da177e4SLinus Torvalds * embedded in compute_return_epc. At some point, 4321da177e4SLinus Torvalds * a single subroutine should be used across both 4331da177e4SLinus Torvalds * modules. 4341da177e4SLinus Torvalds */ 435102cedc3SLeonid Yegoshin static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, 436102cedc3SLeonid Yegoshin unsigned long *contpc) 4371da177e4SLinus Torvalds { 438102cedc3SLeonid Yegoshin union mips_instruction insn = (union mips_instruction)dec_insn.insn; 439102cedc3SLeonid Yegoshin unsigned int fcr31; 440102cedc3SLeonid Yegoshin unsigned int bit = 0; 441102cedc3SLeonid Yegoshin 442102cedc3SLeonid Yegoshin switch (insn.i_format.opcode) { 4431da177e4SLinus Torvalds case spec_op: 444102cedc3SLeonid Yegoshin switch (insn.r_format.func) { 4451da177e4SLinus Torvalds case jalr_op: 446102cedc3SLeonid Yegoshin regs->regs[insn.r_format.rd] = 447102cedc3SLeonid Yegoshin regs->cp0_epc + dec_insn.pc_inc + 448102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 449102cedc3SLeonid Yegoshin /* Fall through */ 4501da177e4SLinus Torvalds case jr_op: 451102cedc3SLeonid Yegoshin *contpc = regs->regs[insn.r_format.rs]; 4521da177e4SLinus Torvalds return 1; 4531da177e4SLinus Torvalds } 4541da177e4SLinus Torvalds break; 4551da177e4SLinus Torvalds case bcond_op: 456102cedc3SLeonid Yegoshin switch (insn.i_format.rt) { 4571da177e4SLinus Torvalds case bltzal_op: 4581da177e4SLinus Torvalds case bltzall_op: 459102cedc3SLeonid Yegoshin regs->regs[31] = regs->cp0_epc + 460102cedc3SLeonid Yegoshin dec_insn.pc_inc + 461102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 462102cedc3SLeonid Yegoshin /* Fall through */ 463102cedc3SLeonid Yegoshin case bltz_op: 464102cedc3SLeonid Yegoshin case bltzl_op: 465102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.i_format.rs] < 0) 466102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 467102cedc3SLeonid Yegoshin dec_insn.pc_inc + 468102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 469102cedc3SLeonid Yegoshin else 470102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 471102cedc3SLeonid Yegoshin dec_insn.pc_inc + 472102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 4731da177e4SLinus Torvalds return 1; 474102cedc3SLeonid Yegoshin case bgezal_op: 475102cedc3SLeonid Yegoshin case bgezall_op: 476102cedc3SLeonid Yegoshin regs->regs[31] = regs->cp0_epc + 477102cedc3SLeonid Yegoshin dec_insn.pc_inc + 478102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 479102cedc3SLeonid Yegoshin /* Fall through */ 480102cedc3SLeonid Yegoshin case bgez_op: 481102cedc3SLeonid Yegoshin case bgezl_op: 482102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.i_format.rs] >= 0) 483102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 484102cedc3SLeonid Yegoshin dec_insn.pc_inc + 485102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 486102cedc3SLeonid Yegoshin else 487102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 488102cedc3SLeonid Yegoshin dec_insn.pc_inc + 489102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 490102cedc3SLeonid Yegoshin return 1; 4911da177e4SLinus Torvalds } 4921da177e4SLinus Torvalds break; 4931da177e4SLinus Torvalds case jalx_op: 494102cedc3SLeonid Yegoshin set_isa16_mode(bit); 495102cedc3SLeonid Yegoshin case jal_op: 496102cedc3SLeonid Yegoshin regs->regs[31] = regs->cp0_epc + 497102cedc3SLeonid Yegoshin dec_insn.pc_inc + 498102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 499102cedc3SLeonid Yegoshin /* Fall through */ 500102cedc3SLeonid Yegoshin case j_op: 501102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + dec_insn.pc_inc; 502102cedc3SLeonid Yegoshin *contpc >>= 28; 503102cedc3SLeonid Yegoshin *contpc <<= 28; 504102cedc3SLeonid Yegoshin *contpc |= (insn.j_format.target << 2); 505102cedc3SLeonid Yegoshin /* Set microMIPS mode bit: XOR for jalx. */ 506102cedc3SLeonid Yegoshin *contpc ^= bit; 5071da177e4SLinus Torvalds return 1; 508102cedc3SLeonid Yegoshin case beq_op: 509102cedc3SLeonid Yegoshin case beql_op: 510102cedc3SLeonid Yegoshin if (regs->regs[insn.i_format.rs] == 511102cedc3SLeonid Yegoshin regs->regs[insn.i_format.rt]) 512102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 513102cedc3SLeonid Yegoshin dec_insn.pc_inc + 514102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 515102cedc3SLeonid Yegoshin else 516102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 517102cedc3SLeonid Yegoshin dec_insn.pc_inc + 518102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 519102cedc3SLeonid Yegoshin return 1; 520102cedc3SLeonid Yegoshin case bne_op: 521102cedc3SLeonid Yegoshin case bnel_op: 522102cedc3SLeonid Yegoshin if (regs->regs[insn.i_format.rs] != 523102cedc3SLeonid Yegoshin regs->regs[insn.i_format.rt]) 524102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 525102cedc3SLeonid Yegoshin dec_insn.pc_inc + 526102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 527102cedc3SLeonid Yegoshin else 528102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 529102cedc3SLeonid Yegoshin dec_insn.pc_inc + 530102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 531102cedc3SLeonid Yegoshin return 1; 532102cedc3SLeonid Yegoshin case blez_op: 533102cedc3SLeonid Yegoshin case blezl_op: 534102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.i_format.rs] <= 0) 535102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 536102cedc3SLeonid Yegoshin dec_insn.pc_inc + 537102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 538102cedc3SLeonid Yegoshin else 539102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 540102cedc3SLeonid Yegoshin dec_insn.pc_inc + 541102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 542102cedc3SLeonid Yegoshin return 1; 543102cedc3SLeonid Yegoshin case bgtz_op: 544102cedc3SLeonid Yegoshin case bgtzl_op: 545102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.i_format.rs] > 0) 546102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 547102cedc3SLeonid Yegoshin dec_insn.pc_inc + 548102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 549102cedc3SLeonid Yegoshin else 550102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 551102cedc3SLeonid Yegoshin dec_insn.pc_inc + 552102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 553102cedc3SLeonid Yegoshin return 1; 554c26d4219SDavid Daney #ifdef CONFIG_CPU_CAVIUM_OCTEON 555c26d4219SDavid Daney case lwc2_op: /* This is bbit0 on Octeon */ 556c26d4219SDavid Daney if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) == 0) 557c26d4219SDavid Daney *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); 558c26d4219SDavid Daney else 559c26d4219SDavid Daney *contpc = regs->cp0_epc + 8; 560c26d4219SDavid Daney return 1; 561c26d4219SDavid Daney case ldc2_op: /* This is bbit032 on Octeon */ 562c26d4219SDavid Daney if ((regs->regs[insn.i_format.rs] & (1ull<<(insn.i_format.rt + 32))) == 0) 563c26d4219SDavid Daney *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); 564c26d4219SDavid Daney else 565c26d4219SDavid Daney *contpc = regs->cp0_epc + 8; 566c26d4219SDavid Daney return 1; 567c26d4219SDavid Daney case swc2_op: /* This is bbit1 on Octeon */ 568c26d4219SDavid Daney if (regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) 569c26d4219SDavid Daney *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); 570c26d4219SDavid Daney else 571c26d4219SDavid Daney *contpc = regs->cp0_epc + 8; 572c26d4219SDavid Daney return 1; 573c26d4219SDavid Daney case sdc2_op: /* This is bbit132 on Octeon */ 574c26d4219SDavid Daney if (regs->regs[insn.i_format.rs] & (1ull<<(insn.i_format.rt + 32))) 575c26d4219SDavid Daney *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); 576c26d4219SDavid Daney else 577c26d4219SDavid Daney *contpc = regs->cp0_epc + 8; 578c26d4219SDavid Daney return 1; 579c26d4219SDavid Daney #endif 5801da177e4SLinus Torvalds case cop0_op: 5811da177e4SLinus Torvalds case cop1_op: 5821da177e4SLinus Torvalds case cop2_op: 5831da177e4SLinus Torvalds case cop1x_op: 584102cedc3SLeonid Yegoshin if (insn.i_format.rs == bc_op) { 585102cedc3SLeonid Yegoshin preempt_disable(); 586102cedc3SLeonid Yegoshin if (is_fpu_owner()) 587102cedc3SLeonid Yegoshin asm volatile("cfc1\t%0,$31" : "=r" (fcr31)); 588102cedc3SLeonid Yegoshin else 589102cedc3SLeonid Yegoshin fcr31 = current->thread.fpu.fcr31; 590102cedc3SLeonid Yegoshin preempt_enable(); 591102cedc3SLeonid Yegoshin 592102cedc3SLeonid Yegoshin bit = (insn.i_format.rt >> 2); 593102cedc3SLeonid Yegoshin bit += (bit != 0); 594102cedc3SLeonid Yegoshin bit += 23; 595102cedc3SLeonid Yegoshin switch (insn.i_format.rt & 3) { 596102cedc3SLeonid Yegoshin case 0: /* bc1f */ 597102cedc3SLeonid Yegoshin case 2: /* bc1fl */ 598102cedc3SLeonid Yegoshin if (~fcr31 & (1 << bit)) 599102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 600102cedc3SLeonid Yegoshin dec_insn.pc_inc + 601102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 602102cedc3SLeonid Yegoshin else 603102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 604102cedc3SLeonid Yegoshin dec_insn.pc_inc + 605102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 606102cedc3SLeonid Yegoshin return 1; 607102cedc3SLeonid Yegoshin case 1: /* bc1t */ 608102cedc3SLeonid Yegoshin case 3: /* bc1tl */ 609102cedc3SLeonid Yegoshin if (fcr31 & (1 << bit)) 610102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 611102cedc3SLeonid Yegoshin dec_insn.pc_inc + 612102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 613102cedc3SLeonid Yegoshin else 614102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 615102cedc3SLeonid Yegoshin dec_insn.pc_inc + 616102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 6171da177e4SLinus Torvalds return 1; 6181da177e4SLinus Torvalds } 619102cedc3SLeonid Yegoshin } 620102cedc3SLeonid Yegoshin break; 621102cedc3SLeonid Yegoshin } 6221da177e4SLinus Torvalds return 0; 6231da177e4SLinus Torvalds } 6241da177e4SLinus Torvalds 6251da177e4SLinus Torvalds /* 6261da177e4SLinus Torvalds * In the Linux kernel, we support selection of FPR format on the 627da0bac33SDavid Daney * basis of the Status.FR bit. If an FPU is not present, the FR bit 628da0bac33SDavid Daney * is hardwired to zero, which would imply a 32-bit FPU even for 629597ce172SPaul Burton * 64-bit CPUs so we rather look at TIF_32BIT_FPREGS. 63051d943f0SRalf Baechle * FPU emu is slow and bulky and optimizing this function offers fairly 63151d943f0SRalf Baechle * sizeable benefits so we try to be clever and make this function return 63251d943f0SRalf Baechle * a constant whenever possible, that is on 64-bit kernels without O32 633597ce172SPaul Burton * compatibility enabled and on 32-bit without 64-bit FPU support. 6341da177e4SLinus Torvalds */ 635da0bac33SDavid Daney static inline int cop1_64bit(struct pt_regs *xcp) 636da0bac33SDavid Daney { 63708a07904SRalf Baechle if (config_enabled(CONFIG_64BIT) && !config_enabled(CONFIG_MIPS32_O32)) 63851d943f0SRalf Baechle return 1; 63908a07904SRalf Baechle else if (config_enabled(CONFIG_32BIT) && 64008a07904SRalf Baechle !config_enabled(CONFIG_MIPS_O32_FP64_SUPPORT)) 641da0bac33SDavid Daney return 0; 64208a07904SRalf Baechle 643597ce172SPaul Burton return !test_thread_flag(TIF_32BIT_FPREGS); 644da0bac33SDavid Daney } 6451da177e4SLinus Torvalds 64647fa0c02SRalf Baechle #define SIFROMREG(si, x) \ 64747fa0c02SRalf Baechle do { \ 648bbd426f5SPaul Burton if (cop1_64bit(xcp)) \ 649bbd426f5SPaul Burton (si) = get_fpr32(&ctx->fpr[x], 0); \ 650bbd426f5SPaul Burton else \ 651bbd426f5SPaul Burton (si) = get_fpr32(&ctx->fpr[(x) & ~1], (x) & 1); \ 652bbd426f5SPaul Burton } while (0) 653da0bac33SDavid Daney 65447fa0c02SRalf Baechle #define SITOREG(si, x) \ 65547fa0c02SRalf Baechle do { \ 656ef1c47afSPaul Burton if (cop1_64bit(xcp)) { \ 657ef1c47afSPaul Burton unsigned i; \ 658bbd426f5SPaul Burton set_fpr32(&ctx->fpr[x], 0, si); \ 659ef1c47afSPaul Burton for (i = 1; i < ARRAY_SIZE(ctx->fpr[x].val32); i++) \ 660ef1c47afSPaul Burton set_fpr32(&ctx->fpr[x], i, 0); \ 661ef1c47afSPaul Burton } else { \ 662bbd426f5SPaul Burton set_fpr32(&ctx->fpr[(x) & ~1], (x) & 1, si); \ 663ef1c47afSPaul Burton } \ 664bbd426f5SPaul Burton } while (0) 6651da177e4SLinus Torvalds 666bbd426f5SPaul Burton #define SIFROMHREG(si, x) ((si) = get_fpr32(&ctx->fpr[x], 1)) 667ef1c47afSPaul Burton 66847fa0c02SRalf Baechle #define SITOHREG(si, x) \ 66947fa0c02SRalf Baechle do { \ 670ef1c47afSPaul Burton unsigned i; \ 671ef1c47afSPaul Burton set_fpr32(&ctx->fpr[x], 1, si); \ 672ef1c47afSPaul Burton for (i = 2; i < ARRAY_SIZE(ctx->fpr[x].val32); i++) \ 673ef1c47afSPaul Burton set_fpr32(&ctx->fpr[x], i, 0); \ 674ef1c47afSPaul Burton } while (0) 6751ac94400SLeonid Yegoshin 676bbd426f5SPaul Burton #define DIFROMREG(di, x) \ 677bbd426f5SPaul Burton ((di) = get_fpr64(&ctx->fpr[(x) & ~(cop1_64bit(xcp) == 0)], 0)) 678bbd426f5SPaul Burton 67947fa0c02SRalf Baechle #define DITOREG(di, x) \ 68047fa0c02SRalf Baechle do { \ 681ef1c47afSPaul Burton unsigned fpr, i; \ 682ef1c47afSPaul Burton fpr = (x) & ~(cop1_64bit(xcp) == 0); \ 683ef1c47afSPaul Burton set_fpr64(&ctx->fpr[fpr], 0, di); \ 684ef1c47afSPaul Burton for (i = 1; i < ARRAY_SIZE(ctx->fpr[x].val64); i++) \ 685ef1c47afSPaul Burton set_fpr64(&ctx->fpr[fpr], i, 0); \ 686ef1c47afSPaul Burton } while (0) 6871da177e4SLinus Torvalds 6881da177e4SLinus Torvalds #define SPFROMREG(sp, x) SIFROMREG((sp).bits, x) 6891da177e4SLinus Torvalds #define SPTOREG(sp, x) SITOREG((sp).bits, x) 6901da177e4SLinus Torvalds #define DPFROMREG(dp, x) DIFROMREG((dp).bits, x) 6911da177e4SLinus Torvalds #define DPTOREG(dp, x) DITOREG((dp).bits, x) 6921da177e4SLinus Torvalds 6931da177e4SLinus Torvalds /* 6941da177e4SLinus Torvalds * Emulate the single floating point instruction pointed at by EPC. 6951da177e4SLinus Torvalds * Two instructions if the instruction is in a branch delay slot. 6961da177e4SLinus Torvalds */ 6971da177e4SLinus Torvalds 698515b029dSDavid Daney static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 699102cedc3SLeonid Yegoshin struct mm_decoded_insn dec_insn, void *__user *fault_addr) 7001da177e4SLinus Torvalds { 701102cedc3SLeonid Yegoshin unsigned long contpc = xcp->cp0_epc + dec_insn.pc_inc; 7023f7cac41SRalf Baechle unsigned int cond, cbit; 7033f7cac41SRalf Baechle mips_instruction ir; 7043f7cac41SRalf Baechle int likely, pc_inc; 7053f7cac41SRalf Baechle u32 __user *wva; 7063f7cac41SRalf Baechle u64 __user *dva; 7073f7cac41SRalf Baechle u32 value; 7083f7cac41SRalf Baechle u32 wval; 7093f7cac41SRalf Baechle u64 dval; 7103f7cac41SRalf Baechle int sig; 7111da177e4SLinus Torvalds 712*70e4c234SRalf Baechle /* 713*70e4c234SRalf Baechle * These are giving gcc a gentle hint about what to expect in 714*70e4c234SRalf Baechle * dec_inst in order to do better optimization. 715*70e4c234SRalf Baechle */ 716*70e4c234SRalf Baechle if (!cpu_has_mmips && dec_insn.micro_mips_mode) 717*70e4c234SRalf Baechle unreachable(); 718*70e4c234SRalf Baechle 7191da177e4SLinus Torvalds /* XXX NEC Vr54xx bug workaround */ 720e7e9cae5SRalf Baechle if (delay_slot(xcp)) { 721102cedc3SLeonid Yegoshin if (dec_insn.micro_mips_mode) { 722102cedc3SLeonid Yegoshin if (!mm_isBranchInstr(xcp, dec_insn, &contpc)) 723e7e9cae5SRalf Baechle clear_delay_slot(xcp); 724102cedc3SLeonid Yegoshin } else { 725102cedc3SLeonid Yegoshin if (!isBranchInstr(xcp, dec_insn, &contpc)) 726e7e9cae5SRalf Baechle clear_delay_slot(xcp); 727102cedc3SLeonid Yegoshin } 728102cedc3SLeonid Yegoshin } 7291da177e4SLinus Torvalds 730e7e9cae5SRalf Baechle if (delay_slot(xcp)) { 7311da177e4SLinus Torvalds /* 7321da177e4SLinus Torvalds * The instruction to be emulated is in a branch delay slot 7331da177e4SLinus Torvalds * which means that we have to emulate the branch instruction 7341da177e4SLinus Torvalds * BEFORE we do the cop1 instruction. 7351da177e4SLinus Torvalds * 7361da177e4SLinus Torvalds * This branch could be a COP1 branch, but in that case we 7371da177e4SLinus Torvalds * would have had a trap for that instruction, and would not 7381da177e4SLinus Torvalds * come through this route. 7391da177e4SLinus Torvalds * 7401da177e4SLinus Torvalds * Linux MIPS branch emulator operates on context, updating the 7411da177e4SLinus Torvalds * cp0_epc. 7421da177e4SLinus Torvalds */ 743102cedc3SLeonid Yegoshin ir = dec_insn.next_insn; /* process delay slot instr */ 744102cedc3SLeonid Yegoshin pc_inc = dec_insn.next_pc_inc; 745333d1f67SRalf Baechle } else { 746102cedc3SLeonid Yegoshin ir = dec_insn.insn; /* process current instr */ 747102cedc3SLeonid Yegoshin pc_inc = dec_insn.pc_inc; 748102cedc3SLeonid Yegoshin } 749102cedc3SLeonid Yegoshin 750102cedc3SLeonid Yegoshin /* 751102cedc3SLeonid Yegoshin * Since microMIPS FPU instructios are a subset of MIPS32 FPU 752102cedc3SLeonid Yegoshin * instructions, we want to convert microMIPS FPU instructions 753102cedc3SLeonid Yegoshin * into MIPS32 instructions so that we could reuse all of the 754102cedc3SLeonid Yegoshin * FPU emulation code. 755102cedc3SLeonid Yegoshin * 756102cedc3SLeonid Yegoshin * NOTE: We cannot do this for branch instructions since they 757102cedc3SLeonid Yegoshin * are not a subset. Example: Cannot emulate a 16-bit 758102cedc3SLeonid Yegoshin * aligned target address with a MIPS32 instruction. 759102cedc3SLeonid Yegoshin */ 760102cedc3SLeonid Yegoshin if (dec_insn.micro_mips_mode) { 761102cedc3SLeonid Yegoshin /* 762102cedc3SLeonid Yegoshin * If next instruction is a 16-bit instruction, then it 763102cedc3SLeonid Yegoshin * it cannot be a FPU instruction. This could happen 764102cedc3SLeonid Yegoshin * since we can be called for non-FPU instructions. 765102cedc3SLeonid Yegoshin */ 766102cedc3SLeonid Yegoshin if ((pc_inc == 2) || 767102cedc3SLeonid Yegoshin (microMIPS32_to_MIPS32((union mips_instruction *)&ir) 768102cedc3SLeonid Yegoshin == SIGILL)) 769102cedc3SLeonid Yegoshin return SIGILL; 7701da177e4SLinus Torvalds } 7711da177e4SLinus Torvalds 7721da177e4SLinus Torvalds emul: 773a8b0ca17SPeter Zijlstra perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, xcp, 0); 774b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(emulated); 7751da177e4SLinus Torvalds switch (MIPSInst_OPCODE(ir)) { 7763f7cac41SRalf Baechle case ldc1_op: 7773f7cac41SRalf Baechle dva = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] + 7781da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 779b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(loads); 780515b029dSDavid Daney 7813f7cac41SRalf Baechle if (!access_ok(VERIFY_READ, dva, sizeof(u64))) { 782b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 7833f7cac41SRalf Baechle *fault_addr = dva; 7841da177e4SLinus Torvalds return SIGBUS; 7851da177e4SLinus Torvalds } 7863f7cac41SRalf Baechle if (__get_user(dval, dva)) { 787515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 7883f7cac41SRalf Baechle *fault_addr = dva; 789515b029dSDavid Daney return SIGSEGV; 790515b029dSDavid Daney } 7913f7cac41SRalf Baechle DITOREG(dval, MIPSInst_RT(ir)); 7921da177e4SLinus Torvalds break; 7931da177e4SLinus Torvalds 7943f7cac41SRalf Baechle case sdc1_op: 7953f7cac41SRalf Baechle dva = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] + 7961da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 797b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(stores); 7983f7cac41SRalf Baechle DIFROMREG(dval, MIPSInst_RT(ir)); 7993f7cac41SRalf Baechle if (!access_ok(VERIFY_WRITE, dva, sizeof(u64))) { 800b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 8013f7cac41SRalf Baechle *fault_addr = dva; 8021da177e4SLinus Torvalds return SIGBUS; 8031da177e4SLinus Torvalds } 8043f7cac41SRalf Baechle if (__put_user(dval, dva)) { 805515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 8063f7cac41SRalf Baechle *fault_addr = dva; 807515b029dSDavid Daney return SIGSEGV; 808515b029dSDavid Daney } 8091da177e4SLinus Torvalds break; 8101da177e4SLinus Torvalds 8113f7cac41SRalf Baechle case lwc1_op: 8123f7cac41SRalf Baechle wva = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] + 8131da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 814b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(loads); 8153f7cac41SRalf Baechle if (!access_ok(VERIFY_READ, wva, sizeof(u32))) { 816b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 8173f7cac41SRalf Baechle *fault_addr = wva; 8181da177e4SLinus Torvalds return SIGBUS; 8191da177e4SLinus Torvalds } 8203f7cac41SRalf Baechle if (__get_user(wval, wva)) { 821515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 8223f7cac41SRalf Baechle *fault_addr = wva; 823515b029dSDavid Daney return SIGSEGV; 824515b029dSDavid Daney } 8253f7cac41SRalf Baechle SITOREG(wval, MIPSInst_RT(ir)); 8261da177e4SLinus Torvalds break; 8271da177e4SLinus Torvalds 8283f7cac41SRalf Baechle case swc1_op: 8293f7cac41SRalf Baechle wva = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] + 8301da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 831b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(stores); 8323f7cac41SRalf Baechle SIFROMREG(wval, MIPSInst_RT(ir)); 8333f7cac41SRalf Baechle if (!access_ok(VERIFY_WRITE, wva, sizeof(u32))) { 834b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 8353f7cac41SRalf Baechle *fault_addr = wva; 8361da177e4SLinus Torvalds return SIGBUS; 8371da177e4SLinus Torvalds } 8383f7cac41SRalf Baechle if (__put_user(wval, wva)) { 839515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 8403f7cac41SRalf Baechle *fault_addr = wva; 841515b029dSDavid Daney return SIGSEGV; 842515b029dSDavid Daney } 8431da177e4SLinus Torvalds break; 8441da177e4SLinus Torvalds 8451da177e4SLinus Torvalds case cop1_op: 8461da177e4SLinus Torvalds switch (MIPSInst_RS(ir)) { 8471da177e4SLinus Torvalds case dmfc_op: 84808a07904SRalf Baechle if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) 84908a07904SRalf Baechle return SIGILL; 85008a07904SRalf Baechle 8511da177e4SLinus Torvalds /* copregister fs -> gpr[rt] */ 8521da177e4SLinus Torvalds if (MIPSInst_RT(ir) != 0) { 8531da177e4SLinus Torvalds DIFROMREG(xcp->regs[MIPSInst_RT(ir)], 8541da177e4SLinus Torvalds MIPSInst_RD(ir)); 8551da177e4SLinus Torvalds } 8561da177e4SLinus Torvalds break; 8571da177e4SLinus Torvalds 8581da177e4SLinus Torvalds case dmtc_op: 85908a07904SRalf Baechle if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) 86008a07904SRalf Baechle return SIGILL; 86108a07904SRalf Baechle 8621da177e4SLinus Torvalds /* copregister fs <- rt */ 8631da177e4SLinus Torvalds DITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); 8641da177e4SLinus Torvalds break; 8651da177e4SLinus Torvalds 8661ac94400SLeonid Yegoshin case mfhc_op: 8671ac94400SLeonid Yegoshin if (!cpu_has_mips_r2) 8681ac94400SLeonid Yegoshin goto sigill; 8691ac94400SLeonid Yegoshin 8701ac94400SLeonid Yegoshin /* copregister rd -> gpr[rt] */ 8711ac94400SLeonid Yegoshin if (MIPSInst_RT(ir) != 0) { 8721ac94400SLeonid Yegoshin SIFROMHREG(xcp->regs[MIPSInst_RT(ir)], 8731ac94400SLeonid Yegoshin MIPSInst_RD(ir)); 8741ac94400SLeonid Yegoshin } 8751ac94400SLeonid Yegoshin break; 8761ac94400SLeonid Yegoshin 8771ac94400SLeonid Yegoshin case mthc_op: 8781ac94400SLeonid Yegoshin if (!cpu_has_mips_r2) 8791ac94400SLeonid Yegoshin goto sigill; 8801ac94400SLeonid Yegoshin 8811ac94400SLeonid Yegoshin /* copregister rd <- gpr[rt] */ 8821ac94400SLeonid Yegoshin SITOHREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); 8831ac94400SLeonid Yegoshin break; 8841ac94400SLeonid Yegoshin 8851da177e4SLinus Torvalds case mfc_op: 8861da177e4SLinus Torvalds /* copregister rd -> gpr[rt] */ 8871da177e4SLinus Torvalds if (MIPSInst_RT(ir) != 0) { 8881da177e4SLinus Torvalds SIFROMREG(xcp->regs[MIPSInst_RT(ir)], 8891da177e4SLinus Torvalds MIPSInst_RD(ir)); 8901da177e4SLinus Torvalds } 8911da177e4SLinus Torvalds break; 8921da177e4SLinus Torvalds 8931da177e4SLinus Torvalds case mtc_op: 8941da177e4SLinus Torvalds /* copregister rd <- rt */ 8951da177e4SLinus Torvalds SITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); 8961da177e4SLinus Torvalds break; 8971da177e4SLinus Torvalds 8983f7cac41SRalf Baechle case cfc_op: 8991da177e4SLinus Torvalds /* cop control register rd -> gpr[rt] */ 9001da177e4SLinus Torvalds if (MIPSInst_RD(ir) == FPCREG_CSR) { 9011da177e4SLinus Torvalds value = ctx->fcr31; 90256a64733SRalf Baechle value = (value & ~FPU_CSR_RM) | modeindex(value); 90392df0f8bSRalf Baechle pr_debug("%p gpr[%d]<-csr=%08x\n", 904333d1f67SRalf Baechle (void *) (xcp->cp0_epc), 9051da177e4SLinus Torvalds MIPSInst_RT(ir), value); 9061da177e4SLinus Torvalds } 9071da177e4SLinus Torvalds else if (MIPSInst_RD(ir) == FPCREG_RID) 9081da177e4SLinus Torvalds value = 0; 9091da177e4SLinus Torvalds else 9101da177e4SLinus Torvalds value = 0; 9111da177e4SLinus Torvalds if (MIPSInst_RT(ir)) 9121da177e4SLinus Torvalds xcp->regs[MIPSInst_RT(ir)] = value; 9131da177e4SLinus Torvalds break; 9141da177e4SLinus Torvalds 9153f7cac41SRalf Baechle case ctc_op: 9161da177e4SLinus Torvalds /* copregister rd <- rt */ 9171da177e4SLinus Torvalds if (MIPSInst_RT(ir) == 0) 9181da177e4SLinus Torvalds value = 0; 9191da177e4SLinus Torvalds else 9201da177e4SLinus Torvalds value = xcp->regs[MIPSInst_RT(ir)]; 9211da177e4SLinus Torvalds 9221da177e4SLinus Torvalds /* we only have one writable control reg 9231da177e4SLinus Torvalds */ 9241da177e4SLinus Torvalds if (MIPSInst_RD(ir) == FPCREG_CSR) { 92592df0f8bSRalf Baechle pr_debug("%p gpr[%d]->csr=%08x\n", 926333d1f67SRalf Baechle (void *) (xcp->cp0_epc), 9271da177e4SLinus Torvalds MIPSInst_RT(ir), value); 92895e8f634SShane McDonald 92995e8f634SShane McDonald /* 93095e8f634SShane McDonald * Don't write reserved bits, 93195e8f634SShane McDonald * and convert to ieee library modes 93295e8f634SShane McDonald */ 93356a64733SRalf Baechle ctx->fcr31 = (value & ~(FPU_CSR_RSVD | FPU_CSR_RM)) | 93456a64733SRalf Baechle modeindex(value); 9351da177e4SLinus Torvalds } 9361da177e4SLinus Torvalds if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 9371da177e4SLinus Torvalds return SIGFPE; 9381da177e4SLinus Torvalds } 9391da177e4SLinus Torvalds break; 9401da177e4SLinus Torvalds 9413f7cac41SRalf Baechle case bc_op: 942e7e9cae5SRalf Baechle if (delay_slot(xcp)) 9431da177e4SLinus Torvalds return SIGILL; 9441da177e4SLinus Torvalds 94508a07904SRalf Baechle if (cpu_has_mips_4_5_r) 94608a07904SRalf Baechle cbit = fpucondbit[MIPSInst_RT(ir) >> 2]; 94708a07904SRalf Baechle else 94808a07904SRalf Baechle cbit = FPU_CSR_COND; 94908a07904SRalf Baechle cond = ctx->fcr31 & cbit; 95008a07904SRalf Baechle 9513f7cac41SRalf Baechle likely = 0; 9521da177e4SLinus Torvalds switch (MIPSInst_RT(ir) & 3) { 9531da177e4SLinus Torvalds case bcfl_op: 9541da177e4SLinus Torvalds likely = 1; 9551da177e4SLinus Torvalds case bcf_op: 9561da177e4SLinus Torvalds cond = !cond; 9571da177e4SLinus Torvalds break; 9581da177e4SLinus Torvalds case bctl_op: 9591da177e4SLinus Torvalds likely = 1; 9601da177e4SLinus Torvalds case bct_op: 9611da177e4SLinus Torvalds break; 9621da177e4SLinus Torvalds default: 9631da177e4SLinus Torvalds /* thats an illegal instruction */ 9641da177e4SLinus Torvalds return SIGILL; 9651da177e4SLinus Torvalds } 9661da177e4SLinus Torvalds 967e7e9cae5SRalf Baechle set_delay_slot(xcp); 9681da177e4SLinus Torvalds if (cond) { 9693f7cac41SRalf Baechle /* 9703f7cac41SRalf Baechle * Branch taken: emulate dslot instruction 9711da177e4SLinus Torvalds */ 972102cedc3SLeonid Yegoshin xcp->cp0_epc += dec_insn.pc_inc; 9731da177e4SLinus Torvalds 974102cedc3SLeonid Yegoshin contpc = MIPSInst_SIMM(ir); 975102cedc3SLeonid Yegoshin ir = dec_insn.next_insn; 976102cedc3SLeonid Yegoshin if (dec_insn.micro_mips_mode) { 977102cedc3SLeonid Yegoshin contpc = (xcp->cp0_epc + (contpc << 1)); 978102cedc3SLeonid Yegoshin 979102cedc3SLeonid Yegoshin /* If 16-bit instruction, not FPU. */ 980102cedc3SLeonid Yegoshin if ((dec_insn.next_pc_inc == 2) || 981102cedc3SLeonid Yegoshin (microMIPS32_to_MIPS32((union mips_instruction *)&ir) == SIGILL)) { 982102cedc3SLeonid Yegoshin 983102cedc3SLeonid Yegoshin /* 984102cedc3SLeonid Yegoshin * Since this instruction will 985102cedc3SLeonid Yegoshin * be put on the stack with 986102cedc3SLeonid Yegoshin * 32-bit words, get around 987102cedc3SLeonid Yegoshin * this problem by putting a 988102cedc3SLeonid Yegoshin * NOP16 as the second one. 989102cedc3SLeonid Yegoshin */ 990102cedc3SLeonid Yegoshin if (dec_insn.next_pc_inc == 2) 991102cedc3SLeonid Yegoshin ir = (ir & (~0xffff)) | MM_NOP16; 992102cedc3SLeonid Yegoshin 993102cedc3SLeonid Yegoshin /* 994102cedc3SLeonid Yegoshin * Single step the non-CP1 995102cedc3SLeonid Yegoshin * instruction in the dslot. 996102cedc3SLeonid Yegoshin */ 997102cedc3SLeonid Yegoshin return mips_dsemul(xcp, ir, contpc); 998515b029dSDavid Daney } 999102cedc3SLeonid Yegoshin } else 1000102cedc3SLeonid Yegoshin contpc = (xcp->cp0_epc + (contpc << 2)); 10011da177e4SLinus Torvalds 10021da177e4SLinus Torvalds switch (MIPSInst_OPCODE(ir)) { 10031da177e4SLinus Torvalds case lwc1_op: 100408a07904SRalf Baechle goto emul; 10053f7cac41SRalf Baechle 10061da177e4SLinus Torvalds case swc1_op: 100708a07904SRalf Baechle goto emul; 10083f7cac41SRalf Baechle 10091da177e4SLinus Torvalds case ldc1_op: 10101da177e4SLinus Torvalds case sdc1_op: 101108a07904SRalf Baechle if (cpu_has_mips_2_3_4_5 || 101208a07904SRalf Baechle cpu_has_mips64) 101308a07904SRalf Baechle goto emul; 101408a07904SRalf Baechle 101508a07904SRalf Baechle return SIGILL; 101608a07904SRalf Baechle goto emul; 10173f7cac41SRalf Baechle 10181da177e4SLinus Torvalds case cop1_op: 101908a07904SRalf Baechle goto emul; 10203f7cac41SRalf Baechle 10211da177e4SLinus Torvalds case cop1x_op: 102208a07904SRalf Baechle if (cpu_has_mips_4_5 || cpu_has_mips64) 10231da177e4SLinus Torvalds /* its one of ours */ 10241da177e4SLinus Torvalds goto emul; 102508a07904SRalf Baechle 102608a07904SRalf Baechle return SIGILL; 10273f7cac41SRalf Baechle 10281da177e4SLinus Torvalds case spec_op: 102908a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 103008a07904SRalf Baechle return SIGILL; 103108a07904SRalf Baechle 10321da177e4SLinus Torvalds if (MIPSInst_FUNC(ir) == movc_op) 10331da177e4SLinus Torvalds goto emul; 10341da177e4SLinus Torvalds break; 10351da177e4SLinus Torvalds } 10361da177e4SLinus Torvalds 10371da177e4SLinus Torvalds /* 10381da177e4SLinus Torvalds * Single step the non-cp1 10391da177e4SLinus Torvalds * instruction in the dslot 10401da177e4SLinus Torvalds */ 1041e70dfc10SAtsushi Nemoto return mips_dsemul(xcp, ir, contpc); 10423f7cac41SRalf Baechle } else if (likely) { /* branch not taken */ 10431da177e4SLinus Torvalds /* 10441da177e4SLinus Torvalds * branch likely nullifies 10451da177e4SLinus Torvalds * dslot if not taken 10461da177e4SLinus Torvalds */ 1047102cedc3SLeonid Yegoshin xcp->cp0_epc += dec_insn.pc_inc; 1048102cedc3SLeonid Yegoshin contpc += dec_insn.pc_inc; 10491da177e4SLinus Torvalds /* 10501da177e4SLinus Torvalds * else continue & execute 10511da177e4SLinus Torvalds * dslot as normal insn 10521da177e4SLinus Torvalds */ 10531da177e4SLinus Torvalds } 10541da177e4SLinus Torvalds break; 10551da177e4SLinus Torvalds 10561da177e4SLinus Torvalds default: 10571da177e4SLinus Torvalds if (!(MIPSInst_RS(ir) & 0x10)) 10581da177e4SLinus Torvalds return SIGILL; 10591da177e4SLinus Torvalds 10601da177e4SLinus Torvalds /* a real fpu computation instruction */ 10611da177e4SLinus Torvalds if ((sig = fpu_emu(xcp, ctx, ir))) 10621da177e4SLinus Torvalds return sig; 10631da177e4SLinus Torvalds } 10641da177e4SLinus Torvalds break; 10651da177e4SLinus Torvalds 10663f7cac41SRalf Baechle case cop1x_op: 106708a07904SRalf Baechle if (!cpu_has_mips_4_5 && !cpu_has_mips64) 106808a07904SRalf Baechle return SIGILL; 106908a07904SRalf Baechle 107008a07904SRalf Baechle sig = fpux_emu(xcp, ctx, ir, fault_addr); 1071515b029dSDavid Daney if (sig) 10721da177e4SLinus Torvalds return sig; 10731da177e4SLinus Torvalds break; 10741da177e4SLinus Torvalds 10751da177e4SLinus Torvalds case spec_op: 107608a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 107708a07904SRalf Baechle return SIGILL; 107808a07904SRalf Baechle 10791da177e4SLinus Torvalds if (MIPSInst_FUNC(ir) != movc_op) 10801da177e4SLinus Torvalds return SIGILL; 10811da177e4SLinus Torvalds cond = fpucondbit[MIPSInst_RT(ir) >> 2]; 10821da177e4SLinus Torvalds if (((ctx->fcr31 & cond) != 0) == ((MIPSInst_RT(ir) & 1) != 0)) 10831da177e4SLinus Torvalds xcp->regs[MIPSInst_RD(ir)] = 10841da177e4SLinus Torvalds xcp->regs[MIPSInst_RS(ir)]; 10851da177e4SLinus Torvalds break; 10861da177e4SLinus Torvalds default: 10871ac94400SLeonid Yegoshin sigill: 10881da177e4SLinus Torvalds return SIGILL; 10891da177e4SLinus Torvalds } 10901da177e4SLinus Torvalds 10911da177e4SLinus Torvalds /* we did it !! */ 1092e70dfc10SAtsushi Nemoto xcp->cp0_epc = contpc; 1093e7e9cae5SRalf Baechle clear_delay_slot(xcp); 1094333d1f67SRalf Baechle 10951da177e4SLinus Torvalds return 0; 10961da177e4SLinus Torvalds } 10971da177e4SLinus Torvalds 10981da177e4SLinus Torvalds /* 10991da177e4SLinus Torvalds * Conversion table from MIPS compare ops 48-63 11001da177e4SLinus Torvalds * cond = ieee754dp_cmp(x,y,IEEE754_UN,sig); 11011da177e4SLinus Torvalds */ 11021da177e4SLinus Torvalds static const unsigned char cmptab[8] = { 11031da177e4SLinus Torvalds 0, /* cmp_0 (sig) cmp_sf */ 11041da177e4SLinus Torvalds IEEE754_CUN, /* cmp_un (sig) cmp_ngle */ 11051da177e4SLinus Torvalds IEEE754_CEQ, /* cmp_eq (sig) cmp_seq */ 11061da177e4SLinus Torvalds IEEE754_CEQ | IEEE754_CUN, /* cmp_ueq (sig) cmp_ngl */ 11071da177e4SLinus Torvalds IEEE754_CLT, /* cmp_olt (sig) cmp_lt */ 11081da177e4SLinus Torvalds IEEE754_CLT | IEEE754_CUN, /* cmp_ult (sig) cmp_nge */ 11091da177e4SLinus Torvalds IEEE754_CLT | IEEE754_CEQ, /* cmp_ole (sig) cmp_le */ 11101da177e4SLinus Torvalds IEEE754_CLT | IEEE754_CEQ | IEEE754_CUN, /* cmp_ule (sig) cmp_ngt */ 11111da177e4SLinus Torvalds }; 11121da177e4SLinus Torvalds 11131da177e4SLinus Torvalds 11141da177e4SLinus Torvalds /* 11151da177e4SLinus Torvalds * Additional MIPS4 instructions 11161da177e4SLinus Torvalds */ 11171da177e4SLinus Torvalds 11181da177e4SLinus Torvalds #define DEF3OP(name, p, f1, f2, f3) \ 111947fa0c02SRalf Baechle static union ieee754##p fpemu_##p##_##name(union ieee754##p r, \ 112047fa0c02SRalf Baechle union ieee754##p s, union ieee754##p t) \ 11211da177e4SLinus Torvalds { \ 1122cd21dfcfSRalf Baechle struct _ieee754_csr ieee754_csr_save; \ 11231da177e4SLinus Torvalds s = f1(s, t); \ 11241da177e4SLinus Torvalds ieee754_csr_save = ieee754_csr; \ 11251da177e4SLinus Torvalds s = f2(s, r); \ 11261da177e4SLinus Torvalds ieee754_csr_save.cx |= ieee754_csr.cx; \ 11271da177e4SLinus Torvalds ieee754_csr_save.sx |= ieee754_csr.sx; \ 11281da177e4SLinus Torvalds s = f3(s); \ 11291da177e4SLinus Torvalds ieee754_csr.cx |= ieee754_csr_save.cx; \ 11301da177e4SLinus Torvalds ieee754_csr.sx |= ieee754_csr_save.sx; \ 11311da177e4SLinus Torvalds return s; \ 11321da177e4SLinus Torvalds } 11331da177e4SLinus Torvalds 11342209bcb1SRalf Baechle static union ieee754dp fpemu_dp_recip(union ieee754dp d) 11351da177e4SLinus Torvalds { 11361da177e4SLinus Torvalds return ieee754dp_div(ieee754dp_one(0), d); 11371da177e4SLinus Torvalds } 11381da177e4SLinus Torvalds 11392209bcb1SRalf Baechle static union ieee754dp fpemu_dp_rsqrt(union ieee754dp d) 11401da177e4SLinus Torvalds { 11411da177e4SLinus Torvalds return ieee754dp_div(ieee754dp_one(0), ieee754dp_sqrt(d)); 11421da177e4SLinus Torvalds } 11431da177e4SLinus Torvalds 11442209bcb1SRalf Baechle static union ieee754sp fpemu_sp_recip(union ieee754sp s) 11451da177e4SLinus Torvalds { 11461da177e4SLinus Torvalds return ieee754sp_div(ieee754sp_one(0), s); 11471da177e4SLinus Torvalds } 11481da177e4SLinus Torvalds 11492209bcb1SRalf Baechle static union ieee754sp fpemu_sp_rsqrt(union ieee754sp s) 11501da177e4SLinus Torvalds { 11511da177e4SLinus Torvalds return ieee754sp_div(ieee754sp_one(0), ieee754sp_sqrt(s)); 11521da177e4SLinus Torvalds } 11531da177e4SLinus Torvalds 11541da177e4SLinus Torvalds DEF3OP(madd, sp, ieee754sp_mul, ieee754sp_add, ); 11551da177e4SLinus Torvalds DEF3OP(msub, sp, ieee754sp_mul, ieee754sp_sub, ); 11561da177e4SLinus Torvalds DEF3OP(nmadd, sp, ieee754sp_mul, ieee754sp_add, ieee754sp_neg); 11571da177e4SLinus Torvalds DEF3OP(nmsub, sp, ieee754sp_mul, ieee754sp_sub, ieee754sp_neg); 11581da177e4SLinus Torvalds DEF3OP(madd, dp, ieee754dp_mul, ieee754dp_add, ); 11591da177e4SLinus Torvalds DEF3OP(msub, dp, ieee754dp_mul, ieee754dp_sub, ); 11601da177e4SLinus Torvalds DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg); 11611da177e4SLinus Torvalds DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg); 11621da177e4SLinus Torvalds 1163eae89076SAtsushi Nemoto static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 1164515b029dSDavid Daney mips_instruction ir, void *__user *fault_addr) 11651da177e4SLinus Torvalds { 11661da177e4SLinus Torvalds unsigned rcsr = 0; /* resulting csr */ 11671da177e4SLinus Torvalds 1168b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(cp1xops); 11691da177e4SLinus Torvalds 11701da177e4SLinus Torvalds switch (MIPSInst_FMA_FFMT(ir)) { 11711da177e4SLinus Torvalds case s_fmt:{ /* 0 */ 11721da177e4SLinus Torvalds 11732209bcb1SRalf Baechle union ieee754sp(*handler) (union ieee754sp, union ieee754sp, union ieee754sp); 11742209bcb1SRalf Baechle union ieee754sp fd, fr, fs, ft; 11753fccc015SRalf Baechle u32 __user *va; 11761da177e4SLinus Torvalds u32 val; 11771da177e4SLinus Torvalds 11781da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 11791da177e4SLinus Torvalds case lwxc1_op: 11803fccc015SRalf Baechle va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 11811da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 11821da177e4SLinus Torvalds 1183b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(loads); 1184515b029dSDavid Daney if (!access_ok(VERIFY_READ, va, sizeof(u32))) { 1185b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1186515b029dSDavid Daney *fault_addr = va; 11871da177e4SLinus Torvalds return SIGBUS; 11881da177e4SLinus Torvalds } 1189515b029dSDavid Daney if (__get_user(val, va)) { 1190515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1191515b029dSDavid Daney *fault_addr = va; 1192515b029dSDavid Daney return SIGSEGV; 1193515b029dSDavid Daney } 11941da177e4SLinus Torvalds SITOREG(val, MIPSInst_FD(ir)); 11951da177e4SLinus Torvalds break; 11961da177e4SLinus Torvalds 11971da177e4SLinus Torvalds case swxc1_op: 11983fccc015SRalf Baechle va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 11991da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 12001da177e4SLinus Torvalds 1201b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(stores); 12021da177e4SLinus Torvalds 12031da177e4SLinus Torvalds SIFROMREG(val, MIPSInst_FS(ir)); 1204515b029dSDavid Daney if (!access_ok(VERIFY_WRITE, va, sizeof(u32))) { 1205515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1206515b029dSDavid Daney *fault_addr = va; 1207515b029dSDavid Daney return SIGBUS; 1208515b029dSDavid Daney } 12091da177e4SLinus Torvalds if (put_user(val, va)) { 1210b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1211515b029dSDavid Daney *fault_addr = va; 1212515b029dSDavid Daney return SIGSEGV; 12131da177e4SLinus Torvalds } 12141da177e4SLinus Torvalds break; 12151da177e4SLinus Torvalds 12161da177e4SLinus Torvalds case madd_s_op: 12171da177e4SLinus Torvalds handler = fpemu_sp_madd; 12181da177e4SLinus Torvalds goto scoptop; 12191da177e4SLinus Torvalds case msub_s_op: 12201da177e4SLinus Torvalds handler = fpemu_sp_msub; 12211da177e4SLinus Torvalds goto scoptop; 12221da177e4SLinus Torvalds case nmadd_s_op: 12231da177e4SLinus Torvalds handler = fpemu_sp_nmadd; 12241da177e4SLinus Torvalds goto scoptop; 12251da177e4SLinus Torvalds case nmsub_s_op: 12261da177e4SLinus Torvalds handler = fpemu_sp_nmsub; 12271da177e4SLinus Torvalds goto scoptop; 12281da177e4SLinus Torvalds 12291da177e4SLinus Torvalds scoptop: 12301da177e4SLinus Torvalds SPFROMREG(fr, MIPSInst_FR(ir)); 12311da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 12321da177e4SLinus Torvalds SPFROMREG(ft, MIPSInst_FT(ir)); 12331da177e4SLinus Torvalds fd = (*handler) (fr, fs, ft); 12341da177e4SLinus Torvalds SPTOREG(fd, MIPSInst_FD(ir)); 12351da177e4SLinus Torvalds 12361da177e4SLinus Torvalds copcsr: 12371da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_INEXACT)) 12381da177e4SLinus Torvalds rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S; 12391da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_UNDERFLOW)) 12401da177e4SLinus Torvalds rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S; 12411da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_OVERFLOW)) 12421da177e4SLinus Torvalds rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S; 12431da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_INVALID_OPERATION)) 12441da177e4SLinus Torvalds rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S; 12451da177e4SLinus Torvalds 12461da177e4SLinus Torvalds ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr; 12471da177e4SLinus Torvalds if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 12483f7cac41SRalf Baechle /*printk ("SIGFPE: FPU csr = %08x\n", 12491da177e4SLinus Torvalds ctx->fcr31); */ 12501da177e4SLinus Torvalds return SIGFPE; 12511da177e4SLinus Torvalds } 12521da177e4SLinus Torvalds 12531da177e4SLinus Torvalds break; 12541da177e4SLinus Torvalds 12551da177e4SLinus Torvalds default: 12561da177e4SLinus Torvalds return SIGILL; 12571da177e4SLinus Torvalds } 12581da177e4SLinus Torvalds break; 12591da177e4SLinus Torvalds } 12601da177e4SLinus Torvalds 12611da177e4SLinus Torvalds case d_fmt:{ /* 1 */ 12622209bcb1SRalf Baechle union ieee754dp(*handler) (union ieee754dp, union ieee754dp, union ieee754dp); 12632209bcb1SRalf Baechle union ieee754dp fd, fr, fs, ft; 12643fccc015SRalf Baechle u64 __user *va; 12651da177e4SLinus Torvalds u64 val; 12661da177e4SLinus Torvalds 12671da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 12681da177e4SLinus Torvalds case ldxc1_op: 12693fccc015SRalf Baechle va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 12701da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 12711da177e4SLinus Torvalds 1272b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(loads); 1273515b029dSDavid Daney if (!access_ok(VERIFY_READ, va, sizeof(u64))) { 1274b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1275515b029dSDavid Daney *fault_addr = va; 12761da177e4SLinus Torvalds return SIGBUS; 12771da177e4SLinus Torvalds } 1278515b029dSDavid Daney if (__get_user(val, va)) { 1279515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1280515b029dSDavid Daney *fault_addr = va; 1281515b029dSDavid Daney return SIGSEGV; 1282515b029dSDavid Daney } 12831da177e4SLinus Torvalds DITOREG(val, MIPSInst_FD(ir)); 12841da177e4SLinus Torvalds break; 12851da177e4SLinus Torvalds 12861da177e4SLinus Torvalds case sdxc1_op: 12873fccc015SRalf Baechle va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 12881da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 12891da177e4SLinus Torvalds 1290b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(stores); 12911da177e4SLinus Torvalds DIFROMREG(val, MIPSInst_FS(ir)); 1292515b029dSDavid Daney if (!access_ok(VERIFY_WRITE, va, sizeof(u64))) { 1293b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1294515b029dSDavid Daney *fault_addr = va; 12951da177e4SLinus Torvalds return SIGBUS; 12961da177e4SLinus Torvalds } 1297515b029dSDavid Daney if (__put_user(val, va)) { 1298515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1299515b029dSDavid Daney *fault_addr = va; 1300515b029dSDavid Daney return SIGSEGV; 1301515b029dSDavid Daney } 13021da177e4SLinus Torvalds break; 13031da177e4SLinus Torvalds 13041da177e4SLinus Torvalds case madd_d_op: 13051da177e4SLinus Torvalds handler = fpemu_dp_madd; 13061da177e4SLinus Torvalds goto dcoptop; 13071da177e4SLinus Torvalds case msub_d_op: 13081da177e4SLinus Torvalds handler = fpemu_dp_msub; 13091da177e4SLinus Torvalds goto dcoptop; 13101da177e4SLinus Torvalds case nmadd_d_op: 13111da177e4SLinus Torvalds handler = fpemu_dp_nmadd; 13121da177e4SLinus Torvalds goto dcoptop; 13131da177e4SLinus Torvalds case nmsub_d_op: 13141da177e4SLinus Torvalds handler = fpemu_dp_nmsub; 13151da177e4SLinus Torvalds goto dcoptop; 13161da177e4SLinus Torvalds 13171da177e4SLinus Torvalds dcoptop: 13181da177e4SLinus Torvalds DPFROMREG(fr, MIPSInst_FR(ir)); 13191da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 13201da177e4SLinus Torvalds DPFROMREG(ft, MIPSInst_FT(ir)); 13211da177e4SLinus Torvalds fd = (*handler) (fr, fs, ft); 13221da177e4SLinus Torvalds DPTOREG(fd, MIPSInst_FD(ir)); 13231da177e4SLinus Torvalds goto copcsr; 13241da177e4SLinus Torvalds 13251da177e4SLinus Torvalds default: 13261da177e4SLinus Torvalds return SIGILL; 13271da177e4SLinus Torvalds } 13281da177e4SLinus Torvalds break; 13291da177e4SLinus Torvalds } 13301da177e4SLinus Torvalds 133151061b88SDeng-Cheng Zhu case 0x3: 133251061b88SDeng-Cheng Zhu if (MIPSInst_FUNC(ir) != pfetch_op) 13331da177e4SLinus Torvalds return SIGILL; 133451061b88SDeng-Cheng Zhu 13351da177e4SLinus Torvalds /* ignore prefx operation */ 13361da177e4SLinus Torvalds break; 13371da177e4SLinus Torvalds 13381da177e4SLinus Torvalds default: 13391da177e4SLinus Torvalds return SIGILL; 13401da177e4SLinus Torvalds } 13411da177e4SLinus Torvalds 13421da177e4SLinus Torvalds return 0; 13431da177e4SLinus Torvalds } 13441da177e4SLinus Torvalds 13451da177e4SLinus Torvalds 13461da177e4SLinus Torvalds 13471da177e4SLinus Torvalds /* 13481da177e4SLinus Torvalds * Emulate a single COP1 arithmetic instruction. 13491da177e4SLinus Torvalds */ 1350eae89076SAtsushi Nemoto static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 13511da177e4SLinus Torvalds mips_instruction ir) 13521da177e4SLinus Torvalds { 13531da177e4SLinus Torvalds int rfmt; /* resulting format */ 13541da177e4SLinus Torvalds unsigned rcsr = 0; /* resulting csr */ 13553f7cac41SRalf Baechle unsigned int oldrm; 13563f7cac41SRalf Baechle unsigned int cbit; 13571da177e4SLinus Torvalds unsigned cond; 13581da177e4SLinus Torvalds union { 13592209bcb1SRalf Baechle union ieee754dp d; 13602209bcb1SRalf Baechle union ieee754sp s; 13611da177e4SLinus Torvalds int w; 13621da177e4SLinus Torvalds s64 l; 13631da177e4SLinus Torvalds } rv; /* resulting value */ 13643f7cac41SRalf Baechle u64 bits; 13651da177e4SLinus Torvalds 1366b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(cp1ops); 13671da177e4SLinus Torvalds switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) { 13681da177e4SLinus Torvalds case s_fmt: { /* 0 */ 13691da177e4SLinus Torvalds union { 13702209bcb1SRalf Baechle union ieee754sp(*b) (union ieee754sp, union ieee754sp); 13712209bcb1SRalf Baechle union ieee754sp(*u) (union ieee754sp); 13721da177e4SLinus Torvalds } handler; 13733f7cac41SRalf Baechle union ieee754sp fs, ft; 13741da177e4SLinus Torvalds 13751da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 13761da177e4SLinus Torvalds /* binary ops */ 13771da177e4SLinus Torvalds case fadd_op: 13781da177e4SLinus Torvalds handler.b = ieee754sp_add; 13791da177e4SLinus Torvalds goto scopbop; 13801da177e4SLinus Torvalds case fsub_op: 13811da177e4SLinus Torvalds handler.b = ieee754sp_sub; 13821da177e4SLinus Torvalds goto scopbop; 13831da177e4SLinus Torvalds case fmul_op: 13841da177e4SLinus Torvalds handler.b = ieee754sp_mul; 13851da177e4SLinus Torvalds goto scopbop; 13861da177e4SLinus Torvalds case fdiv_op: 13871da177e4SLinus Torvalds handler.b = ieee754sp_div; 13881da177e4SLinus Torvalds goto scopbop; 13891da177e4SLinus Torvalds 13901da177e4SLinus Torvalds /* unary ops */ 13911da177e4SLinus Torvalds case fsqrt_op: 139208a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 139308a07904SRalf Baechle return SIGILL; 139408a07904SRalf Baechle 13951da177e4SLinus Torvalds handler.u = ieee754sp_sqrt; 13961da177e4SLinus Torvalds goto scopuop; 13973f7cac41SRalf Baechle 139808a07904SRalf Baechle /* 139908a07904SRalf Baechle * Note that on some MIPS IV implementations such as the 140008a07904SRalf Baechle * R5000 and R8000 the FSQRT and FRECIP instructions do not 140108a07904SRalf Baechle * achieve full IEEE-754 accuracy - however this emulator does. 140208a07904SRalf Baechle */ 14031da177e4SLinus Torvalds case frsqrt_op: 140408a07904SRalf Baechle if (!cpu_has_mips_4_5_r2) 140508a07904SRalf Baechle return SIGILL; 140608a07904SRalf Baechle 14071da177e4SLinus Torvalds handler.u = fpemu_sp_rsqrt; 14081da177e4SLinus Torvalds goto scopuop; 14093f7cac41SRalf Baechle 14101da177e4SLinus Torvalds case frecip_op: 141108a07904SRalf Baechle if (!cpu_has_mips_4_5_r2) 141208a07904SRalf Baechle return SIGILL; 141308a07904SRalf Baechle 14141da177e4SLinus Torvalds handler.u = fpemu_sp_recip; 14151da177e4SLinus Torvalds goto scopuop; 141608a07904SRalf Baechle 14171da177e4SLinus Torvalds case fmovc_op: 141808a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 141908a07904SRalf Baechle return SIGILL; 142008a07904SRalf Baechle 14211da177e4SLinus Torvalds cond = fpucondbit[MIPSInst_FT(ir) >> 2]; 14221da177e4SLinus Torvalds if (((ctx->fcr31 & cond) != 0) != 14231da177e4SLinus Torvalds ((MIPSInst_FT(ir) & 1) != 0)) 14241da177e4SLinus Torvalds return 0; 14251da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 14261da177e4SLinus Torvalds break; 14273f7cac41SRalf Baechle 14281da177e4SLinus Torvalds case fmovz_op: 142908a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 143008a07904SRalf Baechle return SIGILL; 143108a07904SRalf Baechle 14321da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] != 0) 14331da177e4SLinus Torvalds return 0; 14341da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 14351da177e4SLinus Torvalds break; 14363f7cac41SRalf Baechle 14371da177e4SLinus Torvalds case fmovn_op: 143808a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 143908a07904SRalf Baechle return SIGILL; 144008a07904SRalf Baechle 14411da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] == 0) 14421da177e4SLinus Torvalds return 0; 14431da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 14441da177e4SLinus Torvalds break; 14453f7cac41SRalf Baechle 14461da177e4SLinus Torvalds case fabs_op: 14471da177e4SLinus Torvalds handler.u = ieee754sp_abs; 14481da177e4SLinus Torvalds goto scopuop; 14493f7cac41SRalf Baechle 14501da177e4SLinus Torvalds case fneg_op: 14511da177e4SLinus Torvalds handler.u = ieee754sp_neg; 14521da177e4SLinus Torvalds goto scopuop; 14533f7cac41SRalf Baechle 14541da177e4SLinus Torvalds case fmov_op: 14551da177e4SLinus Torvalds /* an easy one */ 14561da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 14571da177e4SLinus Torvalds goto copcsr; 14581da177e4SLinus Torvalds 14591da177e4SLinus Torvalds /* binary op on handler */ 14601da177e4SLinus Torvalds scopbop: 14611da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 14621da177e4SLinus Torvalds SPFROMREG(ft, MIPSInst_FT(ir)); 14631da177e4SLinus Torvalds 14641da177e4SLinus Torvalds rv.s = (*handler.b) (fs, ft); 14651da177e4SLinus Torvalds goto copcsr; 14661da177e4SLinus Torvalds scopuop: 14671da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 14681da177e4SLinus Torvalds rv.s = (*handler.u) (fs); 14691da177e4SLinus Torvalds goto copcsr; 14701da177e4SLinus Torvalds copcsr: 14711da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_INEXACT)) 14721da177e4SLinus Torvalds rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S; 14731da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_UNDERFLOW)) 14741da177e4SLinus Torvalds rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S; 14751da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_OVERFLOW)) 14761da177e4SLinus Torvalds rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S; 14771da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_ZERO_DIVIDE)) 14781da177e4SLinus Torvalds rcsr |= FPU_CSR_DIV_X | FPU_CSR_DIV_S; 14791da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_INVALID_OPERATION)) 14801da177e4SLinus Torvalds rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S; 14811da177e4SLinus Torvalds break; 14821da177e4SLinus Torvalds 14831da177e4SLinus Torvalds /* unary conv ops */ 14841da177e4SLinus Torvalds case fcvts_op: 14851da177e4SLinus Torvalds return SIGILL; /* not defined */ 14861da177e4SLinus Torvalds 14873f7cac41SRalf Baechle case fcvtd_op: 14881da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 14891da177e4SLinus Torvalds rv.d = ieee754dp_fsp(fs); 14901da177e4SLinus Torvalds rfmt = d_fmt; 14911da177e4SLinus Torvalds goto copcsr; 14921da177e4SLinus Torvalds 14933f7cac41SRalf Baechle case fcvtw_op: 14941da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 14951da177e4SLinus Torvalds rv.w = ieee754sp_tint(fs); 14961da177e4SLinus Torvalds rfmt = w_fmt; 14971da177e4SLinus Torvalds goto copcsr; 14981da177e4SLinus Torvalds 14991da177e4SLinus Torvalds case fround_op: 15001da177e4SLinus Torvalds case ftrunc_op: 15011da177e4SLinus Torvalds case fceil_op: 15023f7cac41SRalf Baechle case ffloor_op: 150308a07904SRalf Baechle if (!cpu_has_mips_2_3_4_5 && !cpu_has_mips64) 150408a07904SRalf Baechle return SIGILL; 150508a07904SRalf Baechle 15063f7cac41SRalf Baechle oldrm = ieee754_csr.rm; 15071da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 150856a64733SRalf Baechle ieee754_csr.rm = modeindex(MIPSInst_FUNC(ir)); 15091da177e4SLinus Torvalds rv.w = ieee754sp_tint(fs); 15101da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 15111da177e4SLinus Torvalds rfmt = w_fmt; 15121da177e4SLinus Torvalds goto copcsr; 15131da177e4SLinus Torvalds 15143f7cac41SRalf Baechle case fcvtl_op: 151508a07904SRalf Baechle if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) 151608a07904SRalf Baechle return SIGILL; 151708a07904SRalf Baechle 15181da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 15191da177e4SLinus Torvalds rv.l = ieee754sp_tlong(fs); 15201da177e4SLinus Torvalds rfmt = l_fmt; 15211da177e4SLinus Torvalds goto copcsr; 15221da177e4SLinus Torvalds 15231da177e4SLinus Torvalds case froundl_op: 15241da177e4SLinus Torvalds case ftruncl_op: 15251da177e4SLinus Torvalds case fceill_op: 15263f7cac41SRalf Baechle case ffloorl_op: 152708a07904SRalf Baechle if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) 152808a07904SRalf Baechle return SIGILL; 152908a07904SRalf Baechle 15303f7cac41SRalf Baechle oldrm = ieee754_csr.rm; 15311da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 153256a64733SRalf Baechle ieee754_csr.rm = modeindex(MIPSInst_FUNC(ir)); 15331da177e4SLinus Torvalds rv.l = ieee754sp_tlong(fs); 15341da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 15351da177e4SLinus Torvalds rfmt = l_fmt; 15361da177e4SLinus Torvalds goto copcsr; 15371da177e4SLinus Torvalds 15381da177e4SLinus Torvalds default: 15391da177e4SLinus Torvalds if (MIPSInst_FUNC(ir) >= fcmp_op) { 15401da177e4SLinus Torvalds unsigned cmpop = MIPSInst_FUNC(ir) - fcmp_op; 15412209bcb1SRalf Baechle union ieee754sp fs, ft; 15421da177e4SLinus Torvalds 15431da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 15441da177e4SLinus Torvalds SPFROMREG(ft, MIPSInst_FT(ir)); 15451da177e4SLinus Torvalds rv.w = ieee754sp_cmp(fs, ft, 15461da177e4SLinus Torvalds cmptab[cmpop & 0x7], cmpop & 0x8); 15471da177e4SLinus Torvalds rfmt = -1; 15481da177e4SLinus Torvalds if ((cmpop & 0x8) && ieee754_cxtest 15491da177e4SLinus Torvalds (IEEE754_INVALID_OPERATION)) 15501da177e4SLinus Torvalds rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 15511da177e4SLinus Torvalds else 15521da177e4SLinus Torvalds goto copcsr; 15531da177e4SLinus Torvalds 15543f7cac41SRalf Baechle } else 15551da177e4SLinus Torvalds return SIGILL; 15561da177e4SLinus Torvalds break; 15571da177e4SLinus Torvalds } 15581da177e4SLinus Torvalds break; 15591da177e4SLinus Torvalds } 15601da177e4SLinus Torvalds 15611da177e4SLinus Torvalds case d_fmt: { 15623f7cac41SRalf Baechle union ieee754dp fs, ft; 15631da177e4SLinus Torvalds union { 15642209bcb1SRalf Baechle union ieee754dp(*b) (union ieee754dp, union ieee754dp); 15652209bcb1SRalf Baechle union ieee754dp(*u) (union ieee754dp); 15661da177e4SLinus Torvalds } handler; 15671da177e4SLinus Torvalds 15681da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 15691da177e4SLinus Torvalds /* binary ops */ 15701da177e4SLinus Torvalds case fadd_op: 15711da177e4SLinus Torvalds handler.b = ieee754dp_add; 15721da177e4SLinus Torvalds goto dcopbop; 15731da177e4SLinus Torvalds case fsub_op: 15741da177e4SLinus Torvalds handler.b = ieee754dp_sub; 15751da177e4SLinus Torvalds goto dcopbop; 15761da177e4SLinus Torvalds case fmul_op: 15771da177e4SLinus Torvalds handler.b = ieee754dp_mul; 15781da177e4SLinus Torvalds goto dcopbop; 15791da177e4SLinus Torvalds case fdiv_op: 15801da177e4SLinus Torvalds handler.b = ieee754dp_div; 15811da177e4SLinus Torvalds goto dcopbop; 15821da177e4SLinus Torvalds 15831da177e4SLinus Torvalds /* unary ops */ 15841da177e4SLinus Torvalds case fsqrt_op: 158508a07904SRalf Baechle if (!cpu_has_mips_2_3_4_5_r) 158608a07904SRalf Baechle return SIGILL; 158708a07904SRalf Baechle 15881da177e4SLinus Torvalds handler.u = ieee754dp_sqrt; 15891da177e4SLinus Torvalds goto dcopuop; 159008a07904SRalf Baechle /* 159108a07904SRalf Baechle * Note that on some MIPS IV implementations such as the 159208a07904SRalf Baechle * R5000 and R8000 the FSQRT and FRECIP instructions do not 159308a07904SRalf Baechle * achieve full IEEE-754 accuracy - however this emulator does. 159408a07904SRalf Baechle */ 15951da177e4SLinus Torvalds case frsqrt_op: 159608a07904SRalf Baechle if (!cpu_has_mips_4_5_r2) 159708a07904SRalf Baechle return SIGILL; 159808a07904SRalf Baechle 15991da177e4SLinus Torvalds handler.u = fpemu_dp_rsqrt; 16001da177e4SLinus Torvalds goto dcopuop; 16011da177e4SLinus Torvalds case frecip_op: 160208a07904SRalf Baechle if (!cpu_has_mips_4_5_r2) 160308a07904SRalf Baechle return SIGILL; 160408a07904SRalf Baechle 16051da177e4SLinus Torvalds handler.u = fpemu_dp_recip; 16061da177e4SLinus Torvalds goto dcopuop; 16071da177e4SLinus Torvalds case fmovc_op: 160808a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 160908a07904SRalf Baechle return SIGILL; 161008a07904SRalf Baechle 16111da177e4SLinus Torvalds cond = fpucondbit[MIPSInst_FT(ir) >> 2]; 16121da177e4SLinus Torvalds if (((ctx->fcr31 & cond) != 0) != 16131da177e4SLinus Torvalds ((MIPSInst_FT(ir) & 1) != 0)) 16141da177e4SLinus Torvalds return 0; 16151da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 16161da177e4SLinus Torvalds break; 16171da177e4SLinus Torvalds case fmovz_op: 161808a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 161908a07904SRalf Baechle return SIGILL; 162008a07904SRalf Baechle 16211da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] != 0) 16221da177e4SLinus Torvalds return 0; 16231da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 16241da177e4SLinus Torvalds break; 16251da177e4SLinus Torvalds case fmovn_op: 162608a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 162708a07904SRalf Baechle return SIGILL; 162808a07904SRalf Baechle 16291da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] == 0) 16301da177e4SLinus Torvalds return 0; 16311da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 16321da177e4SLinus Torvalds break; 16331da177e4SLinus Torvalds case fabs_op: 16341da177e4SLinus Torvalds handler.u = ieee754dp_abs; 16351da177e4SLinus Torvalds goto dcopuop; 16361da177e4SLinus Torvalds 16371da177e4SLinus Torvalds case fneg_op: 16381da177e4SLinus Torvalds handler.u = ieee754dp_neg; 16391da177e4SLinus Torvalds goto dcopuop; 16401da177e4SLinus Torvalds 16411da177e4SLinus Torvalds case fmov_op: 16421da177e4SLinus Torvalds /* an easy one */ 16431da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 16441da177e4SLinus Torvalds goto copcsr; 16451da177e4SLinus Torvalds 16461da177e4SLinus Torvalds /* binary op on handler */ 16473f7cac41SRalf Baechle dcopbop: 16481da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 16491da177e4SLinus Torvalds DPFROMREG(ft, MIPSInst_FT(ir)); 16501da177e4SLinus Torvalds 16511da177e4SLinus Torvalds rv.d = (*handler.b) (fs, ft); 16521da177e4SLinus Torvalds goto copcsr; 16533f7cac41SRalf Baechle dcopuop: 16541da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 16551da177e4SLinus Torvalds rv.d = (*handler.u) (fs); 16561da177e4SLinus Torvalds goto copcsr; 16571da177e4SLinus Torvalds 16583f7cac41SRalf Baechle /* 16593f7cac41SRalf Baechle * unary conv ops 16603f7cac41SRalf Baechle */ 16613f7cac41SRalf Baechle case fcvts_op: 16621da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 16631da177e4SLinus Torvalds rv.s = ieee754sp_fdp(fs); 16641da177e4SLinus Torvalds rfmt = s_fmt; 16651da177e4SLinus Torvalds goto copcsr; 16663f7cac41SRalf Baechle 16671da177e4SLinus Torvalds case fcvtd_op: 16681da177e4SLinus Torvalds return SIGILL; /* not defined */ 16691da177e4SLinus Torvalds 16703f7cac41SRalf Baechle case fcvtw_op: 16711da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 16721da177e4SLinus Torvalds rv.w = ieee754dp_tint(fs); /* wrong */ 16731da177e4SLinus Torvalds rfmt = w_fmt; 16741da177e4SLinus Torvalds goto copcsr; 16751da177e4SLinus Torvalds 16761da177e4SLinus Torvalds case fround_op: 16771da177e4SLinus Torvalds case ftrunc_op: 16781da177e4SLinus Torvalds case fceil_op: 16793f7cac41SRalf Baechle case ffloor_op: 168008a07904SRalf Baechle if (!cpu_has_mips_2_3_4_5_r) 168108a07904SRalf Baechle return SIGILL; 168208a07904SRalf Baechle 16833f7cac41SRalf Baechle oldrm = ieee754_csr.rm; 16841da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 168556a64733SRalf Baechle ieee754_csr.rm = modeindex(MIPSInst_FUNC(ir)); 16861da177e4SLinus Torvalds rv.w = ieee754dp_tint(fs); 16871da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 16881da177e4SLinus Torvalds rfmt = w_fmt; 16891da177e4SLinus Torvalds goto copcsr; 16901da177e4SLinus Torvalds 16913f7cac41SRalf Baechle case fcvtl_op: 169208a07904SRalf Baechle if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) 169308a07904SRalf Baechle return SIGILL; 169408a07904SRalf Baechle 16951da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 16961da177e4SLinus Torvalds rv.l = ieee754dp_tlong(fs); 16971da177e4SLinus Torvalds rfmt = l_fmt; 16981da177e4SLinus Torvalds goto copcsr; 16991da177e4SLinus Torvalds 17001da177e4SLinus Torvalds case froundl_op: 17011da177e4SLinus Torvalds case ftruncl_op: 17021da177e4SLinus Torvalds case fceill_op: 17033f7cac41SRalf Baechle case ffloorl_op: 170408a07904SRalf Baechle if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) 170508a07904SRalf Baechle return SIGILL; 170608a07904SRalf Baechle 17073f7cac41SRalf Baechle oldrm = ieee754_csr.rm; 17081da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 170956a64733SRalf Baechle ieee754_csr.rm = modeindex(MIPSInst_FUNC(ir)); 17101da177e4SLinus Torvalds rv.l = ieee754dp_tlong(fs); 17111da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 17121da177e4SLinus Torvalds rfmt = l_fmt; 17131da177e4SLinus Torvalds goto copcsr; 17141da177e4SLinus Torvalds 17151da177e4SLinus Torvalds default: 17161da177e4SLinus Torvalds if (MIPSInst_FUNC(ir) >= fcmp_op) { 17171da177e4SLinus Torvalds unsigned cmpop = MIPSInst_FUNC(ir) - fcmp_op; 17182209bcb1SRalf Baechle union ieee754dp fs, ft; 17191da177e4SLinus Torvalds 17201da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 17211da177e4SLinus Torvalds DPFROMREG(ft, MIPSInst_FT(ir)); 17221da177e4SLinus Torvalds rv.w = ieee754dp_cmp(fs, ft, 17231da177e4SLinus Torvalds cmptab[cmpop & 0x7], cmpop & 0x8); 17241da177e4SLinus Torvalds rfmt = -1; 17251da177e4SLinus Torvalds if ((cmpop & 0x8) 17261da177e4SLinus Torvalds && 17271da177e4SLinus Torvalds ieee754_cxtest 17281da177e4SLinus Torvalds (IEEE754_INVALID_OPERATION)) 17291da177e4SLinus Torvalds rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 17301da177e4SLinus Torvalds else 17311da177e4SLinus Torvalds goto copcsr; 17321da177e4SLinus Torvalds 17331da177e4SLinus Torvalds } 17341da177e4SLinus Torvalds else { 17351da177e4SLinus Torvalds return SIGILL; 17361da177e4SLinus Torvalds } 17371da177e4SLinus Torvalds break; 17381da177e4SLinus Torvalds } 17391da177e4SLinus Torvalds break; 17401da177e4SLinus Torvalds 17413f7cac41SRalf Baechle case w_fmt: 17421da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 17431da177e4SLinus Torvalds case fcvts_op: 17441da177e4SLinus Torvalds /* convert word to single precision real */ 17451da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 17461da177e4SLinus Torvalds rv.s = ieee754sp_fint(fs.bits); 17471da177e4SLinus Torvalds rfmt = s_fmt; 17481da177e4SLinus Torvalds goto copcsr; 17491da177e4SLinus Torvalds case fcvtd_op: 17501da177e4SLinus Torvalds /* convert word to double precision real */ 17511da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 17521da177e4SLinus Torvalds rv.d = ieee754dp_fint(fs.bits); 17531da177e4SLinus Torvalds rfmt = d_fmt; 17541da177e4SLinus Torvalds goto copcsr; 17551da177e4SLinus Torvalds default: 17561da177e4SLinus Torvalds return SIGILL; 17571da177e4SLinus Torvalds } 17581da177e4SLinus Torvalds break; 17591da177e4SLinus Torvalds } 17601da177e4SLinus Torvalds 17613f7cac41SRalf Baechle case l_fmt: 176208a07904SRalf Baechle 176308a07904SRalf Baechle if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) 176408a07904SRalf Baechle return SIGILL; 176508a07904SRalf Baechle 1766bbd426f5SPaul Burton DIFROMREG(bits, MIPSInst_FS(ir)); 1767bbd426f5SPaul Burton 17681da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 17691da177e4SLinus Torvalds case fcvts_op: 17701da177e4SLinus Torvalds /* convert long to single precision real */ 1771bbd426f5SPaul Burton rv.s = ieee754sp_flong(bits); 17721da177e4SLinus Torvalds rfmt = s_fmt; 17731da177e4SLinus Torvalds goto copcsr; 17741da177e4SLinus Torvalds case fcvtd_op: 17751da177e4SLinus Torvalds /* convert long to double precision real */ 1776bbd426f5SPaul Burton rv.d = ieee754dp_flong(bits); 17771da177e4SLinus Torvalds rfmt = d_fmt; 17781da177e4SLinus Torvalds goto copcsr; 17791da177e4SLinus Torvalds default: 17801da177e4SLinus Torvalds return SIGILL; 17811da177e4SLinus Torvalds } 17821da177e4SLinus Torvalds break; 17831da177e4SLinus Torvalds 17841da177e4SLinus Torvalds default: 17851da177e4SLinus Torvalds return SIGILL; 17861da177e4SLinus Torvalds } 17871da177e4SLinus Torvalds 17881da177e4SLinus Torvalds /* 17891da177e4SLinus Torvalds * Update the fpu CSR register for this operation. 17901da177e4SLinus Torvalds * If an exception is required, generate a tidy SIGFPE exception, 17911da177e4SLinus Torvalds * without updating the result register. 17921da177e4SLinus Torvalds * Note: cause exception bits do not accumulate, they are rewritten 17931da177e4SLinus Torvalds * for each op; only the flag/sticky bits accumulate. 17941da177e4SLinus Torvalds */ 17951da177e4SLinus Torvalds ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr; 17961da177e4SLinus Torvalds if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 17973f7cac41SRalf Baechle /*printk ("SIGFPE: FPU csr = %08x\n",ctx->fcr31); */ 17981da177e4SLinus Torvalds return SIGFPE; 17991da177e4SLinus Torvalds } 18001da177e4SLinus Torvalds 18011da177e4SLinus Torvalds /* 18021da177e4SLinus Torvalds * Now we can safely write the result back to the register file. 18031da177e4SLinus Torvalds */ 18041da177e4SLinus Torvalds switch (rfmt) { 180508a07904SRalf Baechle case -1: 180608a07904SRalf Baechle 180708a07904SRalf Baechle if (cpu_has_mips_4_5_r) 180808a07904SRalf Baechle cbit = fpucondbit[MIPSInst_RT(ir) >> 2]; 18091da177e4SLinus Torvalds else 181008a07904SRalf Baechle cbit = FPU_CSR_COND; 181108a07904SRalf Baechle if (rv.w) 181208a07904SRalf Baechle ctx->fcr31 |= cbit; 181308a07904SRalf Baechle else 181408a07904SRalf Baechle ctx->fcr31 &= ~cbit; 18151da177e4SLinus Torvalds break; 181608a07904SRalf Baechle 18171da177e4SLinus Torvalds case d_fmt: 18181da177e4SLinus Torvalds DPTOREG(rv.d, MIPSInst_FD(ir)); 18191da177e4SLinus Torvalds break; 18201da177e4SLinus Torvalds case s_fmt: 18211da177e4SLinus Torvalds SPTOREG(rv.s, MIPSInst_FD(ir)); 18221da177e4SLinus Torvalds break; 18231da177e4SLinus Torvalds case w_fmt: 18241da177e4SLinus Torvalds SITOREG(rv.w, MIPSInst_FD(ir)); 18251da177e4SLinus Torvalds break; 18261da177e4SLinus Torvalds case l_fmt: 182708a07904SRalf Baechle if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) 182808a07904SRalf Baechle return SIGILL; 182908a07904SRalf Baechle 18301da177e4SLinus Torvalds DITOREG(rv.l, MIPSInst_FD(ir)); 18311da177e4SLinus Torvalds break; 18321da177e4SLinus Torvalds default: 18331da177e4SLinus Torvalds return SIGILL; 18341da177e4SLinus Torvalds } 18351da177e4SLinus Torvalds 18361da177e4SLinus Torvalds return 0; 18371da177e4SLinus Torvalds } 18381da177e4SLinus Torvalds 1839e04582b7SAtsushi Nemoto int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 1840515b029dSDavid Daney int has_fpu, void *__user *fault_addr) 18411da177e4SLinus Torvalds { 1842333d1f67SRalf Baechle unsigned long oldepc, prevepc; 1843102cedc3SLeonid Yegoshin struct mm_decoded_insn dec_insn; 1844102cedc3SLeonid Yegoshin u16 instr[4]; 1845102cedc3SLeonid Yegoshin u16 *instr_ptr; 18461da177e4SLinus Torvalds int sig = 0; 18471da177e4SLinus Torvalds 18481da177e4SLinus Torvalds oldepc = xcp->cp0_epc; 18491da177e4SLinus Torvalds do { 18501da177e4SLinus Torvalds prevepc = xcp->cp0_epc; 18511da177e4SLinus Torvalds 1852102cedc3SLeonid Yegoshin if (get_isa16_mode(prevepc) && cpu_has_mmips) { 1853102cedc3SLeonid Yegoshin /* 1854102cedc3SLeonid Yegoshin * Get next 2 microMIPS instructions and convert them 1855102cedc3SLeonid Yegoshin * into 32-bit instructions. 1856102cedc3SLeonid Yegoshin */ 1857102cedc3SLeonid Yegoshin if ((get_user(instr[0], (u16 __user *)msk_isa16_mode(xcp->cp0_epc))) || 1858102cedc3SLeonid Yegoshin (get_user(instr[1], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 2))) || 1859102cedc3SLeonid Yegoshin (get_user(instr[2], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 4))) || 1860102cedc3SLeonid Yegoshin (get_user(instr[3], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 6)))) { 1861b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 18621da177e4SLinus Torvalds return SIGBUS; 18631da177e4SLinus Torvalds } 1864102cedc3SLeonid Yegoshin instr_ptr = instr; 1865102cedc3SLeonid Yegoshin 1866102cedc3SLeonid Yegoshin /* Get first instruction. */ 1867102cedc3SLeonid Yegoshin if (mm_insn_16bit(*instr_ptr)) { 1868102cedc3SLeonid Yegoshin /* Duplicate the half-word. */ 1869102cedc3SLeonid Yegoshin dec_insn.insn = (*instr_ptr << 16) | 1870102cedc3SLeonid Yegoshin (*instr_ptr); 1871102cedc3SLeonid Yegoshin /* 16-bit instruction. */ 1872102cedc3SLeonid Yegoshin dec_insn.pc_inc = 2; 1873102cedc3SLeonid Yegoshin instr_ptr += 1; 1874102cedc3SLeonid Yegoshin } else { 1875102cedc3SLeonid Yegoshin dec_insn.insn = (*instr_ptr << 16) | 1876102cedc3SLeonid Yegoshin *(instr_ptr+1); 1877102cedc3SLeonid Yegoshin /* 32-bit instruction. */ 1878102cedc3SLeonid Yegoshin dec_insn.pc_inc = 4; 1879102cedc3SLeonid Yegoshin instr_ptr += 2; 1880515b029dSDavid Daney } 1881102cedc3SLeonid Yegoshin /* Get second instruction. */ 1882102cedc3SLeonid Yegoshin if (mm_insn_16bit(*instr_ptr)) { 1883102cedc3SLeonid Yegoshin /* Duplicate the half-word. */ 1884102cedc3SLeonid Yegoshin dec_insn.next_insn = (*instr_ptr << 16) | 1885102cedc3SLeonid Yegoshin (*instr_ptr); 1886102cedc3SLeonid Yegoshin /* 16-bit instruction. */ 1887102cedc3SLeonid Yegoshin dec_insn.next_pc_inc = 2; 1888102cedc3SLeonid Yegoshin } else { 1889102cedc3SLeonid Yegoshin dec_insn.next_insn = (*instr_ptr << 16) | 1890102cedc3SLeonid Yegoshin *(instr_ptr+1); 1891102cedc3SLeonid Yegoshin /* 32-bit instruction. */ 1892102cedc3SLeonid Yegoshin dec_insn.next_pc_inc = 4; 1893102cedc3SLeonid Yegoshin } 1894102cedc3SLeonid Yegoshin dec_insn.micro_mips_mode = 1; 1895102cedc3SLeonid Yegoshin } else { 1896102cedc3SLeonid Yegoshin if ((get_user(dec_insn.insn, 1897102cedc3SLeonid Yegoshin (mips_instruction __user *) xcp->cp0_epc)) || 1898102cedc3SLeonid Yegoshin (get_user(dec_insn.next_insn, 1899102cedc3SLeonid Yegoshin (mips_instruction __user *)(xcp->cp0_epc+4)))) { 1900102cedc3SLeonid Yegoshin MIPS_FPU_EMU_INC_STATS(errors); 1901102cedc3SLeonid Yegoshin return SIGBUS; 1902102cedc3SLeonid Yegoshin } 1903102cedc3SLeonid Yegoshin dec_insn.pc_inc = 4; 1904102cedc3SLeonid Yegoshin dec_insn.next_pc_inc = 4; 1905102cedc3SLeonid Yegoshin dec_insn.micro_mips_mode = 0; 1906102cedc3SLeonid Yegoshin } 1907102cedc3SLeonid Yegoshin 1908102cedc3SLeonid Yegoshin if ((dec_insn.insn == 0) || 1909102cedc3SLeonid Yegoshin ((dec_insn.pc_inc == 2) && 1910102cedc3SLeonid Yegoshin ((dec_insn.insn & 0xffff) == MM_NOP16))) 1911102cedc3SLeonid Yegoshin xcp->cp0_epc += dec_insn.pc_inc; /* Skip NOPs */ 19121da177e4SLinus Torvalds else { 1913cd21dfcfSRalf Baechle /* 1914cd21dfcfSRalf Baechle * The 'ieee754_csr' is an alias of 1915cd21dfcfSRalf Baechle * ctx->fcr31. No need to copy ctx->fcr31 to 1916cd21dfcfSRalf Baechle * ieee754_csr. But ieee754_csr.rm is ieee 1917cd21dfcfSRalf Baechle * library modes. (not mips rounding mode) 1918cd21dfcfSRalf Baechle */ 1919102cedc3SLeonid Yegoshin sig = cop1Emulate(xcp, ctx, dec_insn, fault_addr); 19201da177e4SLinus Torvalds } 19211da177e4SLinus Torvalds 1922e04582b7SAtsushi Nemoto if (has_fpu) 19231da177e4SLinus Torvalds break; 19241da177e4SLinus Torvalds if (sig) 19251da177e4SLinus Torvalds break; 19261da177e4SLinus Torvalds 19271da177e4SLinus Torvalds cond_resched(); 19281da177e4SLinus Torvalds } while (xcp->cp0_epc > prevepc); 19291da177e4SLinus Torvalds 19301da177e4SLinus Torvalds /* SIGILL indicates a non-fpu instruction */ 19311da177e4SLinus Torvalds if (sig == SIGILL && xcp->cp0_epc != oldepc) 19323f7cac41SRalf Baechle /* but if EPC has advanced, then ignore it */ 19331da177e4SLinus Torvalds sig = 0; 19341da177e4SLinus Torvalds 19351da177e4SLinus Torvalds return sig; 19361da177e4SLinus Torvalds } 1937