19d5a6349SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 21da177e4SLinus Torvalds /* 33f7cac41SRalf Baechle * cp1emu.c: a MIPS coprocessor 1 (FPU) instruction emulator 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * MIPS floating point support 61da177e4SLinus Torvalds * Copyright (C) 1994-2000 Algorithmics Ltd. 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com 91da177e4SLinus Torvalds * Copyright (C) 2000 MIPS Technologies, Inc. 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * A complete emulator for MIPS coprocessor 1 instructions. This is 121da177e4SLinus Torvalds * required for #float(switch) or #float(trap), where it catches all 131da177e4SLinus Torvalds * COP1 instructions via the "CoProcessor Unusable" exception. 141da177e4SLinus Torvalds * 151da177e4SLinus Torvalds * More surprisingly it is also required for #float(ieee), to help out 163f7cac41SRalf Baechle * the hardware FPU at the boundaries of the IEEE-754 representation 171da177e4SLinus Torvalds * (denormalised values, infinities, underflow, etc). It is made 181da177e4SLinus Torvalds * quite nasty because emulation of some non-COP1 instructions is 191da177e4SLinus Torvalds * required, e.g. in branch delay slots. 201da177e4SLinus Torvalds * 213f7cac41SRalf Baechle * Note if you know that you won't have an FPU, then you'll get much 221da177e4SLinus Torvalds * better performance by compiling with -msoft-float! 231da177e4SLinus Torvalds */ 241da177e4SLinus Torvalds #include <linux/sched.h> 2583fd38caSAtsushi Nemoto #include <linux/debugfs.h> 2685c51c51SRalf Baechle #include <linux/percpu-defs.h> 277f788d2dSDeng-Cheng Zhu #include <linux/perf_event.h> 281da177e4SLinus Torvalds 29cd8ee345SRalf Baechle #include <asm/branch.h> 301da177e4SLinus Torvalds #include <asm/inst.h> 311da177e4SLinus Torvalds #include <asm/ptrace.h> 321da177e4SLinus Torvalds #include <asm/signal.h> 337c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 34cd8ee345SRalf Baechle 35f6843626SMaciej W. Rozycki #include <asm/cpu-info.h> 36cd8ee345SRalf Baechle #include <asm/processor.h> 371da177e4SLinus Torvalds #include <asm/fpu_emulator.h> 38102cedc3SLeonid Yegoshin #include <asm/fpu.h> 39b0a668fbSLeonid Yegoshin #include <asm/mips-r2-to-r6-emul.h> 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds #include "ieee754.h" 421da177e4SLinus Torvalds 431da177e4SLinus Torvalds /* Function which emulates a floating point instruction. */ 441da177e4SLinus Torvalds 45eae89076SAtsushi Nemoto static int fpu_emu(struct pt_regs *, struct mips_fpu_struct *, 461da177e4SLinus Torvalds mips_instruction); 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds static int fpux_emu(struct pt_regs *, 49445a58ceSPaul Burton struct mips_fpu_struct *, mips_instruction, void __user **); 501da177e4SLinus Torvalds 511da177e4SLinus Torvalds /* Control registers */ 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds #define FPCREG_RID 0 /* $0 = revision id */ 54c491cfa2SMaciej W. Rozycki #define FPCREG_FCCR 25 /* $25 = fccr */ 55c491cfa2SMaciej W. Rozycki #define FPCREG_FEXR 26 /* $26 = fexr */ 56c491cfa2SMaciej W. Rozycki #define FPCREG_FENR 28 /* $28 = fenr */ 571da177e4SLinus Torvalds #define FPCREG_CSR 31 /* $31 = csr */ 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds /* convert condition code register number to csr bit */ 60b0a668fbSLeonid Yegoshin const unsigned int fpucondbit[8] = { 61c491cfa2SMaciej W. Rozycki FPU_CSR_COND, 621da177e4SLinus Torvalds FPU_CSR_COND1, 631da177e4SLinus Torvalds FPU_CSR_COND2, 641da177e4SLinus Torvalds FPU_CSR_COND3, 651da177e4SLinus Torvalds FPU_CSR_COND4, 661da177e4SLinus Torvalds FPU_CSR_COND5, 671da177e4SLinus Torvalds FPU_CSR_COND6, 681da177e4SLinus Torvalds FPU_CSR_COND7 691da177e4SLinus Torvalds }; 701da177e4SLinus Torvalds 71102cedc3SLeonid Yegoshin /* (microMIPS) Convert certain microMIPS instructions to MIPS32 format. */ 72102cedc3SLeonid Yegoshin static const int sd_format[] = {16, 17, 0, 0, 0, 0, 0, 0}; 73102cedc3SLeonid Yegoshin static const int sdps_format[] = {16, 17, 22, 0, 0, 0, 0, 0}; 74102cedc3SLeonid Yegoshin static const int dwl_format[] = {17, 20, 21, 0, 0, 0, 0, 0}; 75102cedc3SLeonid Yegoshin static const int swl_format[] = {16, 20, 21, 0, 0, 0, 0, 0}; 76102cedc3SLeonid Yegoshin 77102cedc3SLeonid Yegoshin /* 78102cedc3SLeonid Yegoshin * This functions translates a 32-bit microMIPS instruction 79102cedc3SLeonid Yegoshin * into a 32-bit MIPS32 instruction. Returns 0 on success 80102cedc3SLeonid Yegoshin * and SIGILL otherwise. 81102cedc3SLeonid Yegoshin */ 82102cedc3SLeonid Yegoshin static int microMIPS32_to_MIPS32(union mips_instruction *insn_ptr) 83102cedc3SLeonid Yegoshin { 84102cedc3SLeonid Yegoshin union mips_instruction insn = *insn_ptr; 85102cedc3SLeonid Yegoshin union mips_instruction mips32_insn = insn; 86102cedc3SLeonid Yegoshin int func, fmt, op; 87102cedc3SLeonid Yegoshin 88102cedc3SLeonid Yegoshin switch (insn.mm_i_format.opcode) { 89102cedc3SLeonid Yegoshin case mm_ldc132_op: 90102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.opcode = ldc1_op; 91102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 92102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 93102cedc3SLeonid Yegoshin break; 94102cedc3SLeonid Yegoshin case mm_lwc132_op: 95102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.opcode = lwc1_op; 96102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 97102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 98102cedc3SLeonid Yegoshin break; 99102cedc3SLeonid Yegoshin case mm_sdc132_op: 100102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.opcode = sdc1_op; 101102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 102102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 103102cedc3SLeonid Yegoshin break; 104102cedc3SLeonid Yegoshin case mm_swc132_op: 105102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.opcode = swc1_op; 106102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 107102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 108102cedc3SLeonid Yegoshin break; 109102cedc3SLeonid Yegoshin case mm_pool32i_op: 110102cedc3SLeonid Yegoshin /* NOTE: offset is << by 1 if in microMIPS mode. */ 111102cedc3SLeonid Yegoshin if ((insn.mm_i_format.rt == mm_bc1f_op) || 112102cedc3SLeonid Yegoshin (insn.mm_i_format.rt == mm_bc1t_op)) { 113102cedc3SLeonid Yegoshin mips32_insn.fb_format.opcode = cop1_op; 114102cedc3SLeonid Yegoshin mips32_insn.fb_format.bc = bc_op; 115102cedc3SLeonid Yegoshin mips32_insn.fb_format.flag = 116102cedc3SLeonid Yegoshin (insn.mm_i_format.rt == mm_bc1t_op) ? 1 : 0; 117102cedc3SLeonid Yegoshin } else 118102cedc3SLeonid Yegoshin return SIGILL; 119102cedc3SLeonid Yegoshin break; 120102cedc3SLeonid Yegoshin case mm_pool32f_op: 121102cedc3SLeonid Yegoshin switch (insn.mm_fp0_format.func) { 122102cedc3SLeonid Yegoshin case mm_32f_01_op: 123102cedc3SLeonid Yegoshin case mm_32f_11_op: 124102cedc3SLeonid Yegoshin case mm_32f_02_op: 125102cedc3SLeonid Yegoshin case mm_32f_12_op: 126102cedc3SLeonid Yegoshin case mm_32f_41_op: 127102cedc3SLeonid Yegoshin case mm_32f_51_op: 128102cedc3SLeonid Yegoshin case mm_32f_42_op: 129102cedc3SLeonid Yegoshin case mm_32f_52_op: 130102cedc3SLeonid Yegoshin op = insn.mm_fp0_format.func; 131102cedc3SLeonid Yegoshin if (op == mm_32f_01_op) 132102cedc3SLeonid Yegoshin func = madd_s_op; 133102cedc3SLeonid Yegoshin else if (op == mm_32f_11_op) 134102cedc3SLeonid Yegoshin func = madd_d_op; 135102cedc3SLeonid Yegoshin else if (op == mm_32f_02_op) 136102cedc3SLeonid Yegoshin func = nmadd_s_op; 137102cedc3SLeonid Yegoshin else if (op == mm_32f_12_op) 138102cedc3SLeonid Yegoshin func = nmadd_d_op; 139102cedc3SLeonid Yegoshin else if (op == mm_32f_41_op) 140102cedc3SLeonid Yegoshin func = msub_s_op; 141102cedc3SLeonid Yegoshin else if (op == mm_32f_51_op) 142102cedc3SLeonid Yegoshin func = msub_d_op; 143102cedc3SLeonid Yegoshin else if (op == mm_32f_42_op) 144102cedc3SLeonid Yegoshin func = nmsub_s_op; 145102cedc3SLeonid Yegoshin else 146102cedc3SLeonid Yegoshin func = nmsub_d_op; 147102cedc3SLeonid Yegoshin mips32_insn.fp6_format.opcode = cop1x_op; 148102cedc3SLeonid Yegoshin mips32_insn.fp6_format.fr = insn.mm_fp6_format.fr; 149102cedc3SLeonid Yegoshin mips32_insn.fp6_format.ft = insn.mm_fp6_format.ft; 150102cedc3SLeonid Yegoshin mips32_insn.fp6_format.fs = insn.mm_fp6_format.fs; 151102cedc3SLeonid Yegoshin mips32_insn.fp6_format.fd = insn.mm_fp6_format.fd; 152102cedc3SLeonid Yegoshin mips32_insn.fp6_format.func = func; 153102cedc3SLeonid Yegoshin break; 154102cedc3SLeonid Yegoshin case mm_32f_10_op: 155102cedc3SLeonid Yegoshin func = -1; /* Invalid */ 156102cedc3SLeonid Yegoshin op = insn.mm_fp5_format.op & 0x7; 157102cedc3SLeonid Yegoshin if (op == mm_ldxc1_op) 158102cedc3SLeonid Yegoshin func = ldxc1_op; 159102cedc3SLeonid Yegoshin else if (op == mm_sdxc1_op) 160102cedc3SLeonid Yegoshin func = sdxc1_op; 161102cedc3SLeonid Yegoshin else if (op == mm_lwxc1_op) 162102cedc3SLeonid Yegoshin func = lwxc1_op; 163102cedc3SLeonid Yegoshin else if (op == mm_swxc1_op) 164102cedc3SLeonid Yegoshin func = swxc1_op; 165102cedc3SLeonid Yegoshin 166102cedc3SLeonid Yegoshin if (func != -1) { 167102cedc3SLeonid Yegoshin mips32_insn.r_format.opcode = cop1x_op; 168102cedc3SLeonid Yegoshin mips32_insn.r_format.rs = 169102cedc3SLeonid Yegoshin insn.mm_fp5_format.base; 170102cedc3SLeonid Yegoshin mips32_insn.r_format.rt = 171102cedc3SLeonid Yegoshin insn.mm_fp5_format.index; 172102cedc3SLeonid Yegoshin mips32_insn.r_format.rd = 0; 173102cedc3SLeonid Yegoshin mips32_insn.r_format.re = insn.mm_fp5_format.fd; 174102cedc3SLeonid Yegoshin mips32_insn.r_format.func = func; 175102cedc3SLeonid Yegoshin } else 176102cedc3SLeonid Yegoshin return SIGILL; 177102cedc3SLeonid Yegoshin break; 178102cedc3SLeonid Yegoshin case mm_32f_40_op: 179102cedc3SLeonid Yegoshin op = -1; /* Invalid */ 180102cedc3SLeonid Yegoshin if (insn.mm_fp2_format.op == mm_fmovt_op) 181102cedc3SLeonid Yegoshin op = 1; 182102cedc3SLeonid Yegoshin else if (insn.mm_fp2_format.op == mm_fmovf_op) 183102cedc3SLeonid Yegoshin op = 0; 184102cedc3SLeonid Yegoshin if (op != -1) { 185102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 186102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 187102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp2_format.fmt]; 188102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 189102cedc3SLeonid Yegoshin (insn.mm_fp2_format.cc<<2) + op; 190102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 191102cedc3SLeonid Yegoshin insn.mm_fp2_format.fs; 192102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 193102cedc3SLeonid Yegoshin insn.mm_fp2_format.fd; 194102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = fmovc_op; 195102cedc3SLeonid Yegoshin } else 196102cedc3SLeonid Yegoshin return SIGILL; 197102cedc3SLeonid Yegoshin break; 198102cedc3SLeonid Yegoshin case mm_32f_60_op: 199102cedc3SLeonid Yegoshin func = -1; /* Invalid */ 200102cedc3SLeonid Yegoshin if (insn.mm_fp0_format.op == mm_fadd_op) 201102cedc3SLeonid Yegoshin func = fadd_op; 202102cedc3SLeonid Yegoshin else if (insn.mm_fp0_format.op == mm_fsub_op) 203102cedc3SLeonid Yegoshin func = fsub_op; 204102cedc3SLeonid Yegoshin else if (insn.mm_fp0_format.op == mm_fmul_op) 205102cedc3SLeonid Yegoshin func = fmul_op; 206102cedc3SLeonid Yegoshin else if (insn.mm_fp0_format.op == mm_fdiv_op) 207102cedc3SLeonid Yegoshin func = fdiv_op; 208102cedc3SLeonid Yegoshin if (func != -1) { 209102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 210102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 211102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp0_format.fmt]; 212102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 213102cedc3SLeonid Yegoshin insn.mm_fp0_format.ft; 214102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 215102cedc3SLeonid Yegoshin insn.mm_fp0_format.fs; 216102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 217102cedc3SLeonid Yegoshin insn.mm_fp0_format.fd; 218102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 219102cedc3SLeonid Yegoshin } else 220102cedc3SLeonid Yegoshin return SIGILL; 221102cedc3SLeonid Yegoshin break; 222102cedc3SLeonid Yegoshin case mm_32f_70_op: 223102cedc3SLeonid Yegoshin func = -1; /* Invalid */ 224102cedc3SLeonid Yegoshin if (insn.mm_fp0_format.op == mm_fmovn_op) 225102cedc3SLeonid Yegoshin func = fmovn_op; 226102cedc3SLeonid Yegoshin else if (insn.mm_fp0_format.op == mm_fmovz_op) 227102cedc3SLeonid Yegoshin func = fmovz_op; 228102cedc3SLeonid Yegoshin if (func != -1) { 229102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 230102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 231102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp0_format.fmt]; 232102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 233102cedc3SLeonid Yegoshin insn.mm_fp0_format.ft; 234102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 235102cedc3SLeonid Yegoshin insn.mm_fp0_format.fs; 236102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 237102cedc3SLeonid Yegoshin insn.mm_fp0_format.fd; 238102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 239102cedc3SLeonid Yegoshin } else 240102cedc3SLeonid Yegoshin return SIGILL; 241102cedc3SLeonid Yegoshin break; 242102cedc3SLeonid Yegoshin case mm_32f_73_op: /* POOL32FXF */ 243102cedc3SLeonid Yegoshin switch (insn.mm_fp1_format.op) { 244102cedc3SLeonid Yegoshin case mm_movf0_op: 245102cedc3SLeonid Yegoshin case mm_movf1_op: 246102cedc3SLeonid Yegoshin case mm_movt0_op: 247102cedc3SLeonid Yegoshin case mm_movt1_op: 248102cedc3SLeonid Yegoshin if ((insn.mm_fp1_format.op & 0x7f) == 249102cedc3SLeonid Yegoshin mm_movf0_op) 250102cedc3SLeonid Yegoshin op = 0; 251102cedc3SLeonid Yegoshin else 252102cedc3SLeonid Yegoshin op = 1; 253102cedc3SLeonid Yegoshin mips32_insn.r_format.opcode = spec_op; 254102cedc3SLeonid Yegoshin mips32_insn.r_format.rs = insn.mm_fp4_format.fs; 255102cedc3SLeonid Yegoshin mips32_insn.r_format.rt = 256102cedc3SLeonid Yegoshin (insn.mm_fp4_format.cc << 2) + op; 257102cedc3SLeonid Yegoshin mips32_insn.r_format.rd = insn.mm_fp4_format.rt; 258102cedc3SLeonid Yegoshin mips32_insn.r_format.re = 0; 259102cedc3SLeonid Yegoshin mips32_insn.r_format.func = movc_op; 260102cedc3SLeonid Yegoshin break; 261102cedc3SLeonid Yegoshin case mm_fcvtd0_op: 262102cedc3SLeonid Yegoshin case mm_fcvtd1_op: 263102cedc3SLeonid Yegoshin case mm_fcvts0_op: 264102cedc3SLeonid Yegoshin case mm_fcvts1_op: 265102cedc3SLeonid Yegoshin if ((insn.mm_fp1_format.op & 0x7f) == 266102cedc3SLeonid Yegoshin mm_fcvtd0_op) { 267102cedc3SLeonid Yegoshin func = fcvtd_op; 268102cedc3SLeonid Yegoshin fmt = swl_format[insn.mm_fp3_format.fmt]; 269102cedc3SLeonid Yegoshin } else { 270102cedc3SLeonid Yegoshin func = fcvts_op; 271102cedc3SLeonid Yegoshin fmt = dwl_format[insn.mm_fp3_format.fmt]; 272102cedc3SLeonid Yegoshin } 273102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 274102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = fmt; 275102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 0; 276102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 277102cedc3SLeonid Yegoshin insn.mm_fp3_format.fs; 278102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 279102cedc3SLeonid Yegoshin insn.mm_fp3_format.rt; 280102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 281102cedc3SLeonid Yegoshin break; 282102cedc3SLeonid Yegoshin case mm_fmov0_op: 283102cedc3SLeonid Yegoshin case mm_fmov1_op: 284102cedc3SLeonid Yegoshin case mm_fabs0_op: 285102cedc3SLeonid Yegoshin case mm_fabs1_op: 286102cedc3SLeonid Yegoshin case mm_fneg0_op: 287102cedc3SLeonid Yegoshin case mm_fneg1_op: 288102cedc3SLeonid Yegoshin if ((insn.mm_fp1_format.op & 0x7f) == 289102cedc3SLeonid Yegoshin mm_fmov0_op) 290102cedc3SLeonid Yegoshin func = fmov_op; 291102cedc3SLeonid Yegoshin else if ((insn.mm_fp1_format.op & 0x7f) == 292102cedc3SLeonid Yegoshin mm_fabs0_op) 293102cedc3SLeonid Yegoshin func = fabs_op; 294102cedc3SLeonid Yegoshin else 295102cedc3SLeonid Yegoshin func = fneg_op; 296102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 297102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 298102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp3_format.fmt]; 299102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 0; 300102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 301102cedc3SLeonid Yegoshin insn.mm_fp3_format.fs; 302102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 303102cedc3SLeonid Yegoshin insn.mm_fp3_format.rt; 304102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 305102cedc3SLeonid Yegoshin break; 306102cedc3SLeonid Yegoshin case mm_ffloorl_op: 307102cedc3SLeonid Yegoshin case mm_ffloorw_op: 308102cedc3SLeonid Yegoshin case mm_fceill_op: 309102cedc3SLeonid Yegoshin case mm_fceilw_op: 310102cedc3SLeonid Yegoshin case mm_ftruncl_op: 311102cedc3SLeonid Yegoshin case mm_ftruncw_op: 312102cedc3SLeonid Yegoshin case mm_froundl_op: 313102cedc3SLeonid Yegoshin case mm_froundw_op: 314102cedc3SLeonid Yegoshin case mm_fcvtl_op: 315102cedc3SLeonid Yegoshin case mm_fcvtw_op: 316102cedc3SLeonid Yegoshin if (insn.mm_fp1_format.op == mm_ffloorl_op) 317102cedc3SLeonid Yegoshin func = ffloorl_op; 318102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_ffloorw_op) 319102cedc3SLeonid Yegoshin func = ffloor_op; 320102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_fceill_op) 321102cedc3SLeonid Yegoshin func = fceill_op; 322102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_fceilw_op) 323102cedc3SLeonid Yegoshin func = fceil_op; 324102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_ftruncl_op) 325102cedc3SLeonid Yegoshin func = ftruncl_op; 326102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_ftruncw_op) 327102cedc3SLeonid Yegoshin func = ftrunc_op; 328102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_froundl_op) 329102cedc3SLeonid Yegoshin func = froundl_op; 330102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_froundw_op) 331102cedc3SLeonid Yegoshin func = fround_op; 332102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_fcvtl_op) 333102cedc3SLeonid Yegoshin func = fcvtl_op; 334102cedc3SLeonid Yegoshin else 335102cedc3SLeonid Yegoshin func = fcvtw_op; 336102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 337102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 338102cedc3SLeonid Yegoshin sd_format[insn.mm_fp1_format.fmt]; 339102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 0; 340102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 341102cedc3SLeonid Yegoshin insn.mm_fp1_format.fs; 342102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 343102cedc3SLeonid Yegoshin insn.mm_fp1_format.rt; 344102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 345102cedc3SLeonid Yegoshin break; 346102cedc3SLeonid Yegoshin case mm_frsqrt_op: 347102cedc3SLeonid Yegoshin case mm_fsqrt_op: 348102cedc3SLeonid Yegoshin case mm_frecip_op: 349102cedc3SLeonid Yegoshin if (insn.mm_fp1_format.op == mm_frsqrt_op) 350102cedc3SLeonid Yegoshin func = frsqrt_op; 351102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_fsqrt_op) 352102cedc3SLeonid Yegoshin func = fsqrt_op; 353102cedc3SLeonid Yegoshin else 354102cedc3SLeonid Yegoshin func = frecip_op; 355102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 356102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 357102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp1_format.fmt]; 358102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 0; 359102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 360102cedc3SLeonid Yegoshin insn.mm_fp1_format.fs; 361102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 362102cedc3SLeonid Yegoshin insn.mm_fp1_format.rt; 363102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 364102cedc3SLeonid Yegoshin break; 365102cedc3SLeonid Yegoshin case mm_mfc1_op: 366102cedc3SLeonid Yegoshin case mm_mtc1_op: 367102cedc3SLeonid Yegoshin case mm_cfc1_op: 368102cedc3SLeonid Yegoshin case mm_ctc1_op: 3699355e59cSSteven J. Hill case mm_mfhc1_op: 3709355e59cSSteven J. Hill case mm_mthc1_op: 371102cedc3SLeonid Yegoshin if (insn.mm_fp1_format.op == mm_mfc1_op) 372102cedc3SLeonid Yegoshin op = mfc_op; 373102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_mtc1_op) 374102cedc3SLeonid Yegoshin op = mtc_op; 375102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_cfc1_op) 376102cedc3SLeonid Yegoshin op = cfc_op; 3779355e59cSSteven J. Hill else if (insn.mm_fp1_format.op == mm_ctc1_op) 378102cedc3SLeonid Yegoshin op = ctc_op; 3799355e59cSSteven J. Hill else if (insn.mm_fp1_format.op == mm_mfhc1_op) 3809355e59cSSteven J. Hill op = mfhc_op; 3819355e59cSSteven J. Hill else 3829355e59cSSteven J. Hill op = mthc_op; 383102cedc3SLeonid Yegoshin mips32_insn.fp1_format.opcode = cop1_op; 384102cedc3SLeonid Yegoshin mips32_insn.fp1_format.op = op; 385102cedc3SLeonid Yegoshin mips32_insn.fp1_format.rt = 386102cedc3SLeonid Yegoshin insn.mm_fp1_format.rt; 387102cedc3SLeonid Yegoshin mips32_insn.fp1_format.fs = 388102cedc3SLeonid Yegoshin insn.mm_fp1_format.fs; 389102cedc3SLeonid Yegoshin mips32_insn.fp1_format.fd = 0; 390102cedc3SLeonid Yegoshin mips32_insn.fp1_format.func = 0; 391102cedc3SLeonid Yegoshin break; 392102cedc3SLeonid Yegoshin default: 393102cedc3SLeonid Yegoshin return SIGILL; 394102cedc3SLeonid Yegoshin } 395102cedc3SLeonid Yegoshin break; 396102cedc3SLeonid Yegoshin case mm_32f_74_op: /* c.cond.fmt */ 397102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 398102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 399102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp4_format.fmt]; 400102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = insn.mm_fp4_format.rt; 401102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = insn.mm_fp4_format.fs; 402102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = insn.mm_fp4_format.cc << 2; 403102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = 404102cedc3SLeonid Yegoshin insn.mm_fp4_format.cond | MM_MIPS32_COND_FC; 405102cedc3SLeonid Yegoshin break; 406102cedc3SLeonid Yegoshin default: 407102cedc3SLeonid Yegoshin return SIGILL; 408102cedc3SLeonid Yegoshin } 409102cedc3SLeonid Yegoshin break; 410102cedc3SLeonid Yegoshin default: 411102cedc3SLeonid Yegoshin return SIGILL; 412102cedc3SLeonid Yegoshin } 413102cedc3SLeonid Yegoshin 414102cedc3SLeonid Yegoshin *insn_ptr = mips32_insn; 415102cedc3SLeonid Yegoshin return 0; 416102cedc3SLeonid Yegoshin } 417102cedc3SLeonid Yegoshin 4181da177e4SLinus Torvalds /* 4191da177e4SLinus Torvalds * Redundant with logic already in kernel/branch.c, 4201da177e4SLinus Torvalds * embedded in compute_return_epc. At some point, 4211da177e4SLinus Torvalds * a single subroutine should be used across both 4221da177e4SLinus Torvalds * modules. 4231da177e4SLinus Torvalds */ 424432c6bacSPaul Burton int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, 425102cedc3SLeonid Yegoshin unsigned long *contpc) 4261da177e4SLinus Torvalds { 427102cedc3SLeonid Yegoshin union mips_instruction insn = (union mips_instruction)dec_insn.insn; 428102cedc3SLeonid Yegoshin unsigned int fcr31; 429102cedc3SLeonid Yegoshin unsigned int bit = 0; 4308bcd84a4SDouglas Leung unsigned int bit0; 4318bcd84a4SDouglas Leung union fpureg *fpr; 432102cedc3SLeonid Yegoshin 433102cedc3SLeonid Yegoshin switch (insn.i_format.opcode) { 4341da177e4SLinus Torvalds case spec_op: 435102cedc3SLeonid Yegoshin switch (insn.r_format.func) { 4361da177e4SLinus Torvalds case jalr_op: 437ab4a92e6SPaul Burton if (insn.r_format.rd != 0) { 438102cedc3SLeonid Yegoshin regs->regs[insn.r_format.rd] = 439102cedc3SLeonid Yegoshin regs->cp0_epc + dec_insn.pc_inc + 440102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 441ab4a92e6SPaul Burton } 442c9b02990SLiangliang Huang fallthrough; 4431da177e4SLinus Torvalds case jr_op: 4445f9f41c4SMarkos Chandras /* For R6, JR already emulated in jalr_op */ 445143fefc8SMarkos Chandras if (NO_R6EMU && insn.r_format.func == jr_op) 4465f9f41c4SMarkos Chandras break; 447102cedc3SLeonid Yegoshin *contpc = regs->regs[insn.r_format.rs]; 4481da177e4SLinus Torvalds return 1; 4491da177e4SLinus Torvalds } 4501da177e4SLinus Torvalds break; 4511da177e4SLinus Torvalds case bcond_op: 452102cedc3SLeonid Yegoshin switch (insn.i_format.rt) { 4531da177e4SLinus Torvalds case bltzal_op: 4541da177e4SLinus Torvalds case bltzall_op: 455319824eaSMarkos Chandras if (NO_R6EMU && (insn.i_format.rs || 456319824eaSMarkos Chandras insn.i_format.rt == bltzall_op)) 457319824eaSMarkos Chandras break; 458319824eaSMarkos Chandras 459102cedc3SLeonid Yegoshin regs->regs[31] = regs->cp0_epc + 460102cedc3SLeonid Yegoshin dec_insn.pc_inc + 461102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 462c9b02990SLiangliang Huang fallthrough; 463102cedc3SLeonid Yegoshin case bltzl_op: 464319824eaSMarkos Chandras if (NO_R6EMU) 465319824eaSMarkos Chandras break; 466c9b02990SLiangliang Huang fallthrough; 467319824eaSMarkos Chandras case bltz_op: 468102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.i_format.rs] < 0) 469102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 470102cedc3SLeonid Yegoshin dec_insn.pc_inc + 471102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 472102cedc3SLeonid Yegoshin else 473102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 474102cedc3SLeonid Yegoshin dec_insn.pc_inc + 475102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 4761da177e4SLinus Torvalds return 1; 477102cedc3SLeonid Yegoshin case bgezal_op: 478102cedc3SLeonid Yegoshin case bgezall_op: 479319824eaSMarkos Chandras if (NO_R6EMU && (insn.i_format.rs || 480319824eaSMarkos Chandras insn.i_format.rt == bgezall_op)) 481319824eaSMarkos Chandras break; 482319824eaSMarkos Chandras 483102cedc3SLeonid Yegoshin regs->regs[31] = regs->cp0_epc + 484102cedc3SLeonid Yegoshin dec_insn.pc_inc + 485102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 486c9b02990SLiangliang Huang fallthrough; 487102cedc3SLeonid Yegoshin case bgezl_op: 488319824eaSMarkos Chandras if (NO_R6EMU) 489319824eaSMarkos Chandras break; 490c9b02990SLiangliang Huang fallthrough; 491319824eaSMarkos Chandras case bgez_op: 492102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.i_format.rs] >= 0) 493102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 494102cedc3SLeonid Yegoshin dec_insn.pc_inc + 495102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 496102cedc3SLeonid Yegoshin else 497102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 498102cedc3SLeonid Yegoshin dec_insn.pc_inc + 499102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 500102cedc3SLeonid Yegoshin return 1; 5011da177e4SLinus Torvalds } 5021da177e4SLinus Torvalds break; 5031da177e4SLinus Torvalds case jalx_op: 504102cedc3SLeonid Yegoshin set_isa16_mode(bit); 505c9b02990SLiangliang Huang fallthrough; 506102cedc3SLeonid Yegoshin case jal_op: 507102cedc3SLeonid Yegoshin regs->regs[31] = regs->cp0_epc + 508102cedc3SLeonid Yegoshin dec_insn.pc_inc + 509102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 510c9b02990SLiangliang Huang fallthrough; 511102cedc3SLeonid Yegoshin case j_op: 512102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + dec_insn.pc_inc; 513102cedc3SLeonid Yegoshin *contpc >>= 28; 514102cedc3SLeonid Yegoshin *contpc <<= 28; 515102cedc3SLeonid Yegoshin *contpc |= (insn.j_format.target << 2); 516102cedc3SLeonid Yegoshin /* Set microMIPS mode bit: XOR for jalx. */ 517102cedc3SLeonid Yegoshin *contpc ^= bit; 5181da177e4SLinus Torvalds return 1; 519102cedc3SLeonid Yegoshin case beql_op: 520319824eaSMarkos Chandras if (NO_R6EMU) 521319824eaSMarkos Chandras break; 522c9b02990SLiangliang Huang fallthrough; 523319824eaSMarkos Chandras case beq_op: 524102cedc3SLeonid Yegoshin if (regs->regs[insn.i_format.rs] == 525102cedc3SLeonid Yegoshin regs->regs[insn.i_format.rt]) 526102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 527102cedc3SLeonid Yegoshin dec_insn.pc_inc + 528102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 529102cedc3SLeonid Yegoshin else 530102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 531102cedc3SLeonid Yegoshin dec_insn.pc_inc + 532102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 533102cedc3SLeonid Yegoshin return 1; 534102cedc3SLeonid Yegoshin case bnel_op: 535319824eaSMarkos Chandras if (NO_R6EMU) 536319824eaSMarkos Chandras break; 537c9b02990SLiangliang Huang fallthrough; 538319824eaSMarkos Chandras case bne_op: 539102cedc3SLeonid Yegoshin if (regs->regs[insn.i_format.rs] != 540102cedc3SLeonid Yegoshin regs->regs[insn.i_format.rt]) 541102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 542102cedc3SLeonid Yegoshin dec_insn.pc_inc + 543102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 544102cedc3SLeonid Yegoshin else 545102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 546102cedc3SLeonid Yegoshin dec_insn.pc_inc + 547102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 548102cedc3SLeonid Yegoshin return 1; 549102cedc3SLeonid Yegoshin case blezl_op: 550e9d92d22SMarkos Chandras if (!insn.i_format.rt && NO_R6EMU) 551319824eaSMarkos Chandras break; 552c9b02990SLiangliang Huang fallthrough; 553319824eaSMarkos Chandras case blez_op: 554a8ff66f5SMarkos Chandras 555a8ff66f5SMarkos Chandras /* 556a8ff66f5SMarkos Chandras * Compact branches for R6 for the 557a8ff66f5SMarkos Chandras * blez and blezl opcodes. 558a8ff66f5SMarkos Chandras * BLEZ | rs = 0 | rt != 0 == BLEZALC 559a8ff66f5SMarkos Chandras * BLEZ | rs = rt != 0 == BGEZALC 560a8ff66f5SMarkos Chandras * BLEZ | rs != 0 | rt != 0 == BGEUC 561a8ff66f5SMarkos Chandras * BLEZL | rs = 0 | rt != 0 == BLEZC 562a8ff66f5SMarkos Chandras * BLEZL | rs = rt != 0 == BGEZC 563a8ff66f5SMarkos Chandras * BLEZL | rs != 0 | rt != 0 == BGEC 564a8ff66f5SMarkos Chandras * 565a8ff66f5SMarkos Chandras * For real BLEZ{,L}, rt is always 0. 566a8ff66f5SMarkos Chandras */ 567a8ff66f5SMarkos Chandras if (cpu_has_mips_r6 && insn.i_format.rt) { 568a8ff66f5SMarkos Chandras if ((insn.i_format.opcode == blez_op) && 569a8ff66f5SMarkos Chandras ((!insn.i_format.rs && insn.i_format.rt) || 570a8ff66f5SMarkos Chandras (insn.i_format.rs == insn.i_format.rt))) 571a8ff66f5SMarkos Chandras regs->regs[31] = regs->cp0_epc + 572a8ff66f5SMarkos Chandras dec_insn.pc_inc; 573a8ff66f5SMarkos Chandras *contpc = regs->cp0_epc + dec_insn.pc_inc + 574a8ff66f5SMarkos Chandras dec_insn.next_pc_inc; 575a8ff66f5SMarkos Chandras 576a8ff66f5SMarkos Chandras return 1; 577a8ff66f5SMarkos Chandras } 578102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.i_format.rs] <= 0) 579102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 580102cedc3SLeonid Yegoshin dec_insn.pc_inc + 581102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 582102cedc3SLeonid Yegoshin else 583102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 584102cedc3SLeonid Yegoshin dec_insn.pc_inc + 585102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 586102cedc3SLeonid Yegoshin return 1; 587102cedc3SLeonid Yegoshin case bgtzl_op: 588e9d92d22SMarkos Chandras if (!insn.i_format.rt && NO_R6EMU) 589319824eaSMarkos Chandras break; 590c9b02990SLiangliang Huang fallthrough; 591319824eaSMarkos Chandras case bgtz_op: 592f1b44067SMarkos Chandras /* 593f1b44067SMarkos Chandras * Compact branches for R6 for the 594f1b44067SMarkos Chandras * bgtz and bgtzl opcodes. 595f1b44067SMarkos Chandras * BGTZ | rs = 0 | rt != 0 == BGTZALC 596f1b44067SMarkos Chandras * BGTZ | rs = rt != 0 == BLTZALC 597f1b44067SMarkos Chandras * BGTZ | rs != 0 | rt != 0 == BLTUC 598f1b44067SMarkos Chandras * BGTZL | rs = 0 | rt != 0 == BGTZC 599f1b44067SMarkos Chandras * BGTZL | rs = rt != 0 == BLTZC 600f1b44067SMarkos Chandras * BGTZL | rs != 0 | rt != 0 == BLTC 601f1b44067SMarkos Chandras * 602f1b44067SMarkos Chandras * *ZALC varint for BGTZ &&& rt != 0 603f1b44067SMarkos Chandras * For real GTZ{,L}, rt is always 0. 604f1b44067SMarkos Chandras */ 605f1b44067SMarkos Chandras if (cpu_has_mips_r6 && insn.i_format.rt) { 606f1b44067SMarkos Chandras if ((insn.i_format.opcode == blez_op) && 607f1b44067SMarkos Chandras ((!insn.i_format.rs && insn.i_format.rt) || 608f1b44067SMarkos Chandras (insn.i_format.rs == insn.i_format.rt))) 609f1b44067SMarkos Chandras regs->regs[31] = regs->cp0_epc + 610f1b44067SMarkos Chandras dec_insn.pc_inc; 611f1b44067SMarkos Chandras *contpc = regs->cp0_epc + dec_insn.pc_inc + 612f1b44067SMarkos Chandras dec_insn.next_pc_inc; 613f1b44067SMarkos Chandras 614f1b44067SMarkos Chandras return 1; 615f1b44067SMarkos Chandras } 616f1b44067SMarkos Chandras 617102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.i_format.rs] > 0) 618102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 619102cedc3SLeonid Yegoshin dec_insn.pc_inc + 620102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 621102cedc3SLeonid Yegoshin else 622102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 623102cedc3SLeonid Yegoshin dec_insn.pc_inc + 624102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 625102cedc3SLeonid Yegoshin return 1; 6261b492600SPaul Burton case pop10_op: 6271b492600SPaul Burton case pop30_op: 628c893ce38SMarkos Chandras if (!cpu_has_mips_r6) 629c893ce38SMarkos Chandras break; 630c893ce38SMarkos Chandras if (insn.i_format.rt && !insn.i_format.rs) 631c893ce38SMarkos Chandras regs->regs[31] = regs->cp0_epc + 4; 632c893ce38SMarkos Chandras *contpc = regs->cp0_epc + dec_insn.pc_inc + 633c893ce38SMarkos Chandras dec_insn.next_pc_inc; 634c893ce38SMarkos Chandras 635c893ce38SMarkos Chandras return 1; 636c26d4219SDavid Daney #ifdef CONFIG_CPU_CAVIUM_OCTEON 637c26d4219SDavid Daney case lwc2_op: /* This is bbit0 on Octeon */ 638c26d4219SDavid Daney if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) == 0) 639c26d4219SDavid Daney *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); 640c26d4219SDavid Daney else 641c26d4219SDavid Daney *contpc = regs->cp0_epc + 8; 642c26d4219SDavid Daney return 1; 643c26d4219SDavid Daney case ldc2_op: /* This is bbit032 on Octeon */ 644c26d4219SDavid Daney if ((regs->regs[insn.i_format.rs] & (1ull<<(insn.i_format.rt + 32))) == 0) 645c26d4219SDavid Daney *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); 646c26d4219SDavid Daney else 647c26d4219SDavid Daney *contpc = regs->cp0_epc + 8; 648c26d4219SDavid Daney return 1; 649c26d4219SDavid Daney case swc2_op: /* This is bbit1 on Octeon */ 650c26d4219SDavid Daney if (regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) 651c26d4219SDavid Daney *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); 652c26d4219SDavid Daney else 653c26d4219SDavid Daney *contpc = regs->cp0_epc + 8; 654c26d4219SDavid Daney return 1; 655c26d4219SDavid Daney case sdc2_op: /* This is bbit132 on Octeon */ 656c26d4219SDavid Daney if (regs->regs[insn.i_format.rs] & (1ull<<(insn.i_format.rt + 32))) 657c26d4219SDavid Daney *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); 658c26d4219SDavid Daney else 659c26d4219SDavid Daney *contpc = regs->cp0_epc + 8; 660c26d4219SDavid Daney return 1; 6618467ca01SMarkos Chandras #else 6628467ca01SMarkos Chandras case bc6_op: 6638467ca01SMarkos Chandras /* 6648467ca01SMarkos Chandras * Only valid for MIPS R6 but we can still end up 6658467ca01SMarkos Chandras * here from a broken userland so just tell emulator 6668467ca01SMarkos Chandras * this is not a branch and let it break later on. 6678467ca01SMarkos Chandras */ 6688467ca01SMarkos Chandras if (!cpu_has_mips_r6) 6698467ca01SMarkos Chandras break; 6708467ca01SMarkos Chandras *contpc = regs->cp0_epc + dec_insn.pc_inc + 6718467ca01SMarkos Chandras dec_insn.next_pc_inc; 6728467ca01SMarkos Chandras 6738467ca01SMarkos Chandras return 1; 67484fef630SMarkos Chandras case balc6_op: 67584fef630SMarkos Chandras if (!cpu_has_mips_r6) 67684fef630SMarkos Chandras break; 67784fef630SMarkos Chandras regs->regs[31] = regs->cp0_epc + 4; 67884fef630SMarkos Chandras *contpc = regs->cp0_epc + dec_insn.pc_inc + 67984fef630SMarkos Chandras dec_insn.next_pc_inc; 68084fef630SMarkos Chandras 68184fef630SMarkos Chandras return 1; 6821c66b79bSPaul Burton case pop66_op: 68369b9a2fdSMarkos Chandras if (!cpu_has_mips_r6) 68469b9a2fdSMarkos Chandras break; 68569b9a2fdSMarkos Chandras *contpc = regs->cp0_epc + dec_insn.pc_inc + 68669b9a2fdSMarkos Chandras dec_insn.next_pc_inc; 68769b9a2fdSMarkos Chandras 68869b9a2fdSMarkos Chandras return 1; 6891c66b79bSPaul Burton case pop76_op: 69028d6f93dSMarkos Chandras if (!cpu_has_mips_r6) 69128d6f93dSMarkos Chandras break; 69228d6f93dSMarkos Chandras if (!insn.i_format.rs) 69328d6f93dSMarkos Chandras regs->regs[31] = regs->cp0_epc + 4; 69428d6f93dSMarkos Chandras *contpc = regs->cp0_epc + dec_insn.pc_inc + 69528d6f93dSMarkos Chandras dec_insn.next_pc_inc; 69628d6f93dSMarkos Chandras 69728d6f93dSMarkos Chandras return 1; 698c26d4219SDavid Daney #endif 6991da177e4SLinus Torvalds case cop0_op: 7001da177e4SLinus Torvalds case cop1_op: 701c8a34581SMarkos Chandras /* Need to check for R6 bc1nez and bc1eqz branches */ 702c8a34581SMarkos Chandras if (cpu_has_mips_r6 && 703c8a34581SMarkos Chandras ((insn.i_format.rs == bc1eqz_op) || 704c8a34581SMarkos Chandras (insn.i_format.rs == bc1nez_op))) { 705c8a34581SMarkos Chandras bit = 0; 7068bcd84a4SDouglas Leung fpr = ¤t->thread.fpu.fpr[insn.i_format.rt]; 7078bcd84a4SDouglas Leung bit0 = get_fpr32(fpr, 0) & 0x1; 708c8a34581SMarkos Chandras switch (insn.i_format.rs) { 709c8a34581SMarkos Chandras case bc1eqz_op: 7108bcd84a4SDouglas Leung bit = bit0 == 0; 711c8a34581SMarkos Chandras break; 712c8a34581SMarkos Chandras case bc1nez_op: 7138bcd84a4SDouglas Leung bit = bit0 != 0; 714c8a34581SMarkos Chandras break; 715c8a34581SMarkos Chandras } 716c8a34581SMarkos Chandras if (bit) 717c8a34581SMarkos Chandras *contpc = regs->cp0_epc + 718c8a34581SMarkos Chandras dec_insn.pc_inc + 719c8a34581SMarkos Chandras (insn.i_format.simmediate << 2); 720c8a34581SMarkos Chandras else 721c8a34581SMarkos Chandras *contpc = regs->cp0_epc + 722c8a34581SMarkos Chandras dec_insn.pc_inc + 723c8a34581SMarkos Chandras dec_insn.next_pc_inc; 724c8a34581SMarkos Chandras 725c8a34581SMarkos Chandras return 1; 726c8a34581SMarkos Chandras } 7272a14b21aSAleksandar Markovic /* R2/R6 compatible cop1 instruction */ 728c9b02990SLiangliang Huang fallthrough; 7291da177e4SLinus Torvalds case cop2_op: 7301da177e4SLinus Torvalds case cop1x_op: 731102cedc3SLeonid Yegoshin if (insn.i_format.rs == bc_op) { 732102cedc3SLeonid Yegoshin preempt_disable(); 733102cedc3SLeonid Yegoshin if (is_fpu_owner()) 734842dfc11SManuel Lauss fcr31 = read_32bit_cp1_register(CP1_STATUS); 735102cedc3SLeonid Yegoshin else 736102cedc3SLeonid Yegoshin fcr31 = current->thread.fpu.fcr31; 737102cedc3SLeonid Yegoshin preempt_enable(); 738102cedc3SLeonid Yegoshin 739102cedc3SLeonid Yegoshin bit = (insn.i_format.rt >> 2); 740102cedc3SLeonid Yegoshin bit += (bit != 0); 741102cedc3SLeonid Yegoshin bit += 23; 742102cedc3SLeonid Yegoshin switch (insn.i_format.rt & 3) { 743102cedc3SLeonid Yegoshin case 0: /* bc1f */ 744102cedc3SLeonid Yegoshin case 2: /* bc1fl */ 745102cedc3SLeonid Yegoshin if (~fcr31 & (1 << bit)) 746102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 747102cedc3SLeonid Yegoshin dec_insn.pc_inc + 748102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 749102cedc3SLeonid Yegoshin else 750102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 751102cedc3SLeonid Yegoshin dec_insn.pc_inc + 752102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 753102cedc3SLeonid Yegoshin return 1; 754102cedc3SLeonid Yegoshin case 1: /* bc1t */ 755102cedc3SLeonid Yegoshin case 3: /* bc1tl */ 756102cedc3SLeonid Yegoshin if (fcr31 & (1 << bit)) 757102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 758102cedc3SLeonid Yegoshin dec_insn.pc_inc + 759102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 760102cedc3SLeonid Yegoshin else 761102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 762102cedc3SLeonid Yegoshin dec_insn.pc_inc + 763102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 7641da177e4SLinus Torvalds return 1; 7651da177e4SLinus Torvalds } 766102cedc3SLeonid Yegoshin } 767102cedc3SLeonid Yegoshin break; 768102cedc3SLeonid Yegoshin } 7691da177e4SLinus Torvalds return 0; 7701da177e4SLinus Torvalds } 7711da177e4SLinus Torvalds 7721da177e4SLinus Torvalds /* 7731da177e4SLinus Torvalds * In the Linux kernel, we support selection of FPR format on the 774da0bac33SDavid Daney * basis of the Status.FR bit. If an FPU is not present, the FR bit 775da0bac33SDavid Daney * is hardwired to zero, which would imply a 32-bit FPU even for 776597ce172SPaul Burton * 64-bit CPUs so we rather look at TIF_32BIT_FPREGS. 77751d943f0SRalf Baechle * FPU emu is slow and bulky and optimizing this function offers fairly 77851d943f0SRalf Baechle * sizeable benefits so we try to be clever and make this function return 77951d943f0SRalf Baechle * a constant whenever possible, that is on 64-bit kernels without O32 780597ce172SPaul Burton * compatibility enabled and on 32-bit without 64-bit FPU support. 7811da177e4SLinus Torvalds */ 782da0bac33SDavid Daney static inline int cop1_64bit(struct pt_regs *xcp) 783da0bac33SDavid Daney { 78497f2645fSMasahiro Yamada if (IS_ENABLED(CONFIG_64BIT) && !IS_ENABLED(CONFIG_MIPS32_O32)) 78551d943f0SRalf Baechle return 1; 78697f2645fSMasahiro Yamada else if (IS_ENABLED(CONFIG_32BIT) && 78797f2645fSMasahiro Yamada !IS_ENABLED(CONFIG_MIPS_O32_FP64_SUPPORT)) 788da0bac33SDavid Daney return 0; 78908a07904SRalf Baechle 790597ce172SPaul Burton return !test_thread_flag(TIF_32BIT_FPREGS); 791da0bac33SDavid Daney } 7921da177e4SLinus Torvalds 7934227a2d4SPaul Burton static inline bool hybrid_fprs(void) 7944227a2d4SPaul Burton { 7954227a2d4SPaul Burton return test_thread_flag(TIF_HYBRID_FPREGS); 7964227a2d4SPaul Burton } 7974227a2d4SPaul Burton 79847fa0c02SRalf Baechle #define SIFROMREG(si, x) \ 79947fa0c02SRalf Baechle do { \ 8004227a2d4SPaul Burton if (cop1_64bit(xcp) && !hybrid_fprs()) \ 801c8c0da6bSPaul Burton (si) = (int)get_fpr32(&ctx->fpr[x], 0); \ 802bbd426f5SPaul Burton else \ 803c8c0da6bSPaul Burton (si) = (int)get_fpr32(&ctx->fpr[(x) & ~1], (x) & 1); \ 804bbd426f5SPaul Burton } while (0) 805da0bac33SDavid Daney 80647fa0c02SRalf Baechle #define SITOREG(si, x) \ 80747fa0c02SRalf Baechle do { \ 8084227a2d4SPaul Burton if (cop1_64bit(xcp) && !hybrid_fprs()) { \ 809a58f85b5SAleksandar Markovic unsigned int i; \ 810bbd426f5SPaul Burton set_fpr32(&ctx->fpr[x], 0, si); \ 811ef1c47afSPaul Burton for (i = 1; i < ARRAY_SIZE(ctx->fpr[x].val32); i++) \ 812ef1c47afSPaul Burton set_fpr32(&ctx->fpr[x], i, 0); \ 813ef1c47afSPaul Burton } else { \ 814bbd426f5SPaul Burton set_fpr32(&ctx->fpr[(x) & ~1], (x) & 1, si); \ 815ef1c47afSPaul Burton } \ 816bbd426f5SPaul Burton } while (0) 8171da177e4SLinus Torvalds 818c8c0da6bSPaul Burton #define SIFROMHREG(si, x) ((si) = (int)get_fpr32(&ctx->fpr[x], 1)) 819ef1c47afSPaul Burton 82047fa0c02SRalf Baechle #define SITOHREG(si, x) \ 82147fa0c02SRalf Baechle do { \ 822a58f85b5SAleksandar Markovic unsigned int i; \ 823ef1c47afSPaul Burton set_fpr32(&ctx->fpr[x], 1, si); \ 824ef1c47afSPaul Burton for (i = 2; i < ARRAY_SIZE(ctx->fpr[x].val32); i++) \ 825ef1c47afSPaul Burton set_fpr32(&ctx->fpr[x], i, 0); \ 826ef1c47afSPaul Burton } while (0) 8271ac94400SLeonid Yegoshin 828bbd426f5SPaul Burton #define DIFROMREG(di, x) \ 8298535f2baSManuel Lauss ((di) = get_fpr64(&ctx->fpr[(x) & ~(cop1_64bit(xcp) ^ 1)], 0)) 830bbd426f5SPaul Burton 83147fa0c02SRalf Baechle #define DITOREG(di, x) \ 83247fa0c02SRalf Baechle do { \ 833a58f85b5SAleksandar Markovic unsigned int fpr, i; \ 8348535f2baSManuel Lauss fpr = (x) & ~(cop1_64bit(xcp) ^ 1); \ 835ef1c47afSPaul Burton set_fpr64(&ctx->fpr[fpr], 0, di); \ 836ef1c47afSPaul Burton for (i = 1; i < ARRAY_SIZE(ctx->fpr[x].val64); i++) \ 837ef1c47afSPaul Burton set_fpr64(&ctx->fpr[fpr], i, 0); \ 838ef1c47afSPaul Burton } while (0) 8391da177e4SLinus Torvalds 8401da177e4SLinus Torvalds #define SPFROMREG(sp, x) SIFROMREG((sp).bits, x) 8411da177e4SLinus Torvalds #define SPTOREG(sp, x) SITOREG((sp).bits, x) 8421da177e4SLinus Torvalds #define DPFROMREG(dp, x) DIFROMREG((dp).bits, x) 8431da177e4SLinus Torvalds #define DPTOREG(dp, x) DITOREG((dp).bits, x) 8441da177e4SLinus Torvalds 8451da177e4SLinus Torvalds /* 846d4f5b088SMaciej W. Rozycki * Emulate a CFC1 instruction. 847d4f5b088SMaciej W. Rozycki */ 848d4f5b088SMaciej W. Rozycki static inline void cop1_cfc(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 849d4f5b088SMaciej W. Rozycki mips_instruction ir) 850d4f5b088SMaciej W. Rozycki { 851c491cfa2SMaciej W. Rozycki u32 fcr31 = ctx->fcr31; 852c491cfa2SMaciej W. Rozycki u32 value = 0; 853d4f5b088SMaciej W. Rozycki 854c491cfa2SMaciej W. Rozycki switch (MIPSInst_RD(ir)) { 855c491cfa2SMaciej W. Rozycki case FPCREG_CSR: 856c491cfa2SMaciej W. Rozycki value = fcr31; 857d4f5b088SMaciej W. Rozycki pr_debug("%p gpr[%d]<-csr=%08x\n", 858c491cfa2SMaciej W. Rozycki (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 859c491cfa2SMaciej W. Rozycki break; 860c491cfa2SMaciej W. Rozycki 861c491cfa2SMaciej W. Rozycki case FPCREG_FENR: 862c491cfa2SMaciej W. Rozycki if (!cpu_has_mips_r) 863c491cfa2SMaciej W. Rozycki break; 864c491cfa2SMaciej W. Rozycki value = (fcr31 >> (FPU_CSR_FS_S - MIPS_FENR_FS_S)) & 865c491cfa2SMaciej W. Rozycki MIPS_FENR_FS; 866c491cfa2SMaciej W. Rozycki value |= fcr31 & (FPU_CSR_ALL_E | FPU_CSR_RM); 867c491cfa2SMaciej W. Rozycki pr_debug("%p gpr[%d]<-enr=%08x\n", 868c491cfa2SMaciej W. Rozycki (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 869c491cfa2SMaciej W. Rozycki break; 870c491cfa2SMaciej W. Rozycki 871c491cfa2SMaciej W. Rozycki case FPCREG_FEXR: 872c491cfa2SMaciej W. Rozycki if (!cpu_has_mips_r) 873c491cfa2SMaciej W. Rozycki break; 874c491cfa2SMaciej W. Rozycki value = fcr31 & (FPU_CSR_ALL_X | FPU_CSR_ALL_S); 875c491cfa2SMaciej W. Rozycki pr_debug("%p gpr[%d]<-exr=%08x\n", 876c491cfa2SMaciej W. Rozycki (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 877c491cfa2SMaciej W. Rozycki break; 878c491cfa2SMaciej W. Rozycki 879c491cfa2SMaciej W. Rozycki case FPCREG_FCCR: 880c491cfa2SMaciej W. Rozycki if (!cpu_has_mips_r) 881c491cfa2SMaciej W. Rozycki break; 882c491cfa2SMaciej W. Rozycki value = (fcr31 >> (FPU_CSR_COND_S - MIPS_FCCR_COND0_S)) & 883c491cfa2SMaciej W. Rozycki MIPS_FCCR_COND0; 884c491cfa2SMaciej W. Rozycki value |= (fcr31 >> (FPU_CSR_COND1_S - MIPS_FCCR_COND1_S)) & 885c491cfa2SMaciej W. Rozycki (MIPS_FCCR_CONDX & ~MIPS_FCCR_COND0); 886c491cfa2SMaciej W. Rozycki pr_debug("%p gpr[%d]<-ccr=%08x\n", 887c491cfa2SMaciej W. Rozycki (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 888c491cfa2SMaciej W. Rozycki break; 889c491cfa2SMaciej W. Rozycki 890c491cfa2SMaciej W. Rozycki case FPCREG_RID: 89103dce595SMaciej W. Rozycki value = boot_cpu_data.fpu_id; 892c491cfa2SMaciej W. Rozycki break; 893c491cfa2SMaciej W. Rozycki 894c491cfa2SMaciej W. Rozycki default: 895c491cfa2SMaciej W. Rozycki break; 896c491cfa2SMaciej W. Rozycki } 897c491cfa2SMaciej W. Rozycki 898d4f5b088SMaciej W. Rozycki if (MIPSInst_RT(ir)) 899d4f5b088SMaciej W. Rozycki xcp->regs[MIPSInst_RT(ir)] = value; 900d4f5b088SMaciej W. Rozycki } 901d4f5b088SMaciej W. Rozycki 902d4f5b088SMaciej W. Rozycki /* 903d4f5b088SMaciej W. Rozycki * Emulate a CTC1 instruction. 904d4f5b088SMaciej W. Rozycki */ 905d4f5b088SMaciej W. Rozycki static inline void cop1_ctc(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 906d4f5b088SMaciej W. Rozycki mips_instruction ir) 907d4f5b088SMaciej W. Rozycki { 908c491cfa2SMaciej W. Rozycki u32 fcr31 = ctx->fcr31; 909d4f5b088SMaciej W. Rozycki u32 value; 9109b26616cSMaciej W. Rozycki u32 mask; 911d4f5b088SMaciej W. Rozycki 912d4f5b088SMaciej W. Rozycki if (MIPSInst_RT(ir) == 0) 913d4f5b088SMaciej W. Rozycki value = 0; 914d4f5b088SMaciej W. Rozycki else 915d4f5b088SMaciej W. Rozycki value = xcp->regs[MIPSInst_RT(ir)]; 916d4f5b088SMaciej W. Rozycki 917c491cfa2SMaciej W. Rozycki switch (MIPSInst_RD(ir)) { 918c491cfa2SMaciej W. Rozycki case FPCREG_CSR: 919d4f5b088SMaciej W. Rozycki pr_debug("%p gpr[%d]->csr=%08x\n", 920c491cfa2SMaciej W. Rozycki (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 921d4f5b088SMaciej W. Rozycki 9229b26616cSMaciej W. Rozycki /* Preserve read-only bits. */ 92303dce595SMaciej W. Rozycki mask = boot_cpu_data.fpu_msk31; 9249b26616cSMaciej W. Rozycki fcr31 = (value & ~mask) | (fcr31 & mask); 925c491cfa2SMaciej W. Rozycki break; 926c491cfa2SMaciej W. Rozycki 927c491cfa2SMaciej W. Rozycki case FPCREG_FENR: 928c491cfa2SMaciej W. Rozycki if (!cpu_has_mips_r) 929c491cfa2SMaciej W. Rozycki break; 930c491cfa2SMaciej W. Rozycki pr_debug("%p gpr[%d]->enr=%08x\n", 931c491cfa2SMaciej W. Rozycki (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 932c491cfa2SMaciej W. Rozycki fcr31 &= ~(FPU_CSR_FS | FPU_CSR_ALL_E | FPU_CSR_RM); 933c491cfa2SMaciej W. Rozycki fcr31 |= (value << (FPU_CSR_FS_S - MIPS_FENR_FS_S)) & 934c491cfa2SMaciej W. Rozycki FPU_CSR_FS; 935c491cfa2SMaciej W. Rozycki fcr31 |= value & (FPU_CSR_ALL_E | FPU_CSR_RM); 936c491cfa2SMaciej W. Rozycki break; 937c491cfa2SMaciej W. Rozycki 938c491cfa2SMaciej W. Rozycki case FPCREG_FEXR: 939c491cfa2SMaciej W. Rozycki if (!cpu_has_mips_r) 940c491cfa2SMaciej W. Rozycki break; 941c491cfa2SMaciej W. Rozycki pr_debug("%p gpr[%d]->exr=%08x\n", 942c491cfa2SMaciej W. Rozycki (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 943c491cfa2SMaciej W. Rozycki fcr31 &= ~(FPU_CSR_ALL_X | FPU_CSR_ALL_S); 944c491cfa2SMaciej W. Rozycki fcr31 |= value & (FPU_CSR_ALL_X | FPU_CSR_ALL_S); 945c491cfa2SMaciej W. Rozycki break; 946c491cfa2SMaciej W. Rozycki 947c491cfa2SMaciej W. Rozycki case FPCREG_FCCR: 948c491cfa2SMaciej W. Rozycki if (!cpu_has_mips_r) 949c491cfa2SMaciej W. Rozycki break; 950c491cfa2SMaciej W. Rozycki pr_debug("%p gpr[%d]->ccr=%08x\n", 951c491cfa2SMaciej W. Rozycki (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 952c491cfa2SMaciej W. Rozycki fcr31 &= ~(FPU_CSR_CONDX | FPU_CSR_COND); 953c491cfa2SMaciej W. Rozycki fcr31 |= (value << (FPU_CSR_COND_S - MIPS_FCCR_COND0_S)) & 954c491cfa2SMaciej W. Rozycki FPU_CSR_COND; 955c491cfa2SMaciej W. Rozycki fcr31 |= (value << (FPU_CSR_COND1_S - MIPS_FCCR_COND1_S)) & 956c491cfa2SMaciej W. Rozycki FPU_CSR_CONDX; 957c491cfa2SMaciej W. Rozycki break; 958c491cfa2SMaciej W. Rozycki 959c491cfa2SMaciej W. Rozycki default: 960c491cfa2SMaciej W. Rozycki break; 961d4f5b088SMaciej W. Rozycki } 962c491cfa2SMaciej W. Rozycki 963c491cfa2SMaciej W. Rozycki ctx->fcr31 = fcr31; 964d4f5b088SMaciej W. Rozycki } 965d4f5b088SMaciej W. Rozycki 966d4f5b088SMaciej W. Rozycki /* 9671da177e4SLinus Torvalds * Emulate the single floating point instruction pointed at by EPC. 9681da177e4SLinus Torvalds * Two instructions if the instruction is in a branch delay slot. 9691da177e4SLinus Torvalds */ 9701da177e4SLinus Torvalds 971515b029dSDavid Daney static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 972445a58ceSPaul Burton struct mm_decoded_insn dec_insn, void __user **fault_addr) 9731da177e4SLinus Torvalds { 974102cedc3SLeonid Yegoshin unsigned long contpc = xcp->cp0_epc + dec_insn.pc_inc; 97593583e17SPaul Burton unsigned int cond, cbit, bit0; 9763f7cac41SRalf Baechle mips_instruction ir; 9773f7cac41SRalf Baechle int likely, pc_inc; 97893583e17SPaul Burton union fpureg *fpr; 9793f7cac41SRalf Baechle u32 __user *wva; 9803f7cac41SRalf Baechle u64 __user *dva; 9813f7cac41SRalf Baechle u32 wval; 9823f7cac41SRalf Baechle u64 dval; 9833f7cac41SRalf Baechle int sig; 9841da177e4SLinus Torvalds 98570e4c234SRalf Baechle /* 98670e4c234SRalf Baechle * These are giving gcc a gentle hint about what to expect in 98770e4c234SRalf Baechle * dec_inst in order to do better optimization. 98870e4c234SRalf Baechle */ 98970e4c234SRalf Baechle if (!cpu_has_mmips && dec_insn.micro_mips_mode) 99070e4c234SRalf Baechle unreachable(); 99170e4c234SRalf Baechle 9921da177e4SLinus Torvalds /* XXX NEC Vr54xx bug workaround */ 993e7e9cae5SRalf Baechle if (delay_slot(xcp)) { 994102cedc3SLeonid Yegoshin if (dec_insn.micro_mips_mode) { 995102cedc3SLeonid Yegoshin if (!mm_isBranchInstr(xcp, dec_insn, &contpc)) 996e7e9cae5SRalf Baechle clear_delay_slot(xcp); 997102cedc3SLeonid Yegoshin } else { 998102cedc3SLeonid Yegoshin if (!isBranchInstr(xcp, dec_insn, &contpc)) 999e7e9cae5SRalf Baechle clear_delay_slot(xcp); 1000102cedc3SLeonid Yegoshin } 1001102cedc3SLeonid Yegoshin } 10021da177e4SLinus Torvalds 1003e7e9cae5SRalf Baechle if (delay_slot(xcp)) { 10041da177e4SLinus Torvalds /* 10051da177e4SLinus Torvalds * The instruction to be emulated is in a branch delay slot 10061da177e4SLinus Torvalds * which means that we have to emulate the branch instruction 10071da177e4SLinus Torvalds * BEFORE we do the cop1 instruction. 10081da177e4SLinus Torvalds * 10091da177e4SLinus Torvalds * This branch could be a COP1 branch, but in that case we 10101da177e4SLinus Torvalds * would have had a trap for that instruction, and would not 10111da177e4SLinus Torvalds * come through this route. 10121da177e4SLinus Torvalds * 10131da177e4SLinus Torvalds * Linux MIPS branch emulator operates on context, updating the 10141da177e4SLinus Torvalds * cp0_epc. 10151da177e4SLinus Torvalds */ 1016102cedc3SLeonid Yegoshin ir = dec_insn.next_insn; /* process delay slot instr */ 1017102cedc3SLeonid Yegoshin pc_inc = dec_insn.next_pc_inc; 1018333d1f67SRalf Baechle } else { 1019102cedc3SLeonid Yegoshin ir = dec_insn.insn; /* process current instr */ 1020102cedc3SLeonid Yegoshin pc_inc = dec_insn.pc_inc; 1021102cedc3SLeonid Yegoshin } 1022102cedc3SLeonid Yegoshin 1023102cedc3SLeonid Yegoshin /* 1024102cedc3SLeonid Yegoshin * Since microMIPS FPU instructios are a subset of MIPS32 FPU 1025102cedc3SLeonid Yegoshin * instructions, we want to convert microMIPS FPU instructions 1026102cedc3SLeonid Yegoshin * into MIPS32 instructions so that we could reuse all of the 1027102cedc3SLeonid Yegoshin * FPU emulation code. 1028102cedc3SLeonid Yegoshin * 1029102cedc3SLeonid Yegoshin * NOTE: We cannot do this for branch instructions since they 1030102cedc3SLeonid Yegoshin * are not a subset. Example: Cannot emulate a 16-bit 1031102cedc3SLeonid Yegoshin * aligned target address with a MIPS32 instruction. 1032102cedc3SLeonid Yegoshin */ 1033102cedc3SLeonid Yegoshin if (dec_insn.micro_mips_mode) { 1034102cedc3SLeonid Yegoshin /* 1035*edeed424SJason Wang * If next instruction is a 16-bit instruction, then 1036102cedc3SLeonid Yegoshin * it cannot be a FPU instruction. This could happen 1037102cedc3SLeonid Yegoshin * since we can be called for non-FPU instructions. 1038102cedc3SLeonid Yegoshin */ 1039102cedc3SLeonid Yegoshin if ((pc_inc == 2) || 1040102cedc3SLeonid Yegoshin (microMIPS32_to_MIPS32((union mips_instruction *)&ir) 1041102cedc3SLeonid Yegoshin == SIGILL)) 1042102cedc3SLeonid Yegoshin return SIGILL; 10431da177e4SLinus Torvalds } 10441da177e4SLinus Torvalds 10451da177e4SLinus Torvalds emul: 1046a8b0ca17SPeter Zijlstra perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, xcp, 0); 1047b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(emulated); 10481da177e4SLinus Torvalds switch (MIPSInst_OPCODE(ir)) { 10493f7cac41SRalf Baechle case ldc1_op: 10503f7cac41SRalf Baechle dva = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] + 10511da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 1052b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(loads); 1053515b029dSDavid Daney 105496d4f267SLinus Torvalds if (!access_ok(dva, sizeof(u64))) { 1055b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 10563f7cac41SRalf Baechle *fault_addr = dva; 10571da177e4SLinus Torvalds return SIGBUS; 10581da177e4SLinus Torvalds } 10593f7cac41SRalf Baechle if (__get_user(dval, dva)) { 1060515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 10613f7cac41SRalf Baechle *fault_addr = dva; 1062515b029dSDavid Daney return SIGSEGV; 1063515b029dSDavid Daney } 10643f7cac41SRalf Baechle DITOREG(dval, MIPSInst_RT(ir)); 10651da177e4SLinus Torvalds break; 10661da177e4SLinus Torvalds 10673f7cac41SRalf Baechle case sdc1_op: 10683f7cac41SRalf Baechle dva = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] + 10691da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 1070b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(stores); 10713f7cac41SRalf Baechle DIFROMREG(dval, MIPSInst_RT(ir)); 107296d4f267SLinus Torvalds if (!access_ok(dva, sizeof(u64))) { 1073b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 10743f7cac41SRalf Baechle *fault_addr = dva; 10751da177e4SLinus Torvalds return SIGBUS; 10761da177e4SLinus Torvalds } 10773f7cac41SRalf Baechle if (__put_user(dval, dva)) { 1078515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 10793f7cac41SRalf Baechle *fault_addr = dva; 1080515b029dSDavid Daney return SIGSEGV; 1081515b029dSDavid Daney } 10821da177e4SLinus Torvalds break; 10831da177e4SLinus Torvalds 10843f7cac41SRalf Baechle case lwc1_op: 10853f7cac41SRalf Baechle wva = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] + 10861da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 1087b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(loads); 108896d4f267SLinus Torvalds if (!access_ok(wva, sizeof(u32))) { 1089b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 10903f7cac41SRalf Baechle *fault_addr = wva; 10911da177e4SLinus Torvalds return SIGBUS; 10921da177e4SLinus Torvalds } 10933f7cac41SRalf Baechle if (__get_user(wval, wva)) { 1094515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 10953f7cac41SRalf Baechle *fault_addr = wva; 1096515b029dSDavid Daney return SIGSEGV; 1097515b029dSDavid Daney } 10983f7cac41SRalf Baechle SITOREG(wval, MIPSInst_RT(ir)); 10991da177e4SLinus Torvalds break; 11001da177e4SLinus Torvalds 11013f7cac41SRalf Baechle case swc1_op: 11023f7cac41SRalf Baechle wva = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] + 11031da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 1104b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(stores); 11053f7cac41SRalf Baechle SIFROMREG(wval, MIPSInst_RT(ir)); 110696d4f267SLinus Torvalds if (!access_ok(wva, sizeof(u32))) { 1107b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 11083f7cac41SRalf Baechle *fault_addr = wva; 11091da177e4SLinus Torvalds return SIGBUS; 11101da177e4SLinus Torvalds } 11113f7cac41SRalf Baechle if (__put_user(wval, wva)) { 1112515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 11133f7cac41SRalf Baechle *fault_addr = wva; 1114515b029dSDavid Daney return SIGSEGV; 1115515b029dSDavid Daney } 11161da177e4SLinus Torvalds break; 11171da177e4SLinus Torvalds 11181da177e4SLinus Torvalds case cop1_op: 11191da177e4SLinus Torvalds switch (MIPSInst_RS(ir)) { 11201da177e4SLinus Torvalds case dmfc_op: 112108a07904SRalf Baechle if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) 112208a07904SRalf Baechle return SIGILL; 112308a07904SRalf Baechle 11241da177e4SLinus Torvalds /* copregister fs -> gpr[rt] */ 11251da177e4SLinus Torvalds if (MIPSInst_RT(ir) != 0) { 11261da177e4SLinus Torvalds DIFROMREG(xcp->regs[MIPSInst_RT(ir)], 11271da177e4SLinus Torvalds MIPSInst_RD(ir)); 11281da177e4SLinus Torvalds } 11291da177e4SLinus Torvalds break; 11301da177e4SLinus Torvalds 11311da177e4SLinus Torvalds case dmtc_op: 113208a07904SRalf Baechle if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) 113308a07904SRalf Baechle return SIGILL; 113408a07904SRalf Baechle 11351da177e4SLinus Torvalds /* copregister fs <- rt */ 11361da177e4SLinus Torvalds DITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); 11371da177e4SLinus Torvalds break; 11381da177e4SLinus Torvalds 11391ac94400SLeonid Yegoshin case mfhc_op: 1140e8f80cc1SMarkos Chandras if (!cpu_has_mips_r2_r6) 114170f743d1SMaciej W. Rozycki return SIGILL; 11421ac94400SLeonid Yegoshin 11431ac94400SLeonid Yegoshin /* copregister rd -> gpr[rt] */ 11441ac94400SLeonid Yegoshin if (MIPSInst_RT(ir) != 0) { 11451ac94400SLeonid Yegoshin SIFROMHREG(xcp->regs[MIPSInst_RT(ir)], 11461ac94400SLeonid Yegoshin MIPSInst_RD(ir)); 11471ac94400SLeonid Yegoshin } 11481ac94400SLeonid Yegoshin break; 11491ac94400SLeonid Yegoshin 11501ac94400SLeonid Yegoshin case mthc_op: 1151e8f80cc1SMarkos Chandras if (!cpu_has_mips_r2_r6) 115270f743d1SMaciej W. Rozycki return SIGILL; 11531ac94400SLeonid Yegoshin 11541ac94400SLeonid Yegoshin /* copregister rd <- gpr[rt] */ 11551ac94400SLeonid Yegoshin SITOHREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); 11561ac94400SLeonid Yegoshin break; 11571ac94400SLeonid Yegoshin 11581da177e4SLinus Torvalds case mfc_op: 11591da177e4SLinus Torvalds /* copregister rd -> gpr[rt] */ 11601da177e4SLinus Torvalds if (MIPSInst_RT(ir) != 0) { 11611da177e4SLinus Torvalds SIFROMREG(xcp->regs[MIPSInst_RT(ir)], 11621da177e4SLinus Torvalds MIPSInst_RD(ir)); 11631da177e4SLinus Torvalds } 11641da177e4SLinus Torvalds break; 11651da177e4SLinus Torvalds 11661da177e4SLinus Torvalds case mtc_op: 11671da177e4SLinus Torvalds /* copregister rd <- rt */ 11681da177e4SLinus Torvalds SITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); 11691da177e4SLinus Torvalds break; 11701da177e4SLinus Torvalds 11713f7cac41SRalf Baechle case cfc_op: 11721da177e4SLinus Torvalds /* cop control register rd -> gpr[rt] */ 1173d4f5b088SMaciej W. Rozycki cop1_cfc(xcp, ctx, ir); 11741da177e4SLinus Torvalds break; 11751da177e4SLinus Torvalds 11763f7cac41SRalf Baechle case ctc_op: 11771da177e4SLinus Torvalds /* copregister rd <- rt */ 1178d4f5b088SMaciej W. Rozycki cop1_ctc(xcp, ctx, ir); 11791da177e4SLinus Torvalds if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 11801da177e4SLinus Torvalds return SIGFPE; 11811da177e4SLinus Torvalds } 11821da177e4SLinus Torvalds break; 11831da177e4SLinus Torvalds 1184c909ca71SMarkos Chandras case bc1eqz_op: 1185c909ca71SMarkos Chandras case bc1nez_op: 1186c909ca71SMarkos Chandras if (!cpu_has_mips_r6 || delay_slot(xcp)) 1187c909ca71SMarkos Chandras return SIGILL; 1188c909ca71SMarkos Chandras 118961100500SAleksandar Markovic likely = 0; 119061100500SAleksandar Markovic cond = 0; 119193583e17SPaul Burton fpr = ¤t->thread.fpu.fpr[MIPSInst_RT(ir)]; 119293583e17SPaul Burton bit0 = get_fpr32(fpr, 0) & 0x1; 1193c909ca71SMarkos Chandras switch (MIPSInst_RS(ir)) { 1194c909ca71SMarkos Chandras case bc1eqz_op: 1195454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(bc1eqz); 119693583e17SPaul Burton cond = bit0 == 0; 1197c909ca71SMarkos Chandras break; 1198c909ca71SMarkos Chandras case bc1nez_op: 1199454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(bc1nez); 120093583e17SPaul Burton cond = bit0 != 0; 1201c909ca71SMarkos Chandras break; 1202c909ca71SMarkos Chandras } 1203c909ca71SMarkos Chandras goto branch_common; 1204c909ca71SMarkos Chandras 12053f7cac41SRalf Baechle case bc_op: 1206e7e9cae5SRalf Baechle if (delay_slot(xcp)) 12071da177e4SLinus Torvalds return SIGILL; 12081da177e4SLinus Torvalds 120908a07904SRalf Baechle if (cpu_has_mips_4_5_r) 121008a07904SRalf Baechle cbit = fpucondbit[MIPSInst_RT(ir) >> 2]; 121108a07904SRalf Baechle else 121208a07904SRalf Baechle cbit = FPU_CSR_COND; 121308a07904SRalf Baechle cond = ctx->fcr31 & cbit; 121408a07904SRalf Baechle 12153f7cac41SRalf Baechle likely = 0; 12161da177e4SLinus Torvalds switch (MIPSInst_RT(ir) & 3) { 12171da177e4SLinus Torvalds case bcfl_op: 12182d83fea7SMaciej W. Rozycki if (cpu_has_mips_2_3_4_5_r) 12191da177e4SLinus Torvalds likely = 1; 1220c9b02990SLiangliang Huang fallthrough; 12211da177e4SLinus Torvalds case bcf_op: 12221da177e4SLinus Torvalds cond = !cond; 12231da177e4SLinus Torvalds break; 12241da177e4SLinus Torvalds case bctl_op: 12252d83fea7SMaciej W. Rozycki if (cpu_has_mips_2_3_4_5_r) 12261da177e4SLinus Torvalds likely = 1; 1227c9b02990SLiangliang Huang fallthrough; 12281da177e4SLinus Torvalds case bct_op: 12291da177e4SLinus Torvalds break; 12301da177e4SLinus Torvalds } 1231c909ca71SMarkos Chandras branch_common: 1232ae5f3f5bSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(branches); 1233e7e9cae5SRalf Baechle set_delay_slot(xcp); 12341da177e4SLinus Torvalds if (cond) { 12353f7cac41SRalf Baechle /* 12363f7cac41SRalf Baechle * Branch taken: emulate dslot instruction 12371da177e4SLinus Torvalds */ 12389ab4471cSMaciej W. Rozycki unsigned long bcpc; 12399ab4471cSMaciej W. Rozycki 12409ab4471cSMaciej W. Rozycki /* 12419ab4471cSMaciej W. Rozycki * Remember EPC at the branch to point back 12429ab4471cSMaciej W. Rozycki * at so that any delay-slot instruction 12439ab4471cSMaciej W. Rozycki * signal is not silently ignored. 12449ab4471cSMaciej W. Rozycki */ 12459ab4471cSMaciej W. Rozycki bcpc = xcp->cp0_epc; 1246102cedc3SLeonid Yegoshin xcp->cp0_epc += dec_insn.pc_inc; 12471da177e4SLinus Torvalds 1248102cedc3SLeonid Yegoshin contpc = MIPSInst_SIMM(ir); 1249102cedc3SLeonid Yegoshin ir = dec_insn.next_insn; 1250102cedc3SLeonid Yegoshin if (dec_insn.micro_mips_mode) { 1251102cedc3SLeonid Yegoshin contpc = (xcp->cp0_epc + (contpc << 1)); 1252102cedc3SLeonid Yegoshin 1253102cedc3SLeonid Yegoshin /* If 16-bit instruction, not FPU. */ 1254102cedc3SLeonid Yegoshin if ((dec_insn.next_pc_inc == 2) || 1255102cedc3SLeonid Yegoshin (microMIPS32_to_MIPS32((union mips_instruction *)&ir) == SIGILL)) { 1256102cedc3SLeonid Yegoshin 1257102cedc3SLeonid Yegoshin /* 1258102cedc3SLeonid Yegoshin * Since this instruction will 1259102cedc3SLeonid Yegoshin * be put on the stack with 1260102cedc3SLeonid Yegoshin * 32-bit words, get around 1261102cedc3SLeonid Yegoshin * this problem by putting a 1262102cedc3SLeonid Yegoshin * NOP16 as the second one. 1263102cedc3SLeonid Yegoshin */ 1264102cedc3SLeonid Yegoshin if (dec_insn.next_pc_inc == 2) 1265102cedc3SLeonid Yegoshin ir = (ir & (~0xffff)) | MM_NOP16; 1266102cedc3SLeonid Yegoshin 1267102cedc3SLeonid Yegoshin /* 1268102cedc3SLeonid Yegoshin * Single step the non-CP1 1269102cedc3SLeonid Yegoshin * instruction in the dslot. 1270102cedc3SLeonid Yegoshin */ 12719ab4471cSMaciej W. Rozycki sig = mips_dsemul(xcp, ir, 1272432c6bacSPaul Burton bcpc, contpc); 1273e4553573SMaciej W. Rozycki if (sig < 0) 1274e4553573SMaciej W. Rozycki break; 12759ab4471cSMaciej W. Rozycki if (sig) 12769ab4471cSMaciej W. Rozycki xcp->cp0_epc = bcpc; 12779ab4471cSMaciej W. Rozycki /* 12789ab4471cSMaciej W. Rozycki * SIGILL forces out of 12799ab4471cSMaciej W. Rozycki * the emulation loop. 12809ab4471cSMaciej W. Rozycki */ 12819ab4471cSMaciej W. Rozycki return sig ? sig : SIGILL; 1282515b029dSDavid Daney } 1283102cedc3SLeonid Yegoshin } else 1284102cedc3SLeonid Yegoshin contpc = (xcp->cp0_epc + (contpc << 2)); 12851da177e4SLinus Torvalds 12861da177e4SLinus Torvalds switch (MIPSInst_OPCODE(ir)) { 12871da177e4SLinus Torvalds case lwc1_op: 12881da177e4SLinus Torvalds case swc1_op: 128908a07904SRalf Baechle goto emul; 12903f7cac41SRalf Baechle 12911da177e4SLinus Torvalds case ldc1_op: 12921da177e4SLinus Torvalds case sdc1_op: 12932d83fea7SMaciej W. Rozycki if (cpu_has_mips_2_3_4_5_r) 129408a07904SRalf Baechle goto emul; 129508a07904SRalf Baechle 12969ab4471cSMaciej W. Rozycki goto bc_sigill; 12973f7cac41SRalf Baechle 12981da177e4SLinus Torvalds case cop1_op: 129908a07904SRalf Baechle goto emul; 13003f7cac41SRalf Baechle 13011da177e4SLinus Torvalds case cop1x_op: 13022d83fea7SMaciej W. Rozycki if (cpu_has_mips_4_5_64_r2_r6) 13031da177e4SLinus Torvalds /* its one of ours */ 13041da177e4SLinus Torvalds goto emul; 130508a07904SRalf Baechle 13069ab4471cSMaciej W. Rozycki goto bc_sigill; 13073f7cac41SRalf Baechle 13081da177e4SLinus Torvalds case spec_op: 13092d83fea7SMaciej W. Rozycki switch (MIPSInst_FUNC(ir)) { 13102d83fea7SMaciej W. Rozycki case movc_op: 13112d83fea7SMaciej W. Rozycki if (cpu_has_mips_4_5_r) 13121da177e4SLinus Torvalds goto emul; 13132d83fea7SMaciej W. Rozycki 13149ab4471cSMaciej W. Rozycki goto bc_sigill; 13152d83fea7SMaciej W. Rozycki } 13161da177e4SLinus Torvalds break; 13179ab4471cSMaciej W. Rozycki 13189ab4471cSMaciej W. Rozycki bc_sigill: 13199ab4471cSMaciej W. Rozycki xcp->cp0_epc = bcpc; 13209ab4471cSMaciej W. Rozycki return SIGILL; 13211da177e4SLinus Torvalds } 13221da177e4SLinus Torvalds 13231da177e4SLinus Torvalds /* 13241da177e4SLinus Torvalds * Single step the non-cp1 13251da177e4SLinus Torvalds * instruction in the dslot 13261da177e4SLinus Torvalds */ 1327432c6bacSPaul Burton sig = mips_dsemul(xcp, ir, bcpc, contpc); 1328e4553573SMaciej W. Rozycki if (sig < 0) 1329e4553573SMaciej W. Rozycki break; 13309ab4471cSMaciej W. Rozycki if (sig) 13319ab4471cSMaciej W. Rozycki xcp->cp0_epc = bcpc; 13329ab4471cSMaciej W. Rozycki /* SIGILL forces out of the emulation loop. */ 13339ab4471cSMaciej W. Rozycki return sig ? sig : SIGILL; 13343f7cac41SRalf Baechle } else if (likely) { /* branch not taken */ 13351da177e4SLinus Torvalds /* 13361da177e4SLinus Torvalds * branch likely nullifies 13371da177e4SLinus Torvalds * dslot if not taken 13381da177e4SLinus Torvalds */ 1339102cedc3SLeonid Yegoshin xcp->cp0_epc += dec_insn.pc_inc; 1340102cedc3SLeonid Yegoshin contpc += dec_insn.pc_inc; 13411da177e4SLinus Torvalds /* 13421da177e4SLinus Torvalds * else continue & execute 13431da177e4SLinus Torvalds * dslot as normal insn 13441da177e4SLinus Torvalds */ 13451da177e4SLinus Torvalds } 13461da177e4SLinus Torvalds break; 13471da177e4SLinus Torvalds 13481da177e4SLinus Torvalds default: 13491da177e4SLinus Torvalds if (!(MIPSInst_RS(ir) & 0x10)) 13501da177e4SLinus Torvalds return SIGILL; 13511da177e4SLinus Torvalds 13521da177e4SLinus Torvalds /* a real fpu computation instruction */ 13538904d5b1SAleksandar Markovic sig = fpu_emu(xcp, ctx, ir); 13548904d5b1SAleksandar Markovic if (sig) 13551da177e4SLinus Torvalds return sig; 13561da177e4SLinus Torvalds } 13571da177e4SLinus Torvalds break; 13581da177e4SLinus Torvalds 13593f7cac41SRalf Baechle case cop1x_op: 13602d83fea7SMaciej W. Rozycki if (!cpu_has_mips_4_5_64_r2_r6) 136108a07904SRalf Baechle return SIGILL; 136208a07904SRalf Baechle 136308a07904SRalf Baechle sig = fpux_emu(xcp, ctx, ir, fault_addr); 1364515b029dSDavid Daney if (sig) 13651da177e4SLinus Torvalds return sig; 13661da177e4SLinus Torvalds break; 13671da177e4SLinus Torvalds 13681da177e4SLinus Torvalds case spec_op: 136908a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 137008a07904SRalf Baechle return SIGILL; 137108a07904SRalf Baechle 13721da177e4SLinus Torvalds if (MIPSInst_FUNC(ir) != movc_op) 13731da177e4SLinus Torvalds return SIGILL; 13741da177e4SLinus Torvalds cond = fpucondbit[MIPSInst_RT(ir) >> 2]; 13751da177e4SLinus Torvalds if (((ctx->fcr31 & cond) != 0) == ((MIPSInst_RT(ir) & 1) != 0)) 13761da177e4SLinus Torvalds xcp->regs[MIPSInst_RD(ir)] = 13771da177e4SLinus Torvalds xcp->regs[MIPSInst_RS(ir)]; 13781da177e4SLinus Torvalds break; 13791da177e4SLinus Torvalds default: 13801da177e4SLinus Torvalds return SIGILL; 13811da177e4SLinus Torvalds } 13821da177e4SLinus Torvalds 13831da177e4SLinus Torvalds /* we did it !! */ 1384e70dfc10SAtsushi Nemoto xcp->cp0_epc = contpc; 1385e7e9cae5SRalf Baechle clear_delay_slot(xcp); 1386333d1f67SRalf Baechle 13871da177e4SLinus Torvalds return 0; 13881da177e4SLinus Torvalds } 13891da177e4SLinus Torvalds 13901da177e4SLinus Torvalds /* 13911da177e4SLinus Torvalds * Conversion table from MIPS compare ops 48-63 13921da177e4SLinus Torvalds * cond = ieee754dp_cmp(x,y,IEEE754_UN,sig); 13931da177e4SLinus Torvalds */ 13941da177e4SLinus Torvalds static const unsigned char cmptab[8] = { 13951da177e4SLinus Torvalds 0, /* cmp_0 (sig) cmp_sf */ 13961da177e4SLinus Torvalds IEEE754_CUN, /* cmp_un (sig) cmp_ngle */ 13971da177e4SLinus Torvalds IEEE754_CEQ, /* cmp_eq (sig) cmp_seq */ 13981da177e4SLinus Torvalds IEEE754_CEQ | IEEE754_CUN, /* cmp_ueq (sig) cmp_ngl */ 13991da177e4SLinus Torvalds IEEE754_CLT, /* cmp_olt (sig) cmp_lt */ 14001da177e4SLinus Torvalds IEEE754_CLT | IEEE754_CUN, /* cmp_ult (sig) cmp_nge */ 14011da177e4SLinus Torvalds IEEE754_CLT | IEEE754_CEQ, /* cmp_ole (sig) cmp_le */ 14021da177e4SLinus Torvalds IEEE754_CLT | IEEE754_CEQ | IEEE754_CUN, /* cmp_ule (sig) cmp_ngt */ 14031da177e4SLinus Torvalds }; 14041da177e4SLinus Torvalds 1405f8c3c671SMarkos Chandras static const unsigned char negative_cmptab[8] = { 1406f8c3c671SMarkos Chandras 0, /* Reserved */ 1407f8c3c671SMarkos Chandras IEEE754_CLT | IEEE754_CGT | IEEE754_CEQ, 1408f8c3c671SMarkos Chandras IEEE754_CLT | IEEE754_CGT | IEEE754_CUN, 1409f8c3c671SMarkos Chandras IEEE754_CLT | IEEE754_CGT, 1410f8c3c671SMarkos Chandras /* Reserved */ 1411f8c3c671SMarkos Chandras }; 1412f8c3c671SMarkos Chandras 14131da177e4SLinus Torvalds 14141da177e4SLinus Torvalds /* 14151da177e4SLinus Torvalds * Additional MIPS4 instructions 14161da177e4SLinus Torvalds */ 14171da177e4SLinus Torvalds 14181da177e4SLinus Torvalds #define DEF3OP(name, p, f1, f2, f3) \ 141947fa0c02SRalf Baechle static union ieee754##p fpemu_##p##_##name(union ieee754##p r, \ 142047fa0c02SRalf Baechle union ieee754##p s, union ieee754##p t) \ 14211da177e4SLinus Torvalds { \ 1422cd21dfcfSRalf Baechle struct _ieee754_csr ieee754_csr_save; \ 14231da177e4SLinus Torvalds s = f1(s, t); \ 14241da177e4SLinus Torvalds ieee754_csr_save = ieee754_csr; \ 14251da177e4SLinus Torvalds s = f2(s, r); \ 14261da177e4SLinus Torvalds ieee754_csr_save.cx |= ieee754_csr.cx; \ 14271da177e4SLinus Torvalds ieee754_csr_save.sx |= ieee754_csr.sx; \ 14281da177e4SLinus Torvalds s = f3(s); \ 14291da177e4SLinus Torvalds ieee754_csr.cx |= ieee754_csr_save.cx; \ 14301da177e4SLinus Torvalds ieee754_csr.sx |= ieee754_csr_save.sx; \ 14311da177e4SLinus Torvalds return s; \ 14321da177e4SLinus Torvalds } 14331da177e4SLinus Torvalds 14342209bcb1SRalf Baechle static union ieee754dp fpemu_dp_recip(union ieee754dp d) 14351da177e4SLinus Torvalds { 14361da177e4SLinus Torvalds return ieee754dp_div(ieee754dp_one(0), d); 14371da177e4SLinus Torvalds } 14381da177e4SLinus Torvalds 14392209bcb1SRalf Baechle static union ieee754dp fpemu_dp_rsqrt(union ieee754dp d) 14401da177e4SLinus Torvalds { 14411da177e4SLinus Torvalds return ieee754dp_div(ieee754dp_one(0), ieee754dp_sqrt(d)); 14421da177e4SLinus Torvalds } 14431da177e4SLinus Torvalds 14442209bcb1SRalf Baechle static union ieee754sp fpemu_sp_recip(union ieee754sp s) 14451da177e4SLinus Torvalds { 14461da177e4SLinus Torvalds return ieee754sp_div(ieee754sp_one(0), s); 14471da177e4SLinus Torvalds } 14481da177e4SLinus Torvalds 14492209bcb1SRalf Baechle static union ieee754sp fpemu_sp_rsqrt(union ieee754sp s) 14501da177e4SLinus Torvalds { 14511da177e4SLinus Torvalds return ieee754sp_div(ieee754sp_one(0), ieee754sp_sqrt(s)); 14521da177e4SLinus Torvalds } 14531da177e4SLinus Torvalds 14541da177e4SLinus Torvalds DEF3OP(madd, sp, ieee754sp_mul, ieee754sp_add, ); 14551da177e4SLinus Torvalds DEF3OP(msub, sp, ieee754sp_mul, ieee754sp_sub, ); 14561da177e4SLinus Torvalds DEF3OP(nmadd, sp, ieee754sp_mul, ieee754sp_add, ieee754sp_neg); 14571da177e4SLinus Torvalds DEF3OP(nmsub, sp, ieee754sp_mul, ieee754sp_sub, ieee754sp_neg); 14581da177e4SLinus Torvalds DEF3OP(madd, dp, ieee754dp_mul, ieee754dp_add, ); 14591da177e4SLinus Torvalds DEF3OP(msub, dp, ieee754dp_mul, ieee754dp_sub, ); 14601da177e4SLinus Torvalds DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg); 14611da177e4SLinus Torvalds DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg); 14621da177e4SLinus Torvalds 1463eae89076SAtsushi Nemoto static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 1464445a58ceSPaul Burton mips_instruction ir, void __user **fault_addr) 14651da177e4SLinus Torvalds { 1466a58f85b5SAleksandar Markovic unsigned int rcsr = 0; /* resulting csr */ 14671da177e4SLinus Torvalds 1468b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(cp1xops); 14691da177e4SLinus Torvalds 14701da177e4SLinus Torvalds switch (MIPSInst_FMA_FFMT(ir)) { 14711da177e4SLinus Torvalds case s_fmt:{ /* 0 */ 14721da177e4SLinus Torvalds 14732209bcb1SRalf Baechle union ieee754sp(*handler) (union ieee754sp, union ieee754sp, union ieee754sp); 14742209bcb1SRalf Baechle union ieee754sp fd, fr, fs, ft; 14753fccc015SRalf Baechle u32 __user *va; 14761da177e4SLinus Torvalds u32 val; 14771da177e4SLinus Torvalds 14781da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 14791da177e4SLinus Torvalds case lwxc1_op: 14803fccc015SRalf Baechle va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 14811da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 14821da177e4SLinus Torvalds 1483b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(loads); 148496d4f267SLinus Torvalds if (!access_ok(va, sizeof(u32))) { 1485b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1486515b029dSDavid Daney *fault_addr = va; 14871da177e4SLinus Torvalds return SIGBUS; 14881da177e4SLinus Torvalds } 1489515b029dSDavid Daney if (__get_user(val, va)) { 1490515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1491515b029dSDavid Daney *fault_addr = va; 1492515b029dSDavid Daney return SIGSEGV; 1493515b029dSDavid Daney } 14941da177e4SLinus Torvalds SITOREG(val, MIPSInst_FD(ir)); 14951da177e4SLinus Torvalds break; 14961da177e4SLinus Torvalds 14971da177e4SLinus Torvalds case swxc1_op: 14983fccc015SRalf Baechle va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 14991da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 15001da177e4SLinus Torvalds 1501b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(stores); 15021da177e4SLinus Torvalds 15031da177e4SLinus Torvalds SIFROMREG(val, MIPSInst_FS(ir)); 150496d4f267SLinus Torvalds if (!access_ok(va, sizeof(u32))) { 1505515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1506515b029dSDavid Daney *fault_addr = va; 1507515b029dSDavid Daney return SIGBUS; 1508515b029dSDavid Daney } 15091da177e4SLinus Torvalds if (put_user(val, va)) { 1510b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1511515b029dSDavid Daney *fault_addr = va; 1512515b029dSDavid Daney return SIGSEGV; 15131da177e4SLinus Torvalds } 15141da177e4SLinus Torvalds break; 15151da177e4SLinus Torvalds 15161da177e4SLinus Torvalds case madd_s_op: 1517ece276deSJiaxun Yang if (cpu_has_mac2008_only) 1518ece276deSJiaxun Yang handler = ieee754sp_madd; 1519ece276deSJiaxun Yang else 15201da177e4SLinus Torvalds handler = fpemu_sp_madd; 15211da177e4SLinus Torvalds goto scoptop; 15221da177e4SLinus Torvalds case msub_s_op: 1523ece276deSJiaxun Yang if (cpu_has_mac2008_only) 1524ece276deSJiaxun Yang handler = ieee754sp_msub; 1525ece276deSJiaxun Yang else 15261da177e4SLinus Torvalds handler = fpemu_sp_msub; 15271da177e4SLinus Torvalds goto scoptop; 15281da177e4SLinus Torvalds case nmadd_s_op: 1529ece276deSJiaxun Yang if (cpu_has_mac2008_only) 1530ece276deSJiaxun Yang handler = ieee754sp_nmadd; 1531ece276deSJiaxun Yang else 15321da177e4SLinus Torvalds handler = fpemu_sp_nmadd; 15331da177e4SLinus Torvalds goto scoptop; 15341da177e4SLinus Torvalds case nmsub_s_op: 1535ece276deSJiaxun Yang if (cpu_has_mac2008_only) 1536ece276deSJiaxun Yang handler = ieee754sp_nmsub; 1537ece276deSJiaxun Yang else 15381da177e4SLinus Torvalds handler = fpemu_sp_nmsub; 15391da177e4SLinus Torvalds goto scoptop; 15401da177e4SLinus Torvalds 15411da177e4SLinus Torvalds scoptop: 15421da177e4SLinus Torvalds SPFROMREG(fr, MIPSInst_FR(ir)); 15431da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 15441da177e4SLinus Torvalds SPFROMREG(ft, MIPSInst_FT(ir)); 15451da177e4SLinus Torvalds fd = (*handler) (fr, fs, ft); 15461da177e4SLinus Torvalds SPTOREG(fd, MIPSInst_FD(ir)); 15471da177e4SLinus Torvalds 15481da177e4SLinus Torvalds copcsr: 1549c4103526SDeng-Cheng Zhu if (ieee754_cxtest(IEEE754_INEXACT)) { 1550c4103526SDeng-Cheng Zhu MIPS_FPU_EMU_INC_STATS(ieee754_inexact); 15511da177e4SLinus Torvalds rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S; 1552c4103526SDeng-Cheng Zhu } 1553c4103526SDeng-Cheng Zhu if (ieee754_cxtest(IEEE754_UNDERFLOW)) { 1554c4103526SDeng-Cheng Zhu MIPS_FPU_EMU_INC_STATS(ieee754_underflow); 15551da177e4SLinus Torvalds rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S; 1556c4103526SDeng-Cheng Zhu } 1557c4103526SDeng-Cheng Zhu if (ieee754_cxtest(IEEE754_OVERFLOW)) { 1558c4103526SDeng-Cheng Zhu MIPS_FPU_EMU_INC_STATS(ieee754_overflow); 15591da177e4SLinus Torvalds rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S; 1560c4103526SDeng-Cheng Zhu } 1561c4103526SDeng-Cheng Zhu if (ieee754_cxtest(IEEE754_INVALID_OPERATION)) { 1562c4103526SDeng-Cheng Zhu MIPS_FPU_EMU_INC_STATS(ieee754_invalidop); 15631da177e4SLinus Torvalds rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S; 1564c4103526SDeng-Cheng Zhu } 15651da177e4SLinus Torvalds 15661da177e4SLinus Torvalds ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr; 15671da177e4SLinus Torvalds if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 15683f7cac41SRalf Baechle /*printk ("SIGFPE: FPU csr = %08x\n", 15691da177e4SLinus Torvalds ctx->fcr31); */ 15701da177e4SLinus Torvalds return SIGFPE; 15711da177e4SLinus Torvalds } 15721da177e4SLinus Torvalds 15731da177e4SLinus Torvalds break; 15741da177e4SLinus Torvalds 15751da177e4SLinus Torvalds default: 15761da177e4SLinus Torvalds return SIGILL; 15771da177e4SLinus Torvalds } 15781da177e4SLinus Torvalds break; 15791da177e4SLinus Torvalds } 15801da177e4SLinus Torvalds 15811da177e4SLinus Torvalds case d_fmt:{ /* 1 */ 15822209bcb1SRalf Baechle union ieee754dp(*handler) (union ieee754dp, union ieee754dp, union ieee754dp); 15832209bcb1SRalf Baechle union ieee754dp fd, fr, fs, ft; 15843fccc015SRalf Baechle u64 __user *va; 15851da177e4SLinus Torvalds u64 val; 15861da177e4SLinus Torvalds 15871da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 15881da177e4SLinus Torvalds case ldxc1_op: 15893fccc015SRalf Baechle va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 15901da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 15911da177e4SLinus Torvalds 1592b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(loads); 159396d4f267SLinus Torvalds if (!access_ok(va, sizeof(u64))) { 1594b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1595515b029dSDavid Daney *fault_addr = va; 15961da177e4SLinus Torvalds return SIGBUS; 15971da177e4SLinus Torvalds } 1598515b029dSDavid Daney if (__get_user(val, va)) { 1599515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1600515b029dSDavid Daney *fault_addr = va; 1601515b029dSDavid Daney return SIGSEGV; 1602515b029dSDavid Daney } 16031da177e4SLinus Torvalds DITOREG(val, MIPSInst_FD(ir)); 16041da177e4SLinus Torvalds break; 16051da177e4SLinus Torvalds 16061da177e4SLinus Torvalds case sdxc1_op: 16073fccc015SRalf Baechle va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 16081da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 16091da177e4SLinus Torvalds 1610b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(stores); 16111da177e4SLinus Torvalds DIFROMREG(val, MIPSInst_FS(ir)); 161296d4f267SLinus Torvalds if (!access_ok(va, sizeof(u64))) { 1613b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1614515b029dSDavid Daney *fault_addr = va; 16151da177e4SLinus Torvalds return SIGBUS; 16161da177e4SLinus Torvalds } 1617515b029dSDavid Daney if (__put_user(val, va)) { 1618515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1619515b029dSDavid Daney *fault_addr = va; 1620515b029dSDavid Daney return SIGSEGV; 1621515b029dSDavid Daney } 16221da177e4SLinus Torvalds break; 16231da177e4SLinus Torvalds 16241da177e4SLinus Torvalds case madd_d_op: 1625ece276deSJiaxun Yang if (cpu_has_mac2008_only) 1626ece276deSJiaxun Yang handler = ieee754dp_madd; 1627ece276deSJiaxun Yang else 16281da177e4SLinus Torvalds handler = fpemu_dp_madd; 16291da177e4SLinus Torvalds goto dcoptop; 16301da177e4SLinus Torvalds case msub_d_op: 1631ece276deSJiaxun Yang if (cpu_has_mac2008_only) 1632ece276deSJiaxun Yang handler = ieee754dp_msub; 1633ece276deSJiaxun Yang else 16341da177e4SLinus Torvalds handler = fpemu_dp_msub; 16351da177e4SLinus Torvalds goto dcoptop; 16361da177e4SLinus Torvalds case nmadd_d_op: 1637ece276deSJiaxun Yang if (cpu_has_mac2008_only) 1638ece276deSJiaxun Yang handler = ieee754dp_nmadd; 1639ece276deSJiaxun Yang else 16401da177e4SLinus Torvalds handler = fpemu_dp_nmadd; 16411da177e4SLinus Torvalds goto dcoptop; 16421da177e4SLinus Torvalds case nmsub_d_op: 1643ece276deSJiaxun Yang if (cpu_has_mac2008_only) 1644ece276deSJiaxun Yang handler = ieee754dp_nmsub; 1645ece276deSJiaxun Yang else 16461da177e4SLinus Torvalds handler = fpemu_dp_nmsub; 16471da177e4SLinus Torvalds goto dcoptop; 16481da177e4SLinus Torvalds 16491da177e4SLinus Torvalds dcoptop: 16501da177e4SLinus Torvalds DPFROMREG(fr, MIPSInst_FR(ir)); 16511da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 16521da177e4SLinus Torvalds DPFROMREG(ft, MIPSInst_FT(ir)); 16531da177e4SLinus Torvalds fd = (*handler) (fr, fs, ft); 16541da177e4SLinus Torvalds DPTOREG(fd, MIPSInst_FD(ir)); 16551da177e4SLinus Torvalds goto copcsr; 16561da177e4SLinus Torvalds 16571da177e4SLinus Torvalds default: 16581da177e4SLinus Torvalds return SIGILL; 16591da177e4SLinus Torvalds } 16601da177e4SLinus Torvalds break; 16611da177e4SLinus Torvalds } 16621da177e4SLinus Torvalds 166351061b88SDeng-Cheng Zhu case 0x3: 166451061b88SDeng-Cheng Zhu if (MIPSInst_FUNC(ir) != pfetch_op) 16651da177e4SLinus Torvalds return SIGILL; 166651061b88SDeng-Cheng Zhu 16671da177e4SLinus Torvalds /* ignore prefx operation */ 16681da177e4SLinus Torvalds break; 16691da177e4SLinus Torvalds 16701da177e4SLinus Torvalds default: 16711da177e4SLinus Torvalds return SIGILL; 16721da177e4SLinus Torvalds } 16731da177e4SLinus Torvalds 16741da177e4SLinus Torvalds return 0; 16751da177e4SLinus Torvalds } 16761da177e4SLinus Torvalds 16771da177e4SLinus Torvalds 16781da177e4SLinus Torvalds 16791da177e4SLinus Torvalds /* 16801da177e4SLinus Torvalds * Emulate a single COP1 arithmetic instruction. 16811da177e4SLinus Torvalds */ 1682eae89076SAtsushi Nemoto static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 16831da177e4SLinus Torvalds mips_instruction ir) 16841da177e4SLinus Torvalds { 16851da177e4SLinus Torvalds int rfmt; /* resulting format */ 1686a58f85b5SAleksandar Markovic unsigned int rcsr = 0; /* resulting csr */ 16873f7cac41SRalf Baechle unsigned int oldrm; 16883f7cac41SRalf Baechle unsigned int cbit; 1689a58f85b5SAleksandar Markovic unsigned int cond; 16901da177e4SLinus Torvalds union { 16912209bcb1SRalf Baechle union ieee754dp d; 16922209bcb1SRalf Baechle union ieee754sp s; 16931da177e4SLinus Torvalds int w; 16941da177e4SLinus Torvalds s64 l; 16951da177e4SLinus Torvalds } rv; /* resulting value */ 16963f7cac41SRalf Baechle u64 bits; 16971da177e4SLinus Torvalds 1698b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(cp1ops); 16991da177e4SLinus Torvalds switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) { 17001da177e4SLinus Torvalds case s_fmt: { /* 0 */ 17011da177e4SLinus Torvalds union { 17022209bcb1SRalf Baechle union ieee754sp(*b) (union ieee754sp, union ieee754sp); 17032209bcb1SRalf Baechle union ieee754sp(*u) (union ieee754sp); 17041da177e4SLinus Torvalds } handler; 17054b820d95SPaul Burton union ieee754sp fd, fs, ft; 17061da177e4SLinus Torvalds 17071da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 17081da177e4SLinus Torvalds /* binary ops */ 17091da177e4SLinus Torvalds case fadd_op: 1710454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(add_s); 17111da177e4SLinus Torvalds handler.b = ieee754sp_add; 17121da177e4SLinus Torvalds goto scopbop; 17131da177e4SLinus Torvalds case fsub_op: 1714454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(sub_s); 17151da177e4SLinus Torvalds handler.b = ieee754sp_sub; 17161da177e4SLinus Torvalds goto scopbop; 17171da177e4SLinus Torvalds case fmul_op: 1718454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(mul_s); 17191da177e4SLinus Torvalds handler.b = ieee754sp_mul; 17201da177e4SLinus Torvalds goto scopbop; 17211da177e4SLinus Torvalds case fdiv_op: 1722454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(div_s); 17231da177e4SLinus Torvalds handler.b = ieee754sp_div; 17241da177e4SLinus Torvalds goto scopbop; 17251da177e4SLinus Torvalds 17261da177e4SLinus Torvalds /* unary ops */ 17271da177e4SLinus Torvalds case fsqrt_op: 17282d83fea7SMaciej W. Rozycki if (!cpu_has_mips_2_3_4_5_r) 172908a07904SRalf Baechle return SIGILL; 173008a07904SRalf Baechle 1731454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(sqrt_s); 17321da177e4SLinus Torvalds handler.u = ieee754sp_sqrt; 17331da177e4SLinus Torvalds goto scopuop; 17343f7cac41SRalf Baechle 173508a07904SRalf Baechle /* 173608a07904SRalf Baechle * Note that on some MIPS IV implementations such as the 173708a07904SRalf Baechle * R5000 and R8000 the FSQRT and FRECIP instructions do not 173808a07904SRalf Baechle * achieve full IEEE-754 accuracy - however this emulator does. 173908a07904SRalf Baechle */ 17401da177e4SLinus Torvalds case frsqrt_op: 17412d83fea7SMaciej W. Rozycki if (!cpu_has_mips_4_5_64_r2_r6) 174208a07904SRalf Baechle return SIGILL; 174308a07904SRalf Baechle 1744454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(rsqrt_s); 17451da177e4SLinus Torvalds handler.u = fpemu_sp_rsqrt; 17461da177e4SLinus Torvalds goto scopuop; 17473f7cac41SRalf Baechle 17481da177e4SLinus Torvalds case frecip_op: 17492d83fea7SMaciej W. Rozycki if (!cpu_has_mips_4_5_64_r2_r6) 175008a07904SRalf Baechle return SIGILL; 175108a07904SRalf Baechle 1752454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(recip_s); 17531da177e4SLinus Torvalds handler.u = fpemu_sp_recip; 17541da177e4SLinus Torvalds goto scopuop; 175508a07904SRalf Baechle 17561da177e4SLinus Torvalds case fmovc_op: 175708a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 175808a07904SRalf Baechle return SIGILL; 175908a07904SRalf Baechle 17601da177e4SLinus Torvalds cond = fpucondbit[MIPSInst_FT(ir) >> 2]; 17611da177e4SLinus Torvalds if (((ctx->fcr31 & cond) != 0) != 17621da177e4SLinus Torvalds ((MIPSInst_FT(ir) & 1) != 0)) 17631da177e4SLinus Torvalds return 0; 17641da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 17651da177e4SLinus Torvalds break; 17663f7cac41SRalf Baechle 17671da177e4SLinus Torvalds case fmovz_op: 176808a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 176908a07904SRalf Baechle return SIGILL; 177008a07904SRalf Baechle 17711da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] != 0) 17721da177e4SLinus Torvalds return 0; 17731da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 17741da177e4SLinus Torvalds break; 17753f7cac41SRalf Baechle 17761da177e4SLinus Torvalds case fmovn_op: 177708a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 177808a07904SRalf Baechle return SIGILL; 177908a07904SRalf Baechle 17801da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] == 0) 17811da177e4SLinus Torvalds return 0; 17821da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 17831da177e4SLinus Torvalds break; 17843f7cac41SRalf Baechle 178567613f02SMarkos Chandras case fseleqz_op: 178667613f02SMarkos Chandras if (!cpu_has_mips_r6) 178767613f02SMarkos Chandras return SIGILL; 178867613f02SMarkos Chandras 1789454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(seleqz_s); 179067613f02SMarkos Chandras SPFROMREG(rv.s, MIPSInst_FT(ir)); 179167613f02SMarkos Chandras if (rv.w & 0x1) 179267613f02SMarkos Chandras rv.w = 0; 179367613f02SMarkos Chandras else 179467613f02SMarkos Chandras SPFROMREG(rv.s, MIPSInst_FS(ir)); 179567613f02SMarkos Chandras break; 179667613f02SMarkos Chandras 1797130fe357SMarkos Chandras case fselnez_op: 1798130fe357SMarkos Chandras if (!cpu_has_mips_r6) 1799130fe357SMarkos Chandras return SIGILL; 1800130fe357SMarkos Chandras 1801454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(selnez_s); 1802130fe357SMarkos Chandras SPFROMREG(rv.s, MIPSInst_FT(ir)); 1803130fe357SMarkos Chandras if (rv.w & 0x1) 1804130fe357SMarkos Chandras SPFROMREG(rv.s, MIPSInst_FS(ir)); 1805130fe357SMarkos Chandras else 1806130fe357SMarkos Chandras rv.w = 0; 1807130fe357SMarkos Chandras break; 1808130fe357SMarkos Chandras 1809e24c3becSMarkos Chandras case fmaddf_op: { 1810e24c3becSMarkos Chandras union ieee754sp ft, fs, fd; 1811e24c3becSMarkos Chandras 1812e24c3becSMarkos Chandras if (!cpu_has_mips_r6) 1813e24c3becSMarkos Chandras return SIGILL; 1814e24c3becSMarkos Chandras 1815454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(maddf_s); 1816e24c3becSMarkos Chandras SPFROMREG(ft, MIPSInst_FT(ir)); 1817e24c3becSMarkos Chandras SPFROMREG(fs, MIPSInst_FS(ir)); 1818e24c3becSMarkos Chandras SPFROMREG(fd, MIPSInst_FD(ir)); 1819e24c3becSMarkos Chandras rv.s = ieee754sp_maddf(fd, fs, ft); 1820409fcaceSAleksandar Markovic goto copcsr; 1821e24c3becSMarkos Chandras } 1822e24c3becSMarkos Chandras 182383d43305SMarkos Chandras case fmsubf_op: { 182483d43305SMarkos Chandras union ieee754sp ft, fs, fd; 182583d43305SMarkos Chandras 182683d43305SMarkos Chandras if (!cpu_has_mips_r6) 182783d43305SMarkos Chandras return SIGILL; 182883d43305SMarkos Chandras 1829454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(msubf_s); 183083d43305SMarkos Chandras SPFROMREG(ft, MIPSInst_FT(ir)); 183183d43305SMarkos Chandras SPFROMREG(fs, MIPSInst_FS(ir)); 183283d43305SMarkos Chandras SPFROMREG(fd, MIPSInst_FD(ir)); 183383d43305SMarkos Chandras rv.s = ieee754sp_msubf(fd, fs, ft); 1834409fcaceSAleksandar Markovic goto copcsr; 183583d43305SMarkos Chandras } 183683d43305SMarkos Chandras 1837400bd2e4SMarkos Chandras case frint_op: { 1838400bd2e4SMarkos Chandras union ieee754sp fs; 1839400bd2e4SMarkos Chandras 1840400bd2e4SMarkos Chandras if (!cpu_has_mips_r6) 1841400bd2e4SMarkos Chandras return SIGILL; 1842400bd2e4SMarkos Chandras 1843454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(rint_s); 1844400bd2e4SMarkos Chandras SPFROMREG(fs, MIPSInst_FS(ir)); 18453ec404d8SAleksandar Markovic rv.s = ieee754sp_rint(fs); 1846400bd2e4SMarkos Chandras goto copcsr; 1847400bd2e4SMarkos Chandras } 1848400bd2e4SMarkos Chandras 184938db37baSMarkos Chandras case fclass_op: { 185038db37baSMarkos Chandras union ieee754sp fs; 185138db37baSMarkos Chandras 185238db37baSMarkos Chandras if (!cpu_has_mips_r6) 185338db37baSMarkos Chandras return SIGILL; 185438db37baSMarkos Chandras 1855454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(class_s); 185638db37baSMarkos Chandras SPFROMREG(fs, MIPSInst_FS(ir)); 185738db37baSMarkos Chandras rv.w = ieee754sp_2008class(fs); 185838db37baSMarkos Chandras rfmt = w_fmt; 1859409fcaceSAleksandar Markovic goto copcsr; 186038db37baSMarkos Chandras } 186138db37baSMarkos Chandras 18624e9561b2SMarkos Chandras case fmin_op: { 18634e9561b2SMarkos Chandras union ieee754sp fs, ft; 18644e9561b2SMarkos Chandras 18654e9561b2SMarkos Chandras if (!cpu_has_mips_r6) 18664e9561b2SMarkos Chandras return SIGILL; 18674e9561b2SMarkos Chandras 1868454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(min_s); 18694e9561b2SMarkos Chandras SPFROMREG(ft, MIPSInst_FT(ir)); 18704e9561b2SMarkos Chandras SPFROMREG(fs, MIPSInst_FS(ir)); 18714e9561b2SMarkos Chandras rv.s = ieee754sp_fmin(fs, ft); 1872409fcaceSAleksandar Markovic goto copcsr; 18734e9561b2SMarkos Chandras } 18744e9561b2SMarkos Chandras 18754e9561b2SMarkos Chandras case fmina_op: { 18764e9561b2SMarkos Chandras union ieee754sp fs, ft; 18774e9561b2SMarkos Chandras 18784e9561b2SMarkos Chandras if (!cpu_has_mips_r6) 18794e9561b2SMarkos Chandras return SIGILL; 18804e9561b2SMarkos Chandras 1881454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(mina_s); 18824e9561b2SMarkos Chandras SPFROMREG(ft, MIPSInst_FT(ir)); 18834e9561b2SMarkos Chandras SPFROMREG(fs, MIPSInst_FS(ir)); 18844e9561b2SMarkos Chandras rv.s = ieee754sp_fmina(fs, ft); 1885409fcaceSAleksandar Markovic goto copcsr; 18864e9561b2SMarkos Chandras } 18874e9561b2SMarkos Chandras 1888a79f5f9bSMarkos Chandras case fmax_op: { 1889a79f5f9bSMarkos Chandras union ieee754sp fs, ft; 1890a79f5f9bSMarkos Chandras 1891a79f5f9bSMarkos Chandras if (!cpu_has_mips_r6) 1892a79f5f9bSMarkos Chandras return SIGILL; 1893a79f5f9bSMarkos Chandras 1894454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(max_s); 1895a79f5f9bSMarkos Chandras SPFROMREG(ft, MIPSInst_FT(ir)); 1896a79f5f9bSMarkos Chandras SPFROMREG(fs, MIPSInst_FS(ir)); 1897a79f5f9bSMarkos Chandras rv.s = ieee754sp_fmax(fs, ft); 1898409fcaceSAleksandar Markovic goto copcsr; 1899a79f5f9bSMarkos Chandras } 1900a79f5f9bSMarkos Chandras 1901a79f5f9bSMarkos Chandras case fmaxa_op: { 1902a79f5f9bSMarkos Chandras union ieee754sp fs, ft; 1903a79f5f9bSMarkos Chandras 1904a79f5f9bSMarkos Chandras if (!cpu_has_mips_r6) 1905a79f5f9bSMarkos Chandras return SIGILL; 1906a79f5f9bSMarkos Chandras 1907454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(maxa_s); 1908a79f5f9bSMarkos Chandras SPFROMREG(ft, MIPSInst_FT(ir)); 1909a79f5f9bSMarkos Chandras SPFROMREG(fs, MIPSInst_FS(ir)); 1910a79f5f9bSMarkos Chandras rv.s = ieee754sp_fmaxa(fs, ft); 1911409fcaceSAleksandar Markovic goto copcsr; 1912a79f5f9bSMarkos Chandras } 1913a79f5f9bSMarkos Chandras 19141da177e4SLinus Torvalds case fabs_op: 1915454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(abs_s); 19161da177e4SLinus Torvalds handler.u = ieee754sp_abs; 19171da177e4SLinus Torvalds goto scopuop; 19183f7cac41SRalf Baechle 19191da177e4SLinus Torvalds case fneg_op: 1920454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(neg_s); 19211da177e4SLinus Torvalds handler.u = ieee754sp_neg; 19221da177e4SLinus Torvalds goto scopuop; 19233f7cac41SRalf Baechle 19241da177e4SLinus Torvalds case fmov_op: 19251da177e4SLinus Torvalds /* an easy one */ 1926454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(mov_s); 19271da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 19281da177e4SLinus Torvalds goto copcsr; 19291da177e4SLinus Torvalds 19301da177e4SLinus Torvalds /* binary op on handler */ 19311da177e4SLinus Torvalds scopbop: 19321da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 19331da177e4SLinus Torvalds SPFROMREG(ft, MIPSInst_FT(ir)); 19341da177e4SLinus Torvalds 19351da177e4SLinus Torvalds rv.s = (*handler.b) (fs, ft); 19361da177e4SLinus Torvalds goto copcsr; 19371da177e4SLinus Torvalds scopuop: 19381da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 19391da177e4SLinus Torvalds rv.s = (*handler.u) (fs); 19401da177e4SLinus Torvalds goto copcsr; 19411da177e4SLinus Torvalds copcsr: 1942c4103526SDeng-Cheng Zhu if (ieee754_cxtest(IEEE754_INEXACT)) { 1943c4103526SDeng-Cheng Zhu MIPS_FPU_EMU_INC_STATS(ieee754_inexact); 19441da177e4SLinus Torvalds rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S; 1945c4103526SDeng-Cheng Zhu } 1946c4103526SDeng-Cheng Zhu if (ieee754_cxtest(IEEE754_UNDERFLOW)) { 1947c4103526SDeng-Cheng Zhu MIPS_FPU_EMU_INC_STATS(ieee754_underflow); 19481da177e4SLinus Torvalds rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S; 1949c4103526SDeng-Cheng Zhu } 1950c4103526SDeng-Cheng Zhu if (ieee754_cxtest(IEEE754_OVERFLOW)) { 1951c4103526SDeng-Cheng Zhu MIPS_FPU_EMU_INC_STATS(ieee754_overflow); 19521da177e4SLinus Torvalds rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S; 1953c4103526SDeng-Cheng Zhu } 1954c4103526SDeng-Cheng Zhu if (ieee754_cxtest(IEEE754_ZERO_DIVIDE)) { 1955c4103526SDeng-Cheng Zhu MIPS_FPU_EMU_INC_STATS(ieee754_zerodiv); 19561da177e4SLinus Torvalds rcsr |= FPU_CSR_DIV_X | FPU_CSR_DIV_S; 1957c4103526SDeng-Cheng Zhu } 1958c4103526SDeng-Cheng Zhu if (ieee754_cxtest(IEEE754_INVALID_OPERATION)) { 1959c4103526SDeng-Cheng Zhu MIPS_FPU_EMU_INC_STATS(ieee754_invalidop); 19601da177e4SLinus Torvalds rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S; 1961c4103526SDeng-Cheng Zhu } 19621da177e4SLinus Torvalds break; 19631da177e4SLinus Torvalds 19641da177e4SLinus Torvalds /* unary conv ops */ 19651da177e4SLinus Torvalds case fcvts_op: 19661da177e4SLinus Torvalds return SIGILL; /* not defined */ 19671da177e4SLinus Torvalds 19683f7cac41SRalf Baechle case fcvtd_op: 1969454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_d_s); 19701da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 19711da177e4SLinus Torvalds rv.d = ieee754dp_fsp(fs); 19721da177e4SLinus Torvalds rfmt = d_fmt; 19731da177e4SLinus Torvalds goto copcsr; 19741da177e4SLinus Torvalds 19753f7cac41SRalf Baechle case fcvtw_op: 1976454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_w_s); 19771da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 19781da177e4SLinus Torvalds rv.w = ieee754sp_tint(fs); 19791da177e4SLinus Torvalds rfmt = w_fmt; 19801da177e4SLinus Torvalds goto copcsr; 19811da177e4SLinus Torvalds 19821da177e4SLinus Torvalds case fround_op: 19831da177e4SLinus Torvalds case ftrunc_op: 19841da177e4SLinus Torvalds case fceil_op: 19853f7cac41SRalf Baechle case ffloor_op: 19862d83fea7SMaciej W. Rozycki if (!cpu_has_mips_2_3_4_5_r) 198708a07904SRalf Baechle return SIGILL; 198808a07904SRalf Baechle 1989454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == fceil_op) 1990454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(ceil_w_s); 1991454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == ffloor_op) 1992454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(floor_w_s); 1993454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == fround_op) 1994454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(round_w_s); 1995454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == ftrunc_op) 1996454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(trunc_w_s); 1997454854acSAleksandar Markovic 19983f7cac41SRalf Baechle oldrm = ieee754_csr.rm; 19991da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 20002cfcf8a8SMaciej W. Rozycki ieee754_csr.rm = MIPSInst_FUNC(ir); 20011da177e4SLinus Torvalds rv.w = ieee754sp_tint(fs); 20021da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 20031da177e4SLinus Torvalds rfmt = w_fmt; 20041da177e4SLinus Torvalds goto copcsr; 20051da177e4SLinus Torvalds 20064b820d95SPaul Burton case fsel_op: 20074b820d95SPaul Burton if (!cpu_has_mips_r6) 20084b820d95SPaul Burton return SIGILL; 20094b820d95SPaul Burton 2010454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(sel_s); 20114b820d95SPaul Burton SPFROMREG(fd, MIPSInst_FD(ir)); 20124b820d95SPaul Burton if (fd.bits & 0x1) 20134b820d95SPaul Burton SPFROMREG(rv.s, MIPSInst_FT(ir)); 20144b820d95SPaul Burton else 20154b820d95SPaul Burton SPFROMREG(rv.s, MIPSInst_FS(ir)); 20164b820d95SPaul Burton break; 20174b820d95SPaul Burton 20183f7cac41SRalf Baechle case fcvtl_op: 20192d83fea7SMaciej W. Rozycki if (!cpu_has_mips_3_4_5_64_r2_r6) 202008a07904SRalf Baechle return SIGILL; 202108a07904SRalf Baechle 2022454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_l_s); 20231da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 20241da177e4SLinus Torvalds rv.l = ieee754sp_tlong(fs); 20251da177e4SLinus Torvalds rfmt = l_fmt; 20261da177e4SLinus Torvalds goto copcsr; 20271da177e4SLinus Torvalds 20281da177e4SLinus Torvalds case froundl_op: 20291da177e4SLinus Torvalds case ftruncl_op: 20301da177e4SLinus Torvalds case fceill_op: 20313f7cac41SRalf Baechle case ffloorl_op: 20322d83fea7SMaciej W. Rozycki if (!cpu_has_mips_3_4_5_64_r2_r6) 203308a07904SRalf Baechle return SIGILL; 203408a07904SRalf Baechle 2035454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == fceill_op) 2036454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(ceil_l_s); 2037454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == ffloorl_op) 2038454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(floor_l_s); 2039454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == froundl_op) 2040454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(round_l_s); 2041454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == ftruncl_op) 2042454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(trunc_l_s); 2043454854acSAleksandar Markovic 20443f7cac41SRalf Baechle oldrm = ieee754_csr.rm; 20451da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 20462cfcf8a8SMaciej W. Rozycki ieee754_csr.rm = MIPSInst_FUNC(ir); 20471da177e4SLinus Torvalds rv.l = ieee754sp_tlong(fs); 20481da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 20491da177e4SLinus Torvalds rfmt = l_fmt; 20501da177e4SLinus Torvalds goto copcsr; 20511da177e4SLinus Torvalds 20521da177e4SLinus Torvalds default: 2053f8c3c671SMarkos Chandras if (!NO_R6EMU && MIPSInst_FUNC(ir) >= fcmp_op) { 2054a58f85b5SAleksandar Markovic unsigned int cmpop; 20552209bcb1SRalf Baechle union ieee754sp fs, ft; 20561da177e4SLinus Torvalds 2057a58f85b5SAleksandar Markovic cmpop = MIPSInst_FUNC(ir) - fcmp_op; 20581da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 20591da177e4SLinus Torvalds SPFROMREG(ft, MIPSInst_FT(ir)); 20601da177e4SLinus Torvalds rv.w = ieee754sp_cmp(fs, ft, 20611da177e4SLinus Torvalds cmptab[cmpop & 0x7], cmpop & 0x8); 20621da177e4SLinus Torvalds rfmt = -1; 20631da177e4SLinus Torvalds if ((cmpop & 0x8) && ieee754_cxtest 20641da177e4SLinus Torvalds (IEEE754_INVALID_OPERATION)) 20651da177e4SLinus Torvalds rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 20661da177e4SLinus Torvalds else 20671da177e4SLinus Torvalds goto copcsr; 20681da177e4SLinus Torvalds 20693f7cac41SRalf Baechle } else 20701da177e4SLinus Torvalds return SIGILL; 20711da177e4SLinus Torvalds break; 20721da177e4SLinus Torvalds } 20731da177e4SLinus Torvalds break; 20741da177e4SLinus Torvalds } 20751da177e4SLinus Torvalds 20761da177e4SLinus Torvalds case d_fmt: { 20774b820d95SPaul Burton union ieee754dp fd, fs, ft; 20781da177e4SLinus Torvalds union { 20792209bcb1SRalf Baechle union ieee754dp(*b) (union ieee754dp, union ieee754dp); 20802209bcb1SRalf Baechle union ieee754dp(*u) (union ieee754dp); 20811da177e4SLinus Torvalds } handler; 20821da177e4SLinus Torvalds 20831da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 20841da177e4SLinus Torvalds /* binary ops */ 20851da177e4SLinus Torvalds case fadd_op: 2086454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(add_d); 20871da177e4SLinus Torvalds handler.b = ieee754dp_add; 20881da177e4SLinus Torvalds goto dcopbop; 20891da177e4SLinus Torvalds case fsub_op: 2090454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(sub_d); 20911da177e4SLinus Torvalds handler.b = ieee754dp_sub; 20921da177e4SLinus Torvalds goto dcopbop; 20931da177e4SLinus Torvalds case fmul_op: 2094454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(mul_d); 20951da177e4SLinus Torvalds handler.b = ieee754dp_mul; 20961da177e4SLinus Torvalds goto dcopbop; 20971da177e4SLinus Torvalds case fdiv_op: 2098454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(div_d); 20991da177e4SLinus Torvalds handler.b = ieee754dp_div; 21001da177e4SLinus Torvalds goto dcopbop; 21011da177e4SLinus Torvalds 21021da177e4SLinus Torvalds /* unary ops */ 21031da177e4SLinus Torvalds case fsqrt_op: 210408a07904SRalf Baechle if (!cpu_has_mips_2_3_4_5_r) 210508a07904SRalf Baechle return SIGILL; 210608a07904SRalf Baechle 2107454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(sqrt_d); 21081da177e4SLinus Torvalds handler.u = ieee754dp_sqrt; 21091da177e4SLinus Torvalds goto dcopuop; 211008a07904SRalf Baechle /* 211108a07904SRalf Baechle * Note that on some MIPS IV implementations such as the 211208a07904SRalf Baechle * R5000 and R8000 the FSQRT and FRECIP instructions do not 211308a07904SRalf Baechle * achieve full IEEE-754 accuracy - however this emulator does. 211408a07904SRalf Baechle */ 21151da177e4SLinus Torvalds case frsqrt_op: 21162d83fea7SMaciej W. Rozycki if (!cpu_has_mips_4_5_64_r2_r6) 211708a07904SRalf Baechle return SIGILL; 211808a07904SRalf Baechle 2119454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(rsqrt_d); 21201da177e4SLinus Torvalds handler.u = fpemu_dp_rsqrt; 21211da177e4SLinus Torvalds goto dcopuop; 21221da177e4SLinus Torvalds case frecip_op: 21232d83fea7SMaciej W. Rozycki if (!cpu_has_mips_4_5_64_r2_r6) 212408a07904SRalf Baechle return SIGILL; 212508a07904SRalf Baechle 2126454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(recip_d); 21271da177e4SLinus Torvalds handler.u = fpemu_dp_recip; 21281da177e4SLinus Torvalds goto dcopuop; 21291da177e4SLinus Torvalds case fmovc_op: 213008a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 213108a07904SRalf Baechle return SIGILL; 213208a07904SRalf Baechle 21331da177e4SLinus Torvalds cond = fpucondbit[MIPSInst_FT(ir) >> 2]; 21341da177e4SLinus Torvalds if (((ctx->fcr31 & cond) != 0) != 21351da177e4SLinus Torvalds ((MIPSInst_FT(ir) & 1) != 0)) 21361da177e4SLinus Torvalds return 0; 21371da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 21381da177e4SLinus Torvalds break; 21391da177e4SLinus Torvalds case fmovz_op: 214008a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 214108a07904SRalf Baechle return SIGILL; 214208a07904SRalf Baechle 21431da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] != 0) 21441da177e4SLinus Torvalds return 0; 21451da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 21461da177e4SLinus Torvalds break; 21471da177e4SLinus Torvalds case fmovn_op: 214808a07904SRalf Baechle if (!cpu_has_mips_4_5_r) 214908a07904SRalf Baechle return SIGILL; 215008a07904SRalf Baechle 21511da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] == 0) 21521da177e4SLinus Torvalds return 0; 21531da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 21541da177e4SLinus Torvalds break; 215567613f02SMarkos Chandras 215667613f02SMarkos Chandras case fseleqz_op: 215767613f02SMarkos Chandras if (!cpu_has_mips_r6) 215867613f02SMarkos Chandras return SIGILL; 215967613f02SMarkos Chandras 2160454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(seleqz_d); 216167613f02SMarkos Chandras DPFROMREG(rv.d, MIPSInst_FT(ir)); 216267613f02SMarkos Chandras if (rv.l & 0x1) 216367613f02SMarkos Chandras rv.l = 0; 216467613f02SMarkos Chandras else 216567613f02SMarkos Chandras DPFROMREG(rv.d, MIPSInst_FS(ir)); 216667613f02SMarkos Chandras break; 216767613f02SMarkos Chandras 2168130fe357SMarkos Chandras case fselnez_op: 2169130fe357SMarkos Chandras if (!cpu_has_mips_r6) 2170130fe357SMarkos Chandras return SIGILL; 2171130fe357SMarkos Chandras 2172454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(selnez_d); 2173130fe357SMarkos Chandras DPFROMREG(rv.d, MIPSInst_FT(ir)); 2174130fe357SMarkos Chandras if (rv.l & 0x1) 2175130fe357SMarkos Chandras DPFROMREG(rv.d, MIPSInst_FS(ir)); 2176130fe357SMarkos Chandras else 2177130fe357SMarkos Chandras rv.l = 0; 2178130fe357SMarkos Chandras break; 2179130fe357SMarkos Chandras 2180e24c3becSMarkos Chandras case fmaddf_op: { 2181e24c3becSMarkos Chandras union ieee754dp ft, fs, fd; 2182e24c3becSMarkos Chandras 2183e24c3becSMarkos Chandras if (!cpu_has_mips_r6) 2184e24c3becSMarkos Chandras return SIGILL; 2185e24c3becSMarkos Chandras 2186454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(maddf_d); 2187e24c3becSMarkos Chandras DPFROMREG(ft, MIPSInst_FT(ir)); 2188e24c3becSMarkos Chandras DPFROMREG(fs, MIPSInst_FS(ir)); 2189e24c3becSMarkos Chandras DPFROMREG(fd, MIPSInst_FD(ir)); 2190e24c3becSMarkos Chandras rv.d = ieee754dp_maddf(fd, fs, ft); 2191409fcaceSAleksandar Markovic goto copcsr; 2192e24c3becSMarkos Chandras } 2193e24c3becSMarkos Chandras 219483d43305SMarkos Chandras case fmsubf_op: { 219583d43305SMarkos Chandras union ieee754dp ft, fs, fd; 219683d43305SMarkos Chandras 219783d43305SMarkos Chandras if (!cpu_has_mips_r6) 219883d43305SMarkos Chandras return SIGILL; 219983d43305SMarkos Chandras 2200454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(msubf_d); 220183d43305SMarkos Chandras DPFROMREG(ft, MIPSInst_FT(ir)); 220283d43305SMarkos Chandras DPFROMREG(fs, MIPSInst_FS(ir)); 220383d43305SMarkos Chandras DPFROMREG(fd, MIPSInst_FD(ir)); 220483d43305SMarkos Chandras rv.d = ieee754dp_msubf(fd, fs, ft); 2205409fcaceSAleksandar Markovic goto copcsr; 220683d43305SMarkos Chandras } 220783d43305SMarkos Chandras 2208400bd2e4SMarkos Chandras case frint_op: { 2209400bd2e4SMarkos Chandras union ieee754dp fs; 2210400bd2e4SMarkos Chandras 2211400bd2e4SMarkos Chandras if (!cpu_has_mips_r6) 2212400bd2e4SMarkos Chandras return SIGILL; 2213400bd2e4SMarkos Chandras 2214454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(rint_d); 2215400bd2e4SMarkos Chandras DPFROMREG(fs, MIPSInst_FS(ir)); 22163ec404d8SAleksandar Markovic rv.d = ieee754dp_rint(fs); 2217400bd2e4SMarkos Chandras goto copcsr; 2218400bd2e4SMarkos Chandras } 2219400bd2e4SMarkos Chandras 222038db37baSMarkos Chandras case fclass_op: { 222138db37baSMarkos Chandras union ieee754dp fs; 222238db37baSMarkos Chandras 222338db37baSMarkos Chandras if (!cpu_has_mips_r6) 222438db37baSMarkos Chandras return SIGILL; 222538db37baSMarkos Chandras 2226454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(class_d); 222738db37baSMarkos Chandras DPFROMREG(fs, MIPSInst_FS(ir)); 2228e1231dd6SAleksandar Markovic rv.l = ieee754dp_2008class(fs); 2229e1231dd6SAleksandar Markovic rfmt = l_fmt; 2230409fcaceSAleksandar Markovic goto copcsr; 223138db37baSMarkos Chandras } 223238db37baSMarkos Chandras 22334e9561b2SMarkos Chandras case fmin_op: { 22344e9561b2SMarkos Chandras union ieee754dp fs, ft; 22354e9561b2SMarkos Chandras 22364e9561b2SMarkos Chandras if (!cpu_has_mips_r6) 22374e9561b2SMarkos Chandras return SIGILL; 22384e9561b2SMarkos Chandras 2239454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(min_d); 22404e9561b2SMarkos Chandras DPFROMREG(ft, MIPSInst_FT(ir)); 22414e9561b2SMarkos Chandras DPFROMREG(fs, MIPSInst_FS(ir)); 22424e9561b2SMarkos Chandras rv.d = ieee754dp_fmin(fs, ft); 2243409fcaceSAleksandar Markovic goto copcsr; 22444e9561b2SMarkos Chandras } 22454e9561b2SMarkos Chandras 22464e9561b2SMarkos Chandras case fmina_op: { 22474e9561b2SMarkos Chandras union ieee754dp fs, ft; 22484e9561b2SMarkos Chandras 22494e9561b2SMarkos Chandras if (!cpu_has_mips_r6) 22504e9561b2SMarkos Chandras return SIGILL; 22514e9561b2SMarkos Chandras 2252454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(mina_d); 22534e9561b2SMarkos Chandras DPFROMREG(ft, MIPSInst_FT(ir)); 22544e9561b2SMarkos Chandras DPFROMREG(fs, MIPSInst_FS(ir)); 22554e9561b2SMarkos Chandras rv.d = ieee754dp_fmina(fs, ft); 2256409fcaceSAleksandar Markovic goto copcsr; 22574e9561b2SMarkos Chandras } 22584e9561b2SMarkos Chandras 2259a79f5f9bSMarkos Chandras case fmax_op: { 2260a79f5f9bSMarkos Chandras union ieee754dp fs, ft; 2261a79f5f9bSMarkos Chandras 2262a79f5f9bSMarkos Chandras if (!cpu_has_mips_r6) 2263a79f5f9bSMarkos Chandras return SIGILL; 2264a79f5f9bSMarkos Chandras 2265454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(max_d); 2266a79f5f9bSMarkos Chandras DPFROMREG(ft, MIPSInst_FT(ir)); 2267a79f5f9bSMarkos Chandras DPFROMREG(fs, MIPSInst_FS(ir)); 2268a79f5f9bSMarkos Chandras rv.d = ieee754dp_fmax(fs, ft); 2269409fcaceSAleksandar Markovic goto copcsr; 2270a79f5f9bSMarkos Chandras } 2271a79f5f9bSMarkos Chandras 2272a79f5f9bSMarkos Chandras case fmaxa_op: { 2273a79f5f9bSMarkos Chandras union ieee754dp fs, ft; 2274a79f5f9bSMarkos Chandras 2275a79f5f9bSMarkos Chandras if (!cpu_has_mips_r6) 2276a79f5f9bSMarkos Chandras return SIGILL; 2277a79f5f9bSMarkos Chandras 2278454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(maxa_d); 2279a79f5f9bSMarkos Chandras DPFROMREG(ft, MIPSInst_FT(ir)); 2280a79f5f9bSMarkos Chandras DPFROMREG(fs, MIPSInst_FS(ir)); 2281a79f5f9bSMarkos Chandras rv.d = ieee754dp_fmaxa(fs, ft); 2282409fcaceSAleksandar Markovic goto copcsr; 2283a79f5f9bSMarkos Chandras } 2284a79f5f9bSMarkos Chandras 22851da177e4SLinus Torvalds case fabs_op: 2286454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(abs_d); 22871da177e4SLinus Torvalds handler.u = ieee754dp_abs; 22881da177e4SLinus Torvalds goto dcopuop; 22891da177e4SLinus Torvalds 22901da177e4SLinus Torvalds case fneg_op: 2291454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(neg_d); 22921da177e4SLinus Torvalds handler.u = ieee754dp_neg; 22931da177e4SLinus Torvalds goto dcopuop; 22941da177e4SLinus Torvalds 22951da177e4SLinus Torvalds case fmov_op: 22961da177e4SLinus Torvalds /* an easy one */ 2297454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(mov_d); 22981da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 22991da177e4SLinus Torvalds goto copcsr; 23001da177e4SLinus Torvalds 23011da177e4SLinus Torvalds /* binary op on handler */ 23023f7cac41SRalf Baechle dcopbop: 23031da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 23041da177e4SLinus Torvalds DPFROMREG(ft, MIPSInst_FT(ir)); 23051da177e4SLinus Torvalds 23061da177e4SLinus Torvalds rv.d = (*handler.b) (fs, ft); 23071da177e4SLinus Torvalds goto copcsr; 23083f7cac41SRalf Baechle dcopuop: 23091da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 23101da177e4SLinus Torvalds rv.d = (*handler.u) (fs); 23111da177e4SLinus Torvalds goto copcsr; 23121da177e4SLinus Torvalds 23133f7cac41SRalf Baechle /* 23143f7cac41SRalf Baechle * unary conv ops 23153f7cac41SRalf Baechle */ 23163f7cac41SRalf Baechle case fcvts_op: 2317454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_s_d); 23181da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 23191da177e4SLinus Torvalds rv.s = ieee754sp_fdp(fs); 23201da177e4SLinus Torvalds rfmt = s_fmt; 23211da177e4SLinus Torvalds goto copcsr; 23223f7cac41SRalf Baechle 23231da177e4SLinus Torvalds case fcvtd_op: 23241da177e4SLinus Torvalds return SIGILL; /* not defined */ 23251da177e4SLinus Torvalds 23263f7cac41SRalf Baechle case fcvtw_op: 2327454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_w_d); 23281da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 23291da177e4SLinus Torvalds rv.w = ieee754dp_tint(fs); /* wrong */ 23301da177e4SLinus Torvalds rfmt = w_fmt; 23311da177e4SLinus Torvalds goto copcsr; 23321da177e4SLinus Torvalds 23331da177e4SLinus Torvalds case fround_op: 23341da177e4SLinus Torvalds case ftrunc_op: 23351da177e4SLinus Torvalds case fceil_op: 23363f7cac41SRalf Baechle case ffloor_op: 233708a07904SRalf Baechle if (!cpu_has_mips_2_3_4_5_r) 233808a07904SRalf Baechle return SIGILL; 233908a07904SRalf Baechle 2340454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == fceil_op) 2341454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(ceil_w_d); 2342454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == ffloor_op) 2343454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(floor_w_d); 2344454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == fround_op) 2345454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(round_w_d); 2346454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == ftrunc_op) 2347454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(trunc_w_d); 2348454854acSAleksandar Markovic 23493f7cac41SRalf Baechle oldrm = ieee754_csr.rm; 23501da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 23512cfcf8a8SMaciej W. Rozycki ieee754_csr.rm = MIPSInst_FUNC(ir); 23521da177e4SLinus Torvalds rv.w = ieee754dp_tint(fs); 23531da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 23541da177e4SLinus Torvalds rfmt = w_fmt; 23551da177e4SLinus Torvalds goto copcsr; 23561da177e4SLinus Torvalds 23574b820d95SPaul Burton case fsel_op: 23584b820d95SPaul Burton if (!cpu_has_mips_r6) 23594b820d95SPaul Burton return SIGILL; 23604b820d95SPaul Burton 2361454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(sel_d); 23624b820d95SPaul Burton DPFROMREG(fd, MIPSInst_FD(ir)); 23634b820d95SPaul Burton if (fd.bits & 0x1) 23644b820d95SPaul Burton DPFROMREG(rv.d, MIPSInst_FT(ir)); 23654b820d95SPaul Burton else 23664b820d95SPaul Burton DPFROMREG(rv.d, MIPSInst_FS(ir)); 23674b820d95SPaul Burton break; 23684b820d95SPaul Burton 23693f7cac41SRalf Baechle case fcvtl_op: 23702d83fea7SMaciej W. Rozycki if (!cpu_has_mips_3_4_5_64_r2_r6) 237108a07904SRalf Baechle return SIGILL; 237208a07904SRalf Baechle 2373454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_l_d); 23741da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 23751da177e4SLinus Torvalds rv.l = ieee754dp_tlong(fs); 23761da177e4SLinus Torvalds rfmt = l_fmt; 23771da177e4SLinus Torvalds goto copcsr; 23781da177e4SLinus Torvalds 23791da177e4SLinus Torvalds case froundl_op: 23801da177e4SLinus Torvalds case ftruncl_op: 23811da177e4SLinus Torvalds case fceill_op: 23823f7cac41SRalf Baechle case ffloorl_op: 23832d83fea7SMaciej W. Rozycki if (!cpu_has_mips_3_4_5_64_r2_r6) 238408a07904SRalf Baechle return SIGILL; 238508a07904SRalf Baechle 2386454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == fceill_op) 2387454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(ceil_l_d); 2388454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == ffloorl_op) 2389454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(floor_l_d); 2390454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == froundl_op) 2391454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(round_l_d); 2392454854acSAleksandar Markovic if (MIPSInst_FUNC(ir) == ftruncl_op) 2393454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(trunc_l_d); 2394454854acSAleksandar Markovic 23953f7cac41SRalf Baechle oldrm = ieee754_csr.rm; 23961da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 23972cfcf8a8SMaciej W. Rozycki ieee754_csr.rm = MIPSInst_FUNC(ir); 23981da177e4SLinus Torvalds rv.l = ieee754dp_tlong(fs); 23991da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 24001da177e4SLinus Torvalds rfmt = l_fmt; 24011da177e4SLinus Torvalds goto copcsr; 24021da177e4SLinus Torvalds 24031da177e4SLinus Torvalds default: 2404f8c3c671SMarkos Chandras if (!NO_R6EMU && MIPSInst_FUNC(ir) >= fcmp_op) { 2405a58f85b5SAleksandar Markovic unsigned int cmpop; 24062209bcb1SRalf Baechle union ieee754dp fs, ft; 24071da177e4SLinus Torvalds 2408a58f85b5SAleksandar Markovic cmpop = MIPSInst_FUNC(ir) - fcmp_op; 24091da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 24101da177e4SLinus Torvalds DPFROMREG(ft, MIPSInst_FT(ir)); 24111da177e4SLinus Torvalds rv.w = ieee754dp_cmp(fs, ft, 24121da177e4SLinus Torvalds cmptab[cmpop & 0x7], cmpop & 0x8); 24131da177e4SLinus Torvalds rfmt = -1; 24141da177e4SLinus Torvalds if ((cmpop & 0x8) 24151da177e4SLinus Torvalds && 24161da177e4SLinus Torvalds ieee754_cxtest 24171da177e4SLinus Torvalds (IEEE754_INVALID_OPERATION)) 24181da177e4SLinus Torvalds rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 24191da177e4SLinus Torvalds else 24201da177e4SLinus Torvalds goto copcsr; 24211da177e4SLinus Torvalds 24221da177e4SLinus Torvalds } 24231da177e4SLinus Torvalds else { 24241da177e4SLinus Torvalds return SIGILL; 24251da177e4SLinus Torvalds } 24261da177e4SLinus Torvalds break; 24271da177e4SLinus Torvalds } 24281da177e4SLinus Torvalds break; 2429bbdd8147SMarkos Chandras } 24301da177e4SLinus Torvalds 2431bbdd8147SMarkos Chandras case w_fmt: { 2432bbdd8147SMarkos Chandras union ieee754dp fs; 2433bbdd8147SMarkos Chandras 24341da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 24351da177e4SLinus Torvalds case fcvts_op: 24361da177e4SLinus Torvalds /* convert word to single precision real */ 2437454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_s_w); 24381da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 24391da177e4SLinus Torvalds rv.s = ieee754sp_fint(fs.bits); 24401da177e4SLinus Torvalds rfmt = s_fmt; 24411da177e4SLinus Torvalds goto copcsr; 24421da177e4SLinus Torvalds case fcvtd_op: 24431da177e4SLinus Torvalds /* convert word to double precision real */ 2444454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_d_w); 24451da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 24461da177e4SLinus Torvalds rv.d = ieee754dp_fint(fs.bits); 24471da177e4SLinus Torvalds rfmt = d_fmt; 24481da177e4SLinus Torvalds goto copcsr; 2449f8c3c671SMarkos Chandras default: { 2450f8c3c671SMarkos Chandras /* Emulating the new CMP.condn.fmt R6 instruction */ 2451f8c3c671SMarkos Chandras #define CMPOP_MASK 0x7 2452f8c3c671SMarkos Chandras #define SIGN_BIT (0x1 << 3) 2453f8c3c671SMarkos Chandras #define PREDICATE_BIT (0x1 << 4) 2454f8c3c671SMarkos Chandras 2455f8c3c671SMarkos Chandras int cmpop = MIPSInst_FUNC(ir) & CMPOP_MASK; 2456f8c3c671SMarkos Chandras int sig = MIPSInst_FUNC(ir) & SIGN_BIT; 2457f8c3c671SMarkos Chandras union ieee754sp fs, ft; 2458f8c3c671SMarkos Chandras 2459f8c3c671SMarkos Chandras /* This is an R6 only instruction */ 2460f8c3c671SMarkos Chandras if (!cpu_has_mips_r6 || 2461f8c3c671SMarkos Chandras (MIPSInst_FUNC(ir) & 0x20)) 2462f8c3c671SMarkos Chandras return SIGILL; 2463f8c3c671SMarkos Chandras 2464454854acSAleksandar Markovic if (!sig) { 2465454854acSAleksandar Markovic if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { 2466454854acSAleksandar Markovic switch (cmpop) { 2467454854acSAleksandar Markovic case 0: 2468454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_af_s); 2469454854acSAleksandar Markovic break; 2470454854acSAleksandar Markovic case 1: 2471454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_un_s); 2472454854acSAleksandar Markovic break; 2473454854acSAleksandar Markovic case 2: 2474454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_eq_s); 2475454854acSAleksandar Markovic break; 2476454854acSAleksandar Markovic case 3: 2477454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_ueq_s); 2478454854acSAleksandar Markovic break; 2479454854acSAleksandar Markovic case 4: 2480454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_lt_s); 2481454854acSAleksandar Markovic break; 2482454854acSAleksandar Markovic case 5: 2483454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_ult_s); 2484454854acSAleksandar Markovic break; 2485454854acSAleksandar Markovic case 6: 2486454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_le_s); 2487454854acSAleksandar Markovic break; 2488454854acSAleksandar Markovic case 7: 2489454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_ule_s); 2490454854acSAleksandar Markovic break; 2491454854acSAleksandar Markovic } 2492454854acSAleksandar Markovic } else { 2493454854acSAleksandar Markovic switch (cmpop) { 2494454854acSAleksandar Markovic case 1: 2495454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_or_s); 2496454854acSAleksandar Markovic break; 2497454854acSAleksandar Markovic case 2: 2498454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_une_s); 2499454854acSAleksandar Markovic break; 2500454854acSAleksandar Markovic case 3: 2501454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_ne_s); 2502454854acSAleksandar Markovic break; 2503454854acSAleksandar Markovic } 2504454854acSAleksandar Markovic } 2505454854acSAleksandar Markovic } else { 2506454854acSAleksandar Markovic if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { 2507454854acSAleksandar Markovic switch (cmpop) { 2508454854acSAleksandar Markovic case 0: 2509454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_saf_s); 2510454854acSAleksandar Markovic break; 2511454854acSAleksandar Markovic case 1: 2512454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sun_s); 2513454854acSAleksandar Markovic break; 2514454854acSAleksandar Markovic case 2: 2515454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_seq_s); 2516454854acSAleksandar Markovic break; 2517454854acSAleksandar Markovic case 3: 2518454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sueq_s); 2519454854acSAleksandar Markovic break; 2520454854acSAleksandar Markovic case 4: 2521454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_slt_s); 2522454854acSAleksandar Markovic break; 2523454854acSAleksandar Markovic case 5: 2524454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sult_s); 2525454854acSAleksandar Markovic break; 2526454854acSAleksandar Markovic case 6: 2527454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sle_s); 2528454854acSAleksandar Markovic break; 2529454854acSAleksandar Markovic case 7: 2530454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sule_s); 2531454854acSAleksandar Markovic break; 2532454854acSAleksandar Markovic } 2533454854acSAleksandar Markovic } else { 2534454854acSAleksandar Markovic switch (cmpop) { 2535454854acSAleksandar Markovic case 1: 2536454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sor_s); 2537454854acSAleksandar Markovic break; 2538454854acSAleksandar Markovic case 2: 2539454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sune_s); 2540454854acSAleksandar Markovic break; 2541454854acSAleksandar Markovic case 3: 2542454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sne_s); 2543454854acSAleksandar Markovic break; 2544454854acSAleksandar Markovic } 2545454854acSAleksandar Markovic } 2546454854acSAleksandar Markovic } 2547454854acSAleksandar Markovic 2548f8c3c671SMarkos Chandras /* fmt is w_fmt for single precision so fix it */ 2549f8c3c671SMarkos Chandras rfmt = s_fmt; 2550f8c3c671SMarkos Chandras /* default to false */ 2551f8c3c671SMarkos Chandras rv.w = 0; 2552f8c3c671SMarkos Chandras 2553f8c3c671SMarkos Chandras /* CMP.condn.S */ 2554f8c3c671SMarkos Chandras SPFROMREG(fs, MIPSInst_FS(ir)); 2555f8c3c671SMarkos Chandras SPFROMREG(ft, MIPSInst_FT(ir)); 2556f8c3c671SMarkos Chandras 2557f8c3c671SMarkos Chandras /* positive predicates */ 2558f8c3c671SMarkos Chandras if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { 2559f8c3c671SMarkos Chandras if (ieee754sp_cmp(fs, ft, cmptab[cmpop], 2560f8c3c671SMarkos Chandras sig)) 2561f8c3c671SMarkos Chandras rv.w = -1; /* true, all 1s */ 2562f8c3c671SMarkos Chandras if ((sig) && 2563f8c3c671SMarkos Chandras ieee754_cxtest(IEEE754_INVALID_OPERATION)) 2564f8c3c671SMarkos Chandras rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 2565f8c3c671SMarkos Chandras else 2566f8c3c671SMarkos Chandras goto copcsr; 2567f8c3c671SMarkos Chandras } else { 2568f8c3c671SMarkos Chandras /* negative predicates */ 2569f8c3c671SMarkos Chandras switch (cmpop) { 2570f8c3c671SMarkos Chandras case 1: 2571f8c3c671SMarkos Chandras case 2: 2572f8c3c671SMarkos Chandras case 3: 2573f8c3c671SMarkos Chandras if (ieee754sp_cmp(fs, ft, 2574f8c3c671SMarkos Chandras negative_cmptab[cmpop], 2575f8c3c671SMarkos Chandras sig)) 2576f8c3c671SMarkos Chandras rv.w = -1; /* true, all 1s */ 2577f8c3c671SMarkos Chandras if (sig && 2578f8c3c671SMarkos Chandras ieee754_cxtest(IEEE754_INVALID_OPERATION)) 2579f8c3c671SMarkos Chandras rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 2580f8c3c671SMarkos Chandras else 2581f8c3c671SMarkos Chandras goto copcsr; 2582f8c3c671SMarkos Chandras break; 25831da177e4SLinus Torvalds default: 2584f8c3c671SMarkos Chandras /* Reserved R6 ops */ 25851da177e4SLinus Torvalds return SIGILL; 25861da177e4SLinus Torvalds } 2587f8c3c671SMarkos Chandras } 25881da177e4SLinus Torvalds break; 25891da177e4SLinus Torvalds } 2590f8c3c671SMarkos Chandras } 25911ff8560aSAleksandar Markovic break; 2592f8c3c671SMarkos Chandras } 25931da177e4SLinus Torvalds 25943f7cac41SRalf Baechle case l_fmt: 259508a07904SRalf Baechle 25962d83fea7SMaciej W. Rozycki if (!cpu_has_mips_3_4_5_64_r2_r6) 259708a07904SRalf Baechle return SIGILL; 259808a07904SRalf Baechle 2599bbd426f5SPaul Burton DIFROMREG(bits, MIPSInst_FS(ir)); 2600bbd426f5SPaul Burton 26011da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 26021da177e4SLinus Torvalds case fcvts_op: 26031da177e4SLinus Torvalds /* convert long to single precision real */ 2604454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_s_l); 2605bbd426f5SPaul Burton rv.s = ieee754sp_flong(bits); 26061da177e4SLinus Torvalds rfmt = s_fmt; 26071da177e4SLinus Torvalds goto copcsr; 26081da177e4SLinus Torvalds case fcvtd_op: 26091da177e4SLinus Torvalds /* convert long to double precision real */ 2610454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cvt_d_l); 2611bbd426f5SPaul Burton rv.d = ieee754dp_flong(bits); 26121da177e4SLinus Torvalds rfmt = d_fmt; 26131da177e4SLinus Torvalds goto copcsr; 2614f8c3c671SMarkos Chandras default: { 2615f8c3c671SMarkos Chandras /* Emulating the new CMP.condn.fmt R6 instruction */ 2616f8c3c671SMarkos Chandras int cmpop = MIPSInst_FUNC(ir) & CMPOP_MASK; 2617f8c3c671SMarkos Chandras int sig = MIPSInst_FUNC(ir) & SIGN_BIT; 2618f8c3c671SMarkos Chandras union ieee754dp fs, ft; 2619f8c3c671SMarkos Chandras 2620f8c3c671SMarkos Chandras if (!cpu_has_mips_r6 || 2621f8c3c671SMarkos Chandras (MIPSInst_FUNC(ir) & 0x20)) 2622f8c3c671SMarkos Chandras return SIGILL; 2623f8c3c671SMarkos Chandras 2624454854acSAleksandar Markovic if (!sig) { 2625454854acSAleksandar Markovic if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { 2626454854acSAleksandar Markovic switch (cmpop) { 2627454854acSAleksandar Markovic case 0: 2628454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_af_d); 2629454854acSAleksandar Markovic break; 2630454854acSAleksandar Markovic case 1: 2631454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_un_d); 2632454854acSAleksandar Markovic break; 2633454854acSAleksandar Markovic case 2: 2634454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_eq_d); 2635454854acSAleksandar Markovic break; 2636454854acSAleksandar Markovic case 3: 2637454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_ueq_d); 2638454854acSAleksandar Markovic break; 2639454854acSAleksandar Markovic case 4: 2640454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_lt_d); 2641454854acSAleksandar Markovic break; 2642454854acSAleksandar Markovic case 5: 2643454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_ult_d); 2644454854acSAleksandar Markovic break; 2645454854acSAleksandar Markovic case 6: 2646454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_le_d); 2647454854acSAleksandar Markovic break; 2648454854acSAleksandar Markovic case 7: 2649454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_ule_d); 2650454854acSAleksandar Markovic break; 2651454854acSAleksandar Markovic } 2652454854acSAleksandar Markovic } else { 2653454854acSAleksandar Markovic switch (cmpop) { 2654454854acSAleksandar Markovic case 1: 2655454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_or_d); 2656454854acSAleksandar Markovic break; 2657454854acSAleksandar Markovic case 2: 2658454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_une_d); 2659454854acSAleksandar Markovic break; 2660454854acSAleksandar Markovic case 3: 2661454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_ne_d); 2662454854acSAleksandar Markovic break; 2663454854acSAleksandar Markovic } 2664454854acSAleksandar Markovic } 2665454854acSAleksandar Markovic } else { 2666454854acSAleksandar Markovic if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { 2667454854acSAleksandar Markovic switch (cmpop) { 2668454854acSAleksandar Markovic case 0: 2669454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_saf_d); 2670454854acSAleksandar Markovic break; 2671454854acSAleksandar Markovic case 1: 2672454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sun_d); 2673454854acSAleksandar Markovic break; 2674454854acSAleksandar Markovic case 2: 2675454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_seq_d); 2676454854acSAleksandar Markovic break; 2677454854acSAleksandar Markovic case 3: 2678454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sueq_d); 2679454854acSAleksandar Markovic break; 2680454854acSAleksandar Markovic case 4: 2681454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_slt_d); 2682454854acSAleksandar Markovic break; 2683454854acSAleksandar Markovic case 5: 2684454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sult_d); 2685454854acSAleksandar Markovic break; 2686454854acSAleksandar Markovic case 6: 2687454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sle_d); 2688454854acSAleksandar Markovic break; 2689454854acSAleksandar Markovic case 7: 2690454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sule_d); 2691454854acSAleksandar Markovic break; 2692454854acSAleksandar Markovic } 2693454854acSAleksandar Markovic } else { 2694454854acSAleksandar Markovic switch (cmpop) { 2695454854acSAleksandar Markovic case 1: 2696454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sor_d); 2697454854acSAleksandar Markovic break; 2698454854acSAleksandar Markovic case 2: 2699454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sune_d); 2700454854acSAleksandar Markovic break; 2701454854acSAleksandar Markovic case 3: 2702454854acSAleksandar Markovic MIPS_FPU_EMU_INC_STATS(cmp_sne_d); 2703454854acSAleksandar Markovic break; 2704454854acSAleksandar Markovic } 2705454854acSAleksandar Markovic } 2706454854acSAleksandar Markovic } 2707454854acSAleksandar Markovic 2708f8c3c671SMarkos Chandras /* fmt is l_fmt for double precision so fix it */ 2709f8c3c671SMarkos Chandras rfmt = d_fmt; 2710f8c3c671SMarkos Chandras /* default to false */ 2711f8c3c671SMarkos Chandras rv.l = 0; 2712f8c3c671SMarkos Chandras 2713f8c3c671SMarkos Chandras /* CMP.condn.D */ 2714f8c3c671SMarkos Chandras DPFROMREG(fs, MIPSInst_FS(ir)); 2715f8c3c671SMarkos Chandras DPFROMREG(ft, MIPSInst_FT(ir)); 2716f8c3c671SMarkos Chandras 2717f8c3c671SMarkos Chandras /* positive predicates */ 2718f8c3c671SMarkos Chandras if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { 2719f8c3c671SMarkos Chandras if (ieee754dp_cmp(fs, ft, 2720f8c3c671SMarkos Chandras cmptab[cmpop], sig)) 2721f8c3c671SMarkos Chandras rv.l = -1LL; /* true, all 1s */ 2722f8c3c671SMarkos Chandras if (sig && 2723f8c3c671SMarkos Chandras ieee754_cxtest(IEEE754_INVALID_OPERATION)) 2724f8c3c671SMarkos Chandras rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 2725f8c3c671SMarkos Chandras else 2726f8c3c671SMarkos Chandras goto copcsr; 2727f8c3c671SMarkos Chandras } else { 2728f8c3c671SMarkos Chandras /* negative predicates */ 2729f8c3c671SMarkos Chandras switch (cmpop) { 2730f8c3c671SMarkos Chandras case 1: 2731f8c3c671SMarkos Chandras case 2: 2732f8c3c671SMarkos Chandras case 3: 2733f8c3c671SMarkos Chandras if (ieee754dp_cmp(fs, ft, 2734f8c3c671SMarkos Chandras negative_cmptab[cmpop], 2735f8c3c671SMarkos Chandras sig)) 2736f8c3c671SMarkos Chandras rv.l = -1LL; /* true, all 1s */ 2737f8c3c671SMarkos Chandras if (sig && 2738f8c3c671SMarkos Chandras ieee754_cxtest(IEEE754_INVALID_OPERATION)) 2739f8c3c671SMarkos Chandras rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 2740f8c3c671SMarkos Chandras else 2741f8c3c671SMarkos Chandras goto copcsr; 2742f8c3c671SMarkos Chandras break; 27431da177e4SLinus Torvalds default: 2744f8c3c671SMarkos Chandras /* Reserved R6 ops */ 27451da177e4SLinus Torvalds return SIGILL; 27461da177e4SLinus Torvalds } 2747f8c3c671SMarkos Chandras } 27481da177e4SLinus Torvalds break; 2749f8c3c671SMarkos Chandras } 2750f8c3c671SMarkos Chandras } 27511ff8560aSAleksandar Markovic break; 27521ff8560aSAleksandar Markovic 27531da177e4SLinus Torvalds default: 27541da177e4SLinus Torvalds return SIGILL; 27551da177e4SLinus Torvalds } 27561da177e4SLinus Torvalds 27571da177e4SLinus Torvalds /* 27581da177e4SLinus Torvalds * Update the fpu CSR register for this operation. 27591da177e4SLinus Torvalds * If an exception is required, generate a tidy SIGFPE exception, 27601da177e4SLinus Torvalds * without updating the result register. 27611da177e4SLinus Torvalds * Note: cause exception bits do not accumulate, they are rewritten 27621da177e4SLinus Torvalds * for each op; only the flag/sticky bits accumulate. 27631da177e4SLinus Torvalds */ 27641da177e4SLinus Torvalds ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr; 27651da177e4SLinus Torvalds if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 27663f7cac41SRalf Baechle /*printk ("SIGFPE: FPU csr = %08x\n",ctx->fcr31); */ 27671da177e4SLinus Torvalds return SIGFPE; 27681da177e4SLinus Torvalds } 27691da177e4SLinus Torvalds 27701da177e4SLinus Torvalds /* 27711da177e4SLinus Torvalds * Now we can safely write the result back to the register file. 27721da177e4SLinus Torvalds */ 27731da177e4SLinus Torvalds switch (rfmt) { 277408a07904SRalf Baechle case -1: 277508a07904SRalf Baechle 277608a07904SRalf Baechle if (cpu_has_mips_4_5_r) 2777c3b9b945SRob Kendrick cbit = fpucondbit[MIPSInst_FD(ir) >> 2]; 27781da177e4SLinus Torvalds else 277908a07904SRalf Baechle cbit = FPU_CSR_COND; 278008a07904SRalf Baechle if (rv.w) 278108a07904SRalf Baechle ctx->fcr31 |= cbit; 278208a07904SRalf Baechle else 278308a07904SRalf Baechle ctx->fcr31 &= ~cbit; 27841da177e4SLinus Torvalds break; 278508a07904SRalf Baechle 27861da177e4SLinus Torvalds case d_fmt: 27871da177e4SLinus Torvalds DPTOREG(rv.d, MIPSInst_FD(ir)); 27881da177e4SLinus Torvalds break; 27891da177e4SLinus Torvalds case s_fmt: 27901da177e4SLinus Torvalds SPTOREG(rv.s, MIPSInst_FD(ir)); 27911da177e4SLinus Torvalds break; 27921da177e4SLinus Torvalds case w_fmt: 27931da177e4SLinus Torvalds SITOREG(rv.w, MIPSInst_FD(ir)); 27941da177e4SLinus Torvalds break; 27951da177e4SLinus Torvalds case l_fmt: 27962d83fea7SMaciej W. Rozycki if (!cpu_has_mips_3_4_5_64_r2_r6) 279708a07904SRalf Baechle return SIGILL; 279808a07904SRalf Baechle 27991da177e4SLinus Torvalds DITOREG(rv.l, MIPSInst_FD(ir)); 28001da177e4SLinus Torvalds break; 28011da177e4SLinus Torvalds default: 28021da177e4SLinus Torvalds return SIGILL; 28031da177e4SLinus Torvalds } 28041da177e4SLinus Torvalds 28051da177e4SLinus Torvalds return 0; 28061da177e4SLinus Torvalds } 28071da177e4SLinus Torvalds 280813769ebaSMaciej W. Rozycki /* 280913769ebaSMaciej W. Rozycki * Emulate FPU instructions. 281013769ebaSMaciej W. Rozycki * 281113769ebaSMaciej W. Rozycki * If we use FPU hardware, then we have been typically called to handle 281213769ebaSMaciej W. Rozycki * an unimplemented operation, such as where an operand is a NaN or 281313769ebaSMaciej W. Rozycki * denormalized. In that case exit the emulation loop after a single 281413769ebaSMaciej W. Rozycki * iteration so as to let hardware execute any subsequent instructions. 281513769ebaSMaciej W. Rozycki * 281613769ebaSMaciej W. Rozycki * If we have no FPU hardware or it has been disabled, then continue 281713769ebaSMaciej W. Rozycki * emulating floating-point instructions until one of these conditions 281813769ebaSMaciej W. Rozycki * has occurred: 281913769ebaSMaciej W. Rozycki * 282013769ebaSMaciej W. Rozycki * - a non-FPU instruction has been encountered, 282113769ebaSMaciej W. Rozycki * 282213769ebaSMaciej W. Rozycki * - an attempt to emulate has ended with a signal, 282313769ebaSMaciej W. Rozycki * 282413769ebaSMaciej W. Rozycki * - the ISA mode has been switched. 282513769ebaSMaciej W. Rozycki * 282613769ebaSMaciej W. Rozycki * We need to terminate the emulation loop if we got switched to the 282713769ebaSMaciej W. Rozycki * MIPS16 mode, whether supported or not, so that we do not attempt 282813769ebaSMaciej W. Rozycki * to emulate a MIPS16 instruction as a regular MIPS FPU instruction. 282913769ebaSMaciej W. Rozycki * Similarly if we got switched to the microMIPS mode and only the 283013769ebaSMaciej W. Rozycki * regular MIPS mode is supported, so that we do not attempt to emulate 283113769ebaSMaciej W. Rozycki * a microMIPS instruction as a regular MIPS FPU instruction. Or if 283213769ebaSMaciej W. Rozycki * we got switched to the regular MIPS mode and only the microMIPS mode 283313769ebaSMaciej W. Rozycki * is supported, so that we do not attempt to emulate a regular MIPS 283413769ebaSMaciej W. Rozycki * instruction that should cause an Address Error exception instead. 283513769ebaSMaciej W. Rozycki * For simplicity we always terminate upon an ISA mode switch. 283613769ebaSMaciej W. Rozycki */ 2837e04582b7SAtsushi Nemoto int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 2838445a58ceSPaul Burton int has_fpu, void __user **fault_addr) 28391da177e4SLinus Torvalds { 2840333d1f67SRalf Baechle unsigned long oldepc, prevepc; 2841102cedc3SLeonid Yegoshin struct mm_decoded_insn dec_insn; 2842102cedc3SLeonid Yegoshin u16 instr[4]; 2843102cedc3SLeonid Yegoshin u16 *instr_ptr; 28441da177e4SLinus Torvalds int sig = 0; 28451da177e4SLinus Torvalds 28461975ed43SPaul Burton /* 28471975ed43SPaul Burton * Initialize context if it hasn't been used already, otherwise ensure 28481975ed43SPaul Burton * it has been saved to struct thread_struct. 28491975ed43SPaul Burton */ 28501975ed43SPaul Burton if (!init_fp_ctx(current)) 28511975ed43SPaul Burton lose_fpu(1); 28521975ed43SPaul Burton 28531da177e4SLinus Torvalds oldepc = xcp->cp0_epc; 28541da177e4SLinus Torvalds do { 28551da177e4SLinus Torvalds prevepc = xcp->cp0_epc; 28561da177e4SLinus Torvalds 2857102cedc3SLeonid Yegoshin if (get_isa16_mode(prevepc) && cpu_has_mmips) { 2858102cedc3SLeonid Yegoshin /* 2859102cedc3SLeonid Yegoshin * Get next 2 microMIPS instructions and convert them 2860102cedc3SLeonid Yegoshin * into 32-bit instructions. 2861102cedc3SLeonid Yegoshin */ 2862102cedc3SLeonid Yegoshin if ((get_user(instr[0], (u16 __user *)msk_isa16_mode(xcp->cp0_epc))) || 2863102cedc3SLeonid Yegoshin (get_user(instr[1], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 2))) || 2864102cedc3SLeonid Yegoshin (get_user(instr[2], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 4))) || 2865102cedc3SLeonid Yegoshin (get_user(instr[3], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 6)))) { 2866b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 28671da177e4SLinus Torvalds return SIGBUS; 28681da177e4SLinus Torvalds } 2869102cedc3SLeonid Yegoshin instr_ptr = instr; 2870102cedc3SLeonid Yegoshin 2871102cedc3SLeonid Yegoshin /* Get first instruction. */ 2872102cedc3SLeonid Yegoshin if (mm_insn_16bit(*instr_ptr)) { 2873102cedc3SLeonid Yegoshin /* Duplicate the half-word. */ 2874102cedc3SLeonid Yegoshin dec_insn.insn = (*instr_ptr << 16) | 2875102cedc3SLeonid Yegoshin (*instr_ptr); 2876102cedc3SLeonid Yegoshin /* 16-bit instruction. */ 2877102cedc3SLeonid Yegoshin dec_insn.pc_inc = 2; 2878102cedc3SLeonid Yegoshin instr_ptr += 1; 2879102cedc3SLeonid Yegoshin } else { 2880102cedc3SLeonid Yegoshin dec_insn.insn = (*instr_ptr << 16) | 2881102cedc3SLeonid Yegoshin *(instr_ptr+1); 2882102cedc3SLeonid Yegoshin /* 32-bit instruction. */ 2883102cedc3SLeonid Yegoshin dec_insn.pc_inc = 4; 2884102cedc3SLeonid Yegoshin instr_ptr += 2; 2885515b029dSDavid Daney } 2886102cedc3SLeonid Yegoshin /* Get second instruction. */ 2887102cedc3SLeonid Yegoshin if (mm_insn_16bit(*instr_ptr)) { 2888102cedc3SLeonid Yegoshin /* Duplicate the half-word. */ 2889102cedc3SLeonid Yegoshin dec_insn.next_insn = (*instr_ptr << 16) | 2890102cedc3SLeonid Yegoshin (*instr_ptr); 2891102cedc3SLeonid Yegoshin /* 16-bit instruction. */ 2892102cedc3SLeonid Yegoshin dec_insn.next_pc_inc = 2; 2893102cedc3SLeonid Yegoshin } else { 2894102cedc3SLeonid Yegoshin dec_insn.next_insn = (*instr_ptr << 16) | 2895102cedc3SLeonid Yegoshin *(instr_ptr+1); 2896102cedc3SLeonid Yegoshin /* 32-bit instruction. */ 2897102cedc3SLeonid Yegoshin dec_insn.next_pc_inc = 4; 2898102cedc3SLeonid Yegoshin } 2899102cedc3SLeonid Yegoshin dec_insn.micro_mips_mode = 1; 2900102cedc3SLeonid Yegoshin } else { 2901102cedc3SLeonid Yegoshin if ((get_user(dec_insn.insn, 2902102cedc3SLeonid Yegoshin (mips_instruction __user *) xcp->cp0_epc)) || 2903102cedc3SLeonid Yegoshin (get_user(dec_insn.next_insn, 2904102cedc3SLeonid Yegoshin (mips_instruction __user *)(xcp->cp0_epc+4)))) { 2905102cedc3SLeonid Yegoshin MIPS_FPU_EMU_INC_STATS(errors); 2906102cedc3SLeonid Yegoshin return SIGBUS; 2907102cedc3SLeonid Yegoshin } 2908102cedc3SLeonid Yegoshin dec_insn.pc_inc = 4; 2909102cedc3SLeonid Yegoshin dec_insn.next_pc_inc = 4; 2910102cedc3SLeonid Yegoshin dec_insn.micro_mips_mode = 0; 2911102cedc3SLeonid Yegoshin } 2912102cedc3SLeonid Yegoshin 2913102cedc3SLeonid Yegoshin if ((dec_insn.insn == 0) || 2914102cedc3SLeonid Yegoshin ((dec_insn.pc_inc == 2) && 2915102cedc3SLeonid Yegoshin ((dec_insn.insn & 0xffff) == MM_NOP16))) 2916102cedc3SLeonid Yegoshin xcp->cp0_epc += dec_insn.pc_inc; /* Skip NOPs */ 29171da177e4SLinus Torvalds else { 2918cd21dfcfSRalf Baechle /* 29192cfcf8a8SMaciej W. Rozycki * The 'ieee754_csr' is an alias of ctx->fcr31. 29202cfcf8a8SMaciej W. Rozycki * No need to copy ctx->fcr31 to ieee754_csr. 2921cd21dfcfSRalf Baechle */ 2922102cedc3SLeonid Yegoshin sig = cop1Emulate(xcp, ctx, dec_insn, fault_addr); 29231da177e4SLinus Torvalds } 29241da177e4SLinus Torvalds 2925e04582b7SAtsushi Nemoto if (has_fpu) 29261da177e4SLinus Torvalds break; 29271da177e4SLinus Torvalds if (sig) 29281da177e4SLinus Torvalds break; 292913769ebaSMaciej W. Rozycki /* 293013769ebaSMaciej W. Rozycki * We have to check for the ISA bit explicitly here, 293113769ebaSMaciej W. Rozycki * because `get_isa16_mode' may return 0 if support 293213769ebaSMaciej W. Rozycki * for code compression has been globally disabled, 293313769ebaSMaciej W. Rozycki * or otherwise we may produce the wrong signal or 293413769ebaSMaciej W. Rozycki * even proceed successfully where we must not. 293513769ebaSMaciej W. Rozycki */ 293613769ebaSMaciej W. Rozycki if ((xcp->cp0_epc ^ prevepc) & 0x1) 293713769ebaSMaciej W. Rozycki break; 29381da177e4SLinus Torvalds 29391da177e4SLinus Torvalds cond_resched(); 29401da177e4SLinus Torvalds } while (xcp->cp0_epc > prevepc); 29411da177e4SLinus Torvalds 29421da177e4SLinus Torvalds /* SIGILL indicates a non-fpu instruction */ 29431da177e4SLinus Torvalds if (sig == SIGILL && xcp->cp0_epc != oldepc) 29443f7cac41SRalf Baechle /* but if EPC has advanced, then ignore it */ 29451da177e4SLinus Torvalds sig = 0; 29461da177e4SLinus Torvalds 29471da177e4SLinus Torvalds return sig; 29481da177e4SLinus Torvalds } 2949