11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * cp1emu.c: a MIPS coprocessor 1 (fpu) instruction emulator 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * MIPS floating point support 51da177e4SLinus Torvalds * Copyright (C) 1994-2000 Algorithmics Ltd. 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com 81da177e4SLinus Torvalds * Copyright (C) 2000 MIPS Technologies, Inc. 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * This program is free software; you can distribute it and/or modify it 111da177e4SLinus Torvalds * under the terms of the GNU General Public License (Version 2) as 121da177e4SLinus Torvalds * published by the Free Software Foundation. 131da177e4SLinus Torvalds * 141da177e4SLinus Torvalds * This program is distributed in the hope it will be useful, but WITHOUT 151da177e4SLinus Torvalds * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 161da177e4SLinus Torvalds * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 171da177e4SLinus Torvalds * for more details. 181da177e4SLinus Torvalds * 191da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License along 201da177e4SLinus Torvalds * with this program; if not, write to the Free Software Foundation, Inc., 211da177e4SLinus Torvalds * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. 221da177e4SLinus Torvalds * 231da177e4SLinus Torvalds * A complete emulator for MIPS coprocessor 1 instructions. This is 241da177e4SLinus Torvalds * required for #float(switch) or #float(trap), where it catches all 251da177e4SLinus Torvalds * COP1 instructions via the "CoProcessor Unusable" exception. 261da177e4SLinus Torvalds * 271da177e4SLinus Torvalds * More surprisingly it is also required for #float(ieee), to help out 281da177e4SLinus Torvalds * the hardware fpu at the boundaries of the IEEE-754 representation 291da177e4SLinus Torvalds * (denormalised values, infinities, underflow, etc). It is made 301da177e4SLinus Torvalds * quite nasty because emulation of some non-COP1 instructions is 311da177e4SLinus Torvalds * required, e.g. in branch delay slots. 321da177e4SLinus Torvalds * 331da177e4SLinus Torvalds * Note if you know that you won't have an fpu, then you'll get much 341da177e4SLinus Torvalds * better performance by compiling with -msoft-float! 351da177e4SLinus Torvalds */ 361da177e4SLinus Torvalds #include <linux/sched.h> 37b6ee75edSDavid Daney #include <linux/module.h> 3883fd38caSAtsushi Nemoto #include <linux/debugfs.h> 397f788d2dSDeng-Cheng Zhu #include <linux/perf_event.h> 401da177e4SLinus Torvalds 411da177e4SLinus Torvalds #include <asm/inst.h> 421da177e4SLinus Torvalds #include <asm/bootinfo.h> 431da177e4SLinus Torvalds #include <asm/processor.h> 441da177e4SLinus Torvalds #include <asm/ptrace.h> 451da177e4SLinus Torvalds #include <asm/signal.h> 461da177e4SLinus Torvalds #include <asm/mipsregs.h> 471da177e4SLinus Torvalds #include <asm/fpu_emulator.h> 48102cedc3SLeonid Yegoshin #include <asm/fpu.h> 491da177e4SLinus Torvalds #include <asm/uaccess.h> 501da177e4SLinus Torvalds #include <asm/branch.h> 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds #include "ieee754.h" 531da177e4SLinus Torvalds 541da177e4SLinus Torvalds /* Strap kernel emulator for full MIPS IV emulation */ 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds #ifdef __mips 571da177e4SLinus Torvalds #undef __mips 581da177e4SLinus Torvalds #endif 591da177e4SLinus Torvalds #define __mips 4 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds /* Function which emulates a floating point instruction. */ 621da177e4SLinus Torvalds 63eae89076SAtsushi Nemoto static int fpu_emu(struct pt_regs *, struct mips_fpu_struct *, 641da177e4SLinus Torvalds mips_instruction); 651da177e4SLinus Torvalds 661da177e4SLinus Torvalds #if __mips >= 4 && __mips != 32 671da177e4SLinus Torvalds static int fpux_emu(struct pt_regs *, 68515b029dSDavid Daney struct mips_fpu_struct *, mips_instruction, void *__user *); 691da177e4SLinus Torvalds #endif 701da177e4SLinus Torvalds 71eae89076SAtsushi Nemoto /* Further private data for which no space exists in mips_fpu_struct */ 721da177e4SLinus Torvalds 73b6ee75edSDavid Daney #ifdef CONFIG_DEBUG_FS 74b6ee75edSDavid Daney DEFINE_PER_CPU(struct mips_fpu_emulator_stats, fpuemustats); 75b6ee75edSDavid Daney #endif 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds /* Control registers */ 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds #define FPCREG_RID 0 /* $0 = revision id */ 801da177e4SLinus Torvalds #define FPCREG_CSR 31 /* $31 = csr */ 811da177e4SLinus Torvalds 8295e8f634SShane McDonald /* Determine rounding mode from the RM bits of the FCSR */ 8395e8f634SShane McDonald #define modeindex(v) ((v) & FPU_CSR_RM) 8495e8f634SShane McDonald 85102cedc3SLeonid Yegoshin /* microMIPS bitfields */ 86102cedc3SLeonid Yegoshin #define MM_POOL32A_MINOR_MASK 0x3f 87102cedc3SLeonid Yegoshin #define MM_POOL32A_MINOR_SHIFT 0x6 88102cedc3SLeonid Yegoshin #define MM_MIPS32_COND_FC 0x30 89102cedc3SLeonid Yegoshin 901da177e4SLinus Torvalds /* Convert Mips rounding mode (0..3) to IEEE library modes. */ 911da177e4SLinus Torvalds static const unsigned char ieee_rm[4] = { 92cd21dfcfSRalf Baechle [FPU_CSR_RN] = IEEE754_RN, 93cd21dfcfSRalf Baechle [FPU_CSR_RZ] = IEEE754_RZ, 94cd21dfcfSRalf Baechle [FPU_CSR_RU] = IEEE754_RU, 95cd21dfcfSRalf Baechle [FPU_CSR_RD] = IEEE754_RD, 96cd21dfcfSRalf Baechle }; 97cd21dfcfSRalf Baechle /* Convert IEEE library modes to Mips rounding mode (0..3). */ 98cd21dfcfSRalf Baechle static const unsigned char mips_rm[4] = { 99cd21dfcfSRalf Baechle [IEEE754_RN] = FPU_CSR_RN, 100cd21dfcfSRalf Baechle [IEEE754_RZ] = FPU_CSR_RZ, 101cd21dfcfSRalf Baechle [IEEE754_RD] = FPU_CSR_RD, 102cd21dfcfSRalf Baechle [IEEE754_RU] = FPU_CSR_RU, 1031da177e4SLinus Torvalds }; 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds #if __mips >= 4 1061da177e4SLinus Torvalds /* convert condition code register number to csr bit */ 1071da177e4SLinus Torvalds static const unsigned int fpucondbit[8] = { 1081da177e4SLinus Torvalds FPU_CSR_COND0, 1091da177e4SLinus Torvalds FPU_CSR_COND1, 1101da177e4SLinus Torvalds FPU_CSR_COND2, 1111da177e4SLinus Torvalds FPU_CSR_COND3, 1121da177e4SLinus Torvalds FPU_CSR_COND4, 1131da177e4SLinus Torvalds FPU_CSR_COND5, 1141da177e4SLinus Torvalds FPU_CSR_COND6, 1151da177e4SLinus Torvalds FPU_CSR_COND7 1161da177e4SLinus Torvalds }; 1171da177e4SLinus Torvalds #endif 1181da177e4SLinus Torvalds 119102cedc3SLeonid Yegoshin /* (microMIPS) Convert 16-bit register encoding to 32-bit register encoding. */ 120102cedc3SLeonid Yegoshin static const unsigned int reg16to32map[8] = {16, 17, 2, 3, 4, 5, 6, 7}; 121102cedc3SLeonid Yegoshin 122102cedc3SLeonid Yegoshin /* (microMIPS) Convert certain microMIPS instructions to MIPS32 format. */ 123102cedc3SLeonid Yegoshin static const int sd_format[] = {16, 17, 0, 0, 0, 0, 0, 0}; 124102cedc3SLeonid Yegoshin static const int sdps_format[] = {16, 17, 22, 0, 0, 0, 0, 0}; 125102cedc3SLeonid Yegoshin static const int dwl_format[] = {17, 20, 21, 0, 0, 0, 0, 0}; 126102cedc3SLeonid Yegoshin static const int swl_format[] = {16, 20, 21, 0, 0, 0, 0, 0}; 127102cedc3SLeonid Yegoshin 128102cedc3SLeonid Yegoshin /* 129102cedc3SLeonid Yegoshin * This functions translates a 32-bit microMIPS instruction 130102cedc3SLeonid Yegoshin * into a 32-bit MIPS32 instruction. Returns 0 on success 131102cedc3SLeonid Yegoshin * and SIGILL otherwise. 132102cedc3SLeonid Yegoshin */ 133102cedc3SLeonid Yegoshin static int microMIPS32_to_MIPS32(union mips_instruction *insn_ptr) 134102cedc3SLeonid Yegoshin { 135102cedc3SLeonid Yegoshin union mips_instruction insn = *insn_ptr; 136102cedc3SLeonid Yegoshin union mips_instruction mips32_insn = insn; 137102cedc3SLeonid Yegoshin int func, fmt, op; 138102cedc3SLeonid Yegoshin 139102cedc3SLeonid Yegoshin switch (insn.mm_i_format.opcode) { 140102cedc3SLeonid Yegoshin case mm_ldc132_op: 141102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.opcode = ldc1_op; 142102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 143102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 144102cedc3SLeonid Yegoshin break; 145102cedc3SLeonid Yegoshin case mm_lwc132_op: 146102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.opcode = lwc1_op; 147102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 148102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 149102cedc3SLeonid Yegoshin break; 150102cedc3SLeonid Yegoshin case mm_sdc132_op: 151102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.opcode = sdc1_op; 152102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 153102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 154102cedc3SLeonid Yegoshin break; 155102cedc3SLeonid Yegoshin case mm_swc132_op: 156102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.opcode = swc1_op; 157102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 158102cedc3SLeonid Yegoshin mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 159102cedc3SLeonid Yegoshin break; 160102cedc3SLeonid Yegoshin case mm_pool32i_op: 161102cedc3SLeonid Yegoshin /* NOTE: offset is << by 1 if in microMIPS mode. */ 162102cedc3SLeonid Yegoshin if ((insn.mm_i_format.rt == mm_bc1f_op) || 163102cedc3SLeonid Yegoshin (insn.mm_i_format.rt == mm_bc1t_op)) { 164102cedc3SLeonid Yegoshin mips32_insn.fb_format.opcode = cop1_op; 165102cedc3SLeonid Yegoshin mips32_insn.fb_format.bc = bc_op; 166102cedc3SLeonid Yegoshin mips32_insn.fb_format.flag = 167102cedc3SLeonid Yegoshin (insn.mm_i_format.rt == mm_bc1t_op) ? 1 : 0; 168102cedc3SLeonid Yegoshin } else 169102cedc3SLeonid Yegoshin return SIGILL; 170102cedc3SLeonid Yegoshin break; 171102cedc3SLeonid Yegoshin case mm_pool32f_op: 172102cedc3SLeonid Yegoshin switch (insn.mm_fp0_format.func) { 173102cedc3SLeonid Yegoshin case mm_32f_01_op: 174102cedc3SLeonid Yegoshin case mm_32f_11_op: 175102cedc3SLeonid Yegoshin case mm_32f_02_op: 176102cedc3SLeonid Yegoshin case mm_32f_12_op: 177102cedc3SLeonid Yegoshin case mm_32f_41_op: 178102cedc3SLeonid Yegoshin case mm_32f_51_op: 179102cedc3SLeonid Yegoshin case mm_32f_42_op: 180102cedc3SLeonid Yegoshin case mm_32f_52_op: 181102cedc3SLeonid Yegoshin op = insn.mm_fp0_format.func; 182102cedc3SLeonid Yegoshin if (op == mm_32f_01_op) 183102cedc3SLeonid Yegoshin func = madd_s_op; 184102cedc3SLeonid Yegoshin else if (op == mm_32f_11_op) 185102cedc3SLeonid Yegoshin func = madd_d_op; 186102cedc3SLeonid Yegoshin else if (op == mm_32f_02_op) 187102cedc3SLeonid Yegoshin func = nmadd_s_op; 188102cedc3SLeonid Yegoshin else if (op == mm_32f_12_op) 189102cedc3SLeonid Yegoshin func = nmadd_d_op; 190102cedc3SLeonid Yegoshin else if (op == mm_32f_41_op) 191102cedc3SLeonid Yegoshin func = msub_s_op; 192102cedc3SLeonid Yegoshin else if (op == mm_32f_51_op) 193102cedc3SLeonid Yegoshin func = msub_d_op; 194102cedc3SLeonid Yegoshin else if (op == mm_32f_42_op) 195102cedc3SLeonid Yegoshin func = nmsub_s_op; 196102cedc3SLeonid Yegoshin else 197102cedc3SLeonid Yegoshin func = nmsub_d_op; 198102cedc3SLeonid Yegoshin mips32_insn.fp6_format.opcode = cop1x_op; 199102cedc3SLeonid Yegoshin mips32_insn.fp6_format.fr = insn.mm_fp6_format.fr; 200102cedc3SLeonid Yegoshin mips32_insn.fp6_format.ft = insn.mm_fp6_format.ft; 201102cedc3SLeonid Yegoshin mips32_insn.fp6_format.fs = insn.mm_fp6_format.fs; 202102cedc3SLeonid Yegoshin mips32_insn.fp6_format.fd = insn.mm_fp6_format.fd; 203102cedc3SLeonid Yegoshin mips32_insn.fp6_format.func = func; 204102cedc3SLeonid Yegoshin break; 205102cedc3SLeonid Yegoshin case mm_32f_10_op: 206102cedc3SLeonid Yegoshin func = -1; /* Invalid */ 207102cedc3SLeonid Yegoshin op = insn.mm_fp5_format.op & 0x7; 208102cedc3SLeonid Yegoshin if (op == mm_ldxc1_op) 209102cedc3SLeonid Yegoshin func = ldxc1_op; 210102cedc3SLeonid Yegoshin else if (op == mm_sdxc1_op) 211102cedc3SLeonid Yegoshin func = sdxc1_op; 212102cedc3SLeonid Yegoshin else if (op == mm_lwxc1_op) 213102cedc3SLeonid Yegoshin func = lwxc1_op; 214102cedc3SLeonid Yegoshin else if (op == mm_swxc1_op) 215102cedc3SLeonid Yegoshin func = swxc1_op; 216102cedc3SLeonid Yegoshin 217102cedc3SLeonid Yegoshin if (func != -1) { 218102cedc3SLeonid Yegoshin mips32_insn.r_format.opcode = cop1x_op; 219102cedc3SLeonid Yegoshin mips32_insn.r_format.rs = 220102cedc3SLeonid Yegoshin insn.mm_fp5_format.base; 221102cedc3SLeonid Yegoshin mips32_insn.r_format.rt = 222102cedc3SLeonid Yegoshin insn.mm_fp5_format.index; 223102cedc3SLeonid Yegoshin mips32_insn.r_format.rd = 0; 224102cedc3SLeonid Yegoshin mips32_insn.r_format.re = insn.mm_fp5_format.fd; 225102cedc3SLeonid Yegoshin mips32_insn.r_format.func = func; 226102cedc3SLeonid Yegoshin } else 227102cedc3SLeonid Yegoshin return SIGILL; 228102cedc3SLeonid Yegoshin break; 229102cedc3SLeonid Yegoshin case mm_32f_40_op: 230102cedc3SLeonid Yegoshin op = -1; /* Invalid */ 231102cedc3SLeonid Yegoshin if (insn.mm_fp2_format.op == mm_fmovt_op) 232102cedc3SLeonid Yegoshin op = 1; 233102cedc3SLeonid Yegoshin else if (insn.mm_fp2_format.op == mm_fmovf_op) 234102cedc3SLeonid Yegoshin op = 0; 235102cedc3SLeonid Yegoshin if (op != -1) { 236102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 237102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 238102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp2_format.fmt]; 239102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 240102cedc3SLeonid Yegoshin (insn.mm_fp2_format.cc<<2) + op; 241102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 242102cedc3SLeonid Yegoshin insn.mm_fp2_format.fs; 243102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 244102cedc3SLeonid Yegoshin insn.mm_fp2_format.fd; 245102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = fmovc_op; 246102cedc3SLeonid Yegoshin } else 247102cedc3SLeonid Yegoshin return SIGILL; 248102cedc3SLeonid Yegoshin break; 249102cedc3SLeonid Yegoshin case mm_32f_60_op: 250102cedc3SLeonid Yegoshin func = -1; /* Invalid */ 251102cedc3SLeonid Yegoshin if (insn.mm_fp0_format.op == mm_fadd_op) 252102cedc3SLeonid Yegoshin func = fadd_op; 253102cedc3SLeonid Yegoshin else if (insn.mm_fp0_format.op == mm_fsub_op) 254102cedc3SLeonid Yegoshin func = fsub_op; 255102cedc3SLeonid Yegoshin else if (insn.mm_fp0_format.op == mm_fmul_op) 256102cedc3SLeonid Yegoshin func = fmul_op; 257102cedc3SLeonid Yegoshin else if (insn.mm_fp0_format.op == mm_fdiv_op) 258102cedc3SLeonid Yegoshin func = fdiv_op; 259102cedc3SLeonid Yegoshin if (func != -1) { 260102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 261102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 262102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp0_format.fmt]; 263102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 264102cedc3SLeonid Yegoshin insn.mm_fp0_format.ft; 265102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 266102cedc3SLeonid Yegoshin insn.mm_fp0_format.fs; 267102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 268102cedc3SLeonid Yegoshin insn.mm_fp0_format.fd; 269102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 270102cedc3SLeonid Yegoshin } else 271102cedc3SLeonid Yegoshin return SIGILL; 272102cedc3SLeonid Yegoshin break; 273102cedc3SLeonid Yegoshin case mm_32f_70_op: 274102cedc3SLeonid Yegoshin func = -1; /* Invalid */ 275102cedc3SLeonid Yegoshin if (insn.mm_fp0_format.op == mm_fmovn_op) 276102cedc3SLeonid Yegoshin func = fmovn_op; 277102cedc3SLeonid Yegoshin else if (insn.mm_fp0_format.op == mm_fmovz_op) 278102cedc3SLeonid Yegoshin func = fmovz_op; 279102cedc3SLeonid Yegoshin if (func != -1) { 280102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 281102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 282102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp0_format.fmt]; 283102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 284102cedc3SLeonid Yegoshin insn.mm_fp0_format.ft; 285102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 286102cedc3SLeonid Yegoshin insn.mm_fp0_format.fs; 287102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 288102cedc3SLeonid Yegoshin insn.mm_fp0_format.fd; 289102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 290102cedc3SLeonid Yegoshin } else 291102cedc3SLeonid Yegoshin return SIGILL; 292102cedc3SLeonid Yegoshin break; 293102cedc3SLeonid Yegoshin case mm_32f_73_op: /* POOL32FXF */ 294102cedc3SLeonid Yegoshin switch (insn.mm_fp1_format.op) { 295102cedc3SLeonid Yegoshin case mm_movf0_op: 296102cedc3SLeonid Yegoshin case mm_movf1_op: 297102cedc3SLeonid Yegoshin case mm_movt0_op: 298102cedc3SLeonid Yegoshin case mm_movt1_op: 299102cedc3SLeonid Yegoshin if ((insn.mm_fp1_format.op & 0x7f) == 300102cedc3SLeonid Yegoshin mm_movf0_op) 301102cedc3SLeonid Yegoshin op = 0; 302102cedc3SLeonid Yegoshin else 303102cedc3SLeonid Yegoshin op = 1; 304102cedc3SLeonid Yegoshin mips32_insn.r_format.opcode = spec_op; 305102cedc3SLeonid Yegoshin mips32_insn.r_format.rs = insn.mm_fp4_format.fs; 306102cedc3SLeonid Yegoshin mips32_insn.r_format.rt = 307102cedc3SLeonid Yegoshin (insn.mm_fp4_format.cc << 2) + op; 308102cedc3SLeonid Yegoshin mips32_insn.r_format.rd = insn.mm_fp4_format.rt; 309102cedc3SLeonid Yegoshin mips32_insn.r_format.re = 0; 310102cedc3SLeonid Yegoshin mips32_insn.r_format.func = movc_op; 311102cedc3SLeonid Yegoshin break; 312102cedc3SLeonid Yegoshin case mm_fcvtd0_op: 313102cedc3SLeonid Yegoshin case mm_fcvtd1_op: 314102cedc3SLeonid Yegoshin case mm_fcvts0_op: 315102cedc3SLeonid Yegoshin case mm_fcvts1_op: 316102cedc3SLeonid Yegoshin if ((insn.mm_fp1_format.op & 0x7f) == 317102cedc3SLeonid Yegoshin mm_fcvtd0_op) { 318102cedc3SLeonid Yegoshin func = fcvtd_op; 319102cedc3SLeonid Yegoshin fmt = swl_format[insn.mm_fp3_format.fmt]; 320102cedc3SLeonid Yegoshin } else { 321102cedc3SLeonid Yegoshin func = fcvts_op; 322102cedc3SLeonid Yegoshin fmt = dwl_format[insn.mm_fp3_format.fmt]; 323102cedc3SLeonid Yegoshin } 324102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 325102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = fmt; 326102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 0; 327102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 328102cedc3SLeonid Yegoshin insn.mm_fp3_format.fs; 329102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 330102cedc3SLeonid Yegoshin insn.mm_fp3_format.rt; 331102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 332102cedc3SLeonid Yegoshin break; 333102cedc3SLeonid Yegoshin case mm_fmov0_op: 334102cedc3SLeonid Yegoshin case mm_fmov1_op: 335102cedc3SLeonid Yegoshin case mm_fabs0_op: 336102cedc3SLeonid Yegoshin case mm_fabs1_op: 337102cedc3SLeonid Yegoshin case mm_fneg0_op: 338102cedc3SLeonid Yegoshin case mm_fneg1_op: 339102cedc3SLeonid Yegoshin if ((insn.mm_fp1_format.op & 0x7f) == 340102cedc3SLeonid Yegoshin mm_fmov0_op) 341102cedc3SLeonid Yegoshin func = fmov_op; 342102cedc3SLeonid Yegoshin else if ((insn.mm_fp1_format.op & 0x7f) == 343102cedc3SLeonid Yegoshin mm_fabs0_op) 344102cedc3SLeonid Yegoshin func = fabs_op; 345102cedc3SLeonid Yegoshin else 346102cedc3SLeonid Yegoshin func = fneg_op; 347102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 348102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 349102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp3_format.fmt]; 350102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 0; 351102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 352102cedc3SLeonid Yegoshin insn.mm_fp3_format.fs; 353102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 354102cedc3SLeonid Yegoshin insn.mm_fp3_format.rt; 355102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 356102cedc3SLeonid Yegoshin break; 357102cedc3SLeonid Yegoshin case mm_ffloorl_op: 358102cedc3SLeonid Yegoshin case mm_ffloorw_op: 359102cedc3SLeonid Yegoshin case mm_fceill_op: 360102cedc3SLeonid Yegoshin case mm_fceilw_op: 361102cedc3SLeonid Yegoshin case mm_ftruncl_op: 362102cedc3SLeonid Yegoshin case mm_ftruncw_op: 363102cedc3SLeonid Yegoshin case mm_froundl_op: 364102cedc3SLeonid Yegoshin case mm_froundw_op: 365102cedc3SLeonid Yegoshin case mm_fcvtl_op: 366102cedc3SLeonid Yegoshin case mm_fcvtw_op: 367102cedc3SLeonid Yegoshin if (insn.mm_fp1_format.op == mm_ffloorl_op) 368102cedc3SLeonid Yegoshin func = ffloorl_op; 369102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_ffloorw_op) 370102cedc3SLeonid Yegoshin func = ffloor_op; 371102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_fceill_op) 372102cedc3SLeonid Yegoshin func = fceill_op; 373102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_fceilw_op) 374102cedc3SLeonid Yegoshin func = fceil_op; 375102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_ftruncl_op) 376102cedc3SLeonid Yegoshin func = ftruncl_op; 377102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_ftruncw_op) 378102cedc3SLeonid Yegoshin func = ftrunc_op; 379102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_froundl_op) 380102cedc3SLeonid Yegoshin func = froundl_op; 381102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_froundw_op) 382102cedc3SLeonid Yegoshin func = fround_op; 383102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_fcvtl_op) 384102cedc3SLeonid Yegoshin func = fcvtl_op; 385102cedc3SLeonid Yegoshin else 386102cedc3SLeonid Yegoshin func = fcvtw_op; 387102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 388102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 389102cedc3SLeonid Yegoshin sd_format[insn.mm_fp1_format.fmt]; 390102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 0; 391102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 392102cedc3SLeonid Yegoshin insn.mm_fp1_format.fs; 393102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 394102cedc3SLeonid Yegoshin insn.mm_fp1_format.rt; 395102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 396102cedc3SLeonid Yegoshin break; 397102cedc3SLeonid Yegoshin case mm_frsqrt_op: 398102cedc3SLeonid Yegoshin case mm_fsqrt_op: 399102cedc3SLeonid Yegoshin case mm_frecip_op: 400102cedc3SLeonid Yegoshin if (insn.mm_fp1_format.op == mm_frsqrt_op) 401102cedc3SLeonid Yegoshin func = frsqrt_op; 402102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_fsqrt_op) 403102cedc3SLeonid Yegoshin func = fsqrt_op; 404102cedc3SLeonid Yegoshin else 405102cedc3SLeonid Yegoshin func = frecip_op; 406102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 407102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 408102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp1_format.fmt]; 409102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = 0; 410102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = 411102cedc3SLeonid Yegoshin insn.mm_fp1_format.fs; 412102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = 413102cedc3SLeonid Yegoshin insn.mm_fp1_format.rt; 414102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = func; 415102cedc3SLeonid Yegoshin break; 416102cedc3SLeonid Yegoshin case mm_mfc1_op: 417102cedc3SLeonid Yegoshin case mm_mtc1_op: 418102cedc3SLeonid Yegoshin case mm_cfc1_op: 419102cedc3SLeonid Yegoshin case mm_ctc1_op: 420102cedc3SLeonid Yegoshin if (insn.mm_fp1_format.op == mm_mfc1_op) 421102cedc3SLeonid Yegoshin op = mfc_op; 422102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_mtc1_op) 423102cedc3SLeonid Yegoshin op = mtc_op; 424102cedc3SLeonid Yegoshin else if (insn.mm_fp1_format.op == mm_cfc1_op) 425102cedc3SLeonid Yegoshin op = cfc_op; 426102cedc3SLeonid Yegoshin else 427102cedc3SLeonid Yegoshin op = ctc_op; 428102cedc3SLeonid Yegoshin mips32_insn.fp1_format.opcode = cop1_op; 429102cedc3SLeonid Yegoshin mips32_insn.fp1_format.op = op; 430102cedc3SLeonid Yegoshin mips32_insn.fp1_format.rt = 431102cedc3SLeonid Yegoshin insn.mm_fp1_format.rt; 432102cedc3SLeonid Yegoshin mips32_insn.fp1_format.fs = 433102cedc3SLeonid Yegoshin insn.mm_fp1_format.fs; 434102cedc3SLeonid Yegoshin mips32_insn.fp1_format.fd = 0; 435102cedc3SLeonid Yegoshin mips32_insn.fp1_format.func = 0; 436102cedc3SLeonid Yegoshin break; 437102cedc3SLeonid Yegoshin default: 438102cedc3SLeonid Yegoshin return SIGILL; 439102cedc3SLeonid Yegoshin break; 440102cedc3SLeonid Yegoshin } 441102cedc3SLeonid Yegoshin break; 442102cedc3SLeonid Yegoshin case mm_32f_74_op: /* c.cond.fmt */ 443102cedc3SLeonid Yegoshin mips32_insn.fp0_format.opcode = cop1_op; 444102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fmt = 445102cedc3SLeonid Yegoshin sdps_format[insn.mm_fp4_format.fmt]; 446102cedc3SLeonid Yegoshin mips32_insn.fp0_format.ft = insn.mm_fp4_format.rt; 447102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fs = insn.mm_fp4_format.fs; 448102cedc3SLeonid Yegoshin mips32_insn.fp0_format.fd = insn.mm_fp4_format.cc << 2; 449102cedc3SLeonid Yegoshin mips32_insn.fp0_format.func = 450102cedc3SLeonid Yegoshin insn.mm_fp4_format.cond | MM_MIPS32_COND_FC; 451102cedc3SLeonid Yegoshin break; 452102cedc3SLeonid Yegoshin default: 453102cedc3SLeonid Yegoshin return SIGILL; 454102cedc3SLeonid Yegoshin break; 455102cedc3SLeonid Yegoshin } 456102cedc3SLeonid Yegoshin break; 457102cedc3SLeonid Yegoshin default: 458102cedc3SLeonid Yegoshin return SIGILL; 459102cedc3SLeonid Yegoshin break; 460102cedc3SLeonid Yegoshin } 461102cedc3SLeonid Yegoshin 462102cedc3SLeonid Yegoshin *insn_ptr = mips32_insn; 463102cedc3SLeonid Yegoshin return 0; 464102cedc3SLeonid Yegoshin } 465102cedc3SLeonid Yegoshin 466102cedc3SLeonid Yegoshin int mm_isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, 467102cedc3SLeonid Yegoshin unsigned long *contpc) 468102cedc3SLeonid Yegoshin { 469102cedc3SLeonid Yegoshin union mips_instruction insn = (union mips_instruction)dec_insn.insn; 470102cedc3SLeonid Yegoshin int bc_false = 0; 471102cedc3SLeonid Yegoshin unsigned int fcr31; 472102cedc3SLeonid Yegoshin unsigned int bit; 473102cedc3SLeonid Yegoshin 474*fe6d2909SDavid Daney if (!cpu_has_mmips) 475*fe6d2909SDavid Daney return 0; 476*fe6d2909SDavid Daney 477102cedc3SLeonid Yegoshin switch (insn.mm_i_format.opcode) { 478102cedc3SLeonid Yegoshin case mm_pool32a_op: 479102cedc3SLeonid Yegoshin if ((insn.mm_i_format.simmediate & MM_POOL32A_MINOR_MASK) == 480102cedc3SLeonid Yegoshin mm_pool32axf_op) { 481102cedc3SLeonid Yegoshin switch (insn.mm_i_format.simmediate >> 482102cedc3SLeonid Yegoshin MM_POOL32A_MINOR_SHIFT) { 483102cedc3SLeonid Yegoshin case mm_jalr_op: 484102cedc3SLeonid Yegoshin case mm_jalrhb_op: 485102cedc3SLeonid Yegoshin case mm_jalrs_op: 486102cedc3SLeonid Yegoshin case mm_jalrshb_op: 487102cedc3SLeonid Yegoshin if (insn.mm_i_format.rt != 0) /* Not mm_jr */ 488102cedc3SLeonid Yegoshin regs->regs[insn.mm_i_format.rt] = 489102cedc3SLeonid Yegoshin regs->cp0_epc + 490102cedc3SLeonid Yegoshin dec_insn.pc_inc + 491102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 492102cedc3SLeonid Yegoshin *contpc = regs->regs[insn.mm_i_format.rs]; 493102cedc3SLeonid Yegoshin return 1; 494102cedc3SLeonid Yegoshin break; 495102cedc3SLeonid Yegoshin } 496102cedc3SLeonid Yegoshin } 497102cedc3SLeonid Yegoshin break; 498102cedc3SLeonid Yegoshin case mm_pool32i_op: 499102cedc3SLeonid Yegoshin switch (insn.mm_i_format.rt) { 500102cedc3SLeonid Yegoshin case mm_bltzals_op: 501102cedc3SLeonid Yegoshin case mm_bltzal_op: 502102cedc3SLeonid Yegoshin regs->regs[31] = regs->cp0_epc + 503102cedc3SLeonid Yegoshin dec_insn.pc_inc + 504102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 505102cedc3SLeonid Yegoshin /* Fall through */ 506102cedc3SLeonid Yegoshin case mm_bltz_op: 507102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.mm_i_format.rs] < 0) 508102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 509102cedc3SLeonid Yegoshin dec_insn.pc_inc + 510102cedc3SLeonid Yegoshin (insn.mm_i_format.simmediate << 1); 511102cedc3SLeonid Yegoshin else 512102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 513102cedc3SLeonid Yegoshin dec_insn.pc_inc + 514102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 515102cedc3SLeonid Yegoshin return 1; 516102cedc3SLeonid Yegoshin break; 517102cedc3SLeonid Yegoshin case mm_bgezals_op: 518102cedc3SLeonid Yegoshin case mm_bgezal_op: 519102cedc3SLeonid Yegoshin regs->regs[31] = regs->cp0_epc + 520102cedc3SLeonid Yegoshin dec_insn.pc_inc + 521102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 522102cedc3SLeonid Yegoshin /* Fall through */ 523102cedc3SLeonid Yegoshin case mm_bgez_op: 524102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.mm_i_format.rs] >= 0) 525102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 526102cedc3SLeonid Yegoshin dec_insn.pc_inc + 527102cedc3SLeonid Yegoshin (insn.mm_i_format.simmediate << 1); 528102cedc3SLeonid Yegoshin else 529102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 530102cedc3SLeonid Yegoshin dec_insn.pc_inc + 531102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 532102cedc3SLeonid Yegoshin return 1; 533102cedc3SLeonid Yegoshin break; 534102cedc3SLeonid Yegoshin case mm_blez_op: 535102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.mm_i_format.rs] <= 0) 536102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 537102cedc3SLeonid Yegoshin dec_insn.pc_inc + 538102cedc3SLeonid Yegoshin (insn.mm_i_format.simmediate << 1); 539102cedc3SLeonid Yegoshin else 540102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 541102cedc3SLeonid Yegoshin dec_insn.pc_inc + 542102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 543102cedc3SLeonid Yegoshin return 1; 544102cedc3SLeonid Yegoshin break; 545102cedc3SLeonid Yegoshin case mm_bgtz_op: 546102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.mm_i_format.rs] <= 0) 547102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 548102cedc3SLeonid Yegoshin dec_insn.pc_inc + 549102cedc3SLeonid Yegoshin (insn.mm_i_format.simmediate << 1); 550102cedc3SLeonid Yegoshin else 551102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 552102cedc3SLeonid Yegoshin dec_insn.pc_inc + 553102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 554102cedc3SLeonid Yegoshin return 1; 555102cedc3SLeonid Yegoshin break; 556102cedc3SLeonid Yegoshin case mm_bc2f_op: 557102cedc3SLeonid Yegoshin case mm_bc1f_op: 558102cedc3SLeonid Yegoshin bc_false = 1; 559102cedc3SLeonid Yegoshin /* Fall through */ 560102cedc3SLeonid Yegoshin case mm_bc2t_op: 561102cedc3SLeonid Yegoshin case mm_bc1t_op: 562102cedc3SLeonid Yegoshin preempt_disable(); 563102cedc3SLeonid Yegoshin if (is_fpu_owner()) 564102cedc3SLeonid Yegoshin asm volatile("cfc1\t%0,$31" : "=r" (fcr31)); 565102cedc3SLeonid Yegoshin else 566102cedc3SLeonid Yegoshin fcr31 = current->thread.fpu.fcr31; 567102cedc3SLeonid Yegoshin preempt_enable(); 568102cedc3SLeonid Yegoshin 569102cedc3SLeonid Yegoshin if (bc_false) 570102cedc3SLeonid Yegoshin fcr31 = ~fcr31; 571102cedc3SLeonid Yegoshin 572102cedc3SLeonid Yegoshin bit = (insn.mm_i_format.rs >> 2); 573102cedc3SLeonid Yegoshin bit += (bit != 0); 574102cedc3SLeonid Yegoshin bit += 23; 575102cedc3SLeonid Yegoshin if (fcr31 & (1 << bit)) 576102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 577102cedc3SLeonid Yegoshin dec_insn.pc_inc + 578102cedc3SLeonid Yegoshin (insn.mm_i_format.simmediate << 1); 579102cedc3SLeonid Yegoshin else 580102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 581102cedc3SLeonid Yegoshin dec_insn.pc_inc + dec_insn.next_pc_inc; 582102cedc3SLeonid Yegoshin return 1; 583102cedc3SLeonid Yegoshin break; 584102cedc3SLeonid Yegoshin } 585102cedc3SLeonid Yegoshin break; 586102cedc3SLeonid Yegoshin case mm_pool16c_op: 587102cedc3SLeonid Yegoshin switch (insn.mm_i_format.rt) { 588102cedc3SLeonid Yegoshin case mm_jalr16_op: 589102cedc3SLeonid Yegoshin case mm_jalrs16_op: 590102cedc3SLeonid Yegoshin regs->regs[31] = regs->cp0_epc + 591102cedc3SLeonid Yegoshin dec_insn.pc_inc + dec_insn.next_pc_inc; 592102cedc3SLeonid Yegoshin /* Fall through */ 593102cedc3SLeonid Yegoshin case mm_jr16_op: 594102cedc3SLeonid Yegoshin *contpc = regs->regs[insn.mm_i_format.rs]; 595102cedc3SLeonid Yegoshin return 1; 596102cedc3SLeonid Yegoshin break; 597102cedc3SLeonid Yegoshin } 598102cedc3SLeonid Yegoshin break; 599102cedc3SLeonid Yegoshin case mm_beqz16_op: 600102cedc3SLeonid Yegoshin if ((long)regs->regs[reg16to32map[insn.mm_b1_format.rs]] == 0) 601102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 602102cedc3SLeonid Yegoshin dec_insn.pc_inc + 603102cedc3SLeonid Yegoshin (insn.mm_b1_format.simmediate << 1); 604102cedc3SLeonid Yegoshin else 605102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 606102cedc3SLeonid Yegoshin dec_insn.pc_inc + dec_insn.next_pc_inc; 607102cedc3SLeonid Yegoshin return 1; 608102cedc3SLeonid Yegoshin break; 609102cedc3SLeonid Yegoshin case mm_bnez16_op: 610102cedc3SLeonid Yegoshin if ((long)regs->regs[reg16to32map[insn.mm_b1_format.rs]] != 0) 611102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 612102cedc3SLeonid Yegoshin dec_insn.pc_inc + 613102cedc3SLeonid Yegoshin (insn.mm_b1_format.simmediate << 1); 614102cedc3SLeonid Yegoshin else 615102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 616102cedc3SLeonid Yegoshin dec_insn.pc_inc + dec_insn.next_pc_inc; 617102cedc3SLeonid Yegoshin return 1; 618102cedc3SLeonid Yegoshin break; 619102cedc3SLeonid Yegoshin case mm_b16_op: 620102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + dec_insn.pc_inc + 621102cedc3SLeonid Yegoshin (insn.mm_b0_format.simmediate << 1); 622102cedc3SLeonid Yegoshin return 1; 623102cedc3SLeonid Yegoshin break; 624102cedc3SLeonid Yegoshin case mm_beq32_op: 625102cedc3SLeonid Yegoshin if (regs->regs[insn.mm_i_format.rs] == 626102cedc3SLeonid Yegoshin regs->regs[insn.mm_i_format.rt]) 627102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 628102cedc3SLeonid Yegoshin dec_insn.pc_inc + 629102cedc3SLeonid Yegoshin (insn.mm_i_format.simmediate << 1); 630102cedc3SLeonid Yegoshin else 631102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 632102cedc3SLeonid Yegoshin dec_insn.pc_inc + 633102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 634102cedc3SLeonid Yegoshin return 1; 635102cedc3SLeonid Yegoshin break; 636102cedc3SLeonid Yegoshin case mm_bne32_op: 637102cedc3SLeonid Yegoshin if (regs->regs[insn.mm_i_format.rs] != 638102cedc3SLeonid Yegoshin regs->regs[insn.mm_i_format.rt]) 639102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 640102cedc3SLeonid Yegoshin dec_insn.pc_inc + 641102cedc3SLeonid Yegoshin (insn.mm_i_format.simmediate << 1); 642102cedc3SLeonid Yegoshin else 643102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 644102cedc3SLeonid Yegoshin dec_insn.pc_inc + dec_insn.next_pc_inc; 645102cedc3SLeonid Yegoshin return 1; 646102cedc3SLeonid Yegoshin break; 647102cedc3SLeonid Yegoshin case mm_jalx32_op: 648102cedc3SLeonid Yegoshin regs->regs[31] = regs->cp0_epc + 649102cedc3SLeonid Yegoshin dec_insn.pc_inc + dec_insn.next_pc_inc; 650102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + dec_insn.pc_inc; 651102cedc3SLeonid Yegoshin *contpc >>= 28; 652102cedc3SLeonid Yegoshin *contpc <<= 28; 653102cedc3SLeonid Yegoshin *contpc |= (insn.j_format.target << 2); 654102cedc3SLeonid Yegoshin return 1; 655102cedc3SLeonid Yegoshin break; 656102cedc3SLeonid Yegoshin case mm_jals32_op: 657102cedc3SLeonid Yegoshin case mm_jal32_op: 658102cedc3SLeonid Yegoshin regs->regs[31] = regs->cp0_epc + 659102cedc3SLeonid Yegoshin dec_insn.pc_inc + dec_insn.next_pc_inc; 660102cedc3SLeonid Yegoshin /* Fall through */ 661102cedc3SLeonid Yegoshin case mm_j32_op: 662102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + dec_insn.pc_inc; 663102cedc3SLeonid Yegoshin *contpc >>= 27; 664102cedc3SLeonid Yegoshin *contpc <<= 27; 665102cedc3SLeonid Yegoshin *contpc |= (insn.j_format.target << 1); 666102cedc3SLeonid Yegoshin set_isa16_mode(*contpc); 667102cedc3SLeonid Yegoshin return 1; 668102cedc3SLeonid Yegoshin break; 669102cedc3SLeonid Yegoshin } 670102cedc3SLeonid Yegoshin return 0; 671102cedc3SLeonid Yegoshin } 6721da177e4SLinus Torvalds 6731da177e4SLinus Torvalds /* 6741da177e4SLinus Torvalds * Redundant with logic already in kernel/branch.c, 6751da177e4SLinus Torvalds * embedded in compute_return_epc. At some point, 6761da177e4SLinus Torvalds * a single subroutine should be used across both 6771da177e4SLinus Torvalds * modules. 6781da177e4SLinus Torvalds */ 679102cedc3SLeonid Yegoshin static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, 680102cedc3SLeonid Yegoshin unsigned long *contpc) 6811da177e4SLinus Torvalds { 682102cedc3SLeonid Yegoshin union mips_instruction insn = (union mips_instruction)dec_insn.insn; 683102cedc3SLeonid Yegoshin unsigned int fcr31; 684102cedc3SLeonid Yegoshin unsigned int bit = 0; 685102cedc3SLeonid Yegoshin 686102cedc3SLeonid Yegoshin switch (insn.i_format.opcode) { 6871da177e4SLinus Torvalds case spec_op: 688102cedc3SLeonid Yegoshin switch (insn.r_format.func) { 6891da177e4SLinus Torvalds case jalr_op: 690102cedc3SLeonid Yegoshin regs->regs[insn.r_format.rd] = 691102cedc3SLeonid Yegoshin regs->cp0_epc + dec_insn.pc_inc + 692102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 693102cedc3SLeonid Yegoshin /* Fall through */ 6941da177e4SLinus Torvalds case jr_op: 695102cedc3SLeonid Yegoshin *contpc = regs->regs[insn.r_format.rs]; 6961da177e4SLinus Torvalds return 1; 697102cedc3SLeonid Yegoshin break; 6981da177e4SLinus Torvalds } 6991da177e4SLinus Torvalds break; 7001da177e4SLinus Torvalds case bcond_op: 701102cedc3SLeonid Yegoshin switch (insn.i_format.rt) { 7021da177e4SLinus Torvalds case bltzal_op: 7031da177e4SLinus Torvalds case bltzall_op: 704102cedc3SLeonid Yegoshin regs->regs[31] = regs->cp0_epc + 705102cedc3SLeonid Yegoshin dec_insn.pc_inc + 706102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 707102cedc3SLeonid Yegoshin /* Fall through */ 708102cedc3SLeonid Yegoshin case bltz_op: 709102cedc3SLeonid Yegoshin case bltzl_op: 710102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.i_format.rs] < 0) 711102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 712102cedc3SLeonid Yegoshin dec_insn.pc_inc + 713102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 714102cedc3SLeonid Yegoshin else 715102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 716102cedc3SLeonid Yegoshin dec_insn.pc_inc + 717102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 7181da177e4SLinus Torvalds return 1; 719102cedc3SLeonid Yegoshin break; 720102cedc3SLeonid Yegoshin case bgezal_op: 721102cedc3SLeonid Yegoshin case bgezall_op: 722102cedc3SLeonid Yegoshin regs->regs[31] = regs->cp0_epc + 723102cedc3SLeonid Yegoshin dec_insn.pc_inc + 724102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 725102cedc3SLeonid Yegoshin /* Fall through */ 726102cedc3SLeonid Yegoshin case bgez_op: 727102cedc3SLeonid Yegoshin case bgezl_op: 728102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.i_format.rs] >= 0) 729102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 730102cedc3SLeonid Yegoshin dec_insn.pc_inc + 731102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 732102cedc3SLeonid Yegoshin else 733102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 734102cedc3SLeonid Yegoshin dec_insn.pc_inc + 735102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 736102cedc3SLeonid Yegoshin return 1; 737102cedc3SLeonid Yegoshin break; 7381da177e4SLinus Torvalds } 7391da177e4SLinus Torvalds break; 7401da177e4SLinus Torvalds case jalx_op: 741102cedc3SLeonid Yegoshin set_isa16_mode(bit); 742102cedc3SLeonid Yegoshin case jal_op: 743102cedc3SLeonid Yegoshin regs->regs[31] = regs->cp0_epc + 744102cedc3SLeonid Yegoshin dec_insn.pc_inc + 745102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 746102cedc3SLeonid Yegoshin /* Fall through */ 747102cedc3SLeonid Yegoshin case j_op: 748102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + dec_insn.pc_inc; 749102cedc3SLeonid Yegoshin *contpc >>= 28; 750102cedc3SLeonid Yegoshin *contpc <<= 28; 751102cedc3SLeonid Yegoshin *contpc |= (insn.j_format.target << 2); 752102cedc3SLeonid Yegoshin /* Set microMIPS mode bit: XOR for jalx. */ 753102cedc3SLeonid Yegoshin *contpc ^= bit; 7541da177e4SLinus Torvalds return 1; 755102cedc3SLeonid Yegoshin break; 756102cedc3SLeonid Yegoshin case beq_op: 757102cedc3SLeonid Yegoshin case beql_op: 758102cedc3SLeonid Yegoshin if (regs->regs[insn.i_format.rs] == 759102cedc3SLeonid Yegoshin regs->regs[insn.i_format.rt]) 760102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 761102cedc3SLeonid Yegoshin dec_insn.pc_inc + 762102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 763102cedc3SLeonid Yegoshin else 764102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 765102cedc3SLeonid Yegoshin dec_insn.pc_inc + 766102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 767102cedc3SLeonid Yegoshin return 1; 768102cedc3SLeonid Yegoshin break; 769102cedc3SLeonid Yegoshin case bne_op: 770102cedc3SLeonid Yegoshin case bnel_op: 771102cedc3SLeonid Yegoshin if (regs->regs[insn.i_format.rs] != 772102cedc3SLeonid Yegoshin regs->regs[insn.i_format.rt]) 773102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 774102cedc3SLeonid Yegoshin dec_insn.pc_inc + 775102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 776102cedc3SLeonid Yegoshin else 777102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 778102cedc3SLeonid Yegoshin dec_insn.pc_inc + 779102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 780102cedc3SLeonid Yegoshin return 1; 781102cedc3SLeonid Yegoshin break; 782102cedc3SLeonid Yegoshin case blez_op: 783102cedc3SLeonid Yegoshin case blezl_op: 784102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.i_format.rs] <= 0) 785102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 786102cedc3SLeonid Yegoshin dec_insn.pc_inc + 787102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 788102cedc3SLeonid Yegoshin else 789102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 790102cedc3SLeonid Yegoshin dec_insn.pc_inc + 791102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 792102cedc3SLeonid Yegoshin return 1; 793102cedc3SLeonid Yegoshin break; 794102cedc3SLeonid Yegoshin case bgtz_op: 795102cedc3SLeonid Yegoshin case bgtzl_op: 796102cedc3SLeonid Yegoshin if ((long)regs->regs[insn.i_format.rs] > 0) 797102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 798102cedc3SLeonid Yegoshin dec_insn.pc_inc + 799102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 800102cedc3SLeonid Yegoshin else 801102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 802102cedc3SLeonid Yegoshin dec_insn.pc_inc + 803102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 804102cedc3SLeonid Yegoshin return 1; 805102cedc3SLeonid Yegoshin break; 8061da177e4SLinus Torvalds case cop0_op: 8071da177e4SLinus Torvalds case cop1_op: 8081da177e4SLinus Torvalds case cop2_op: 8091da177e4SLinus Torvalds case cop1x_op: 810102cedc3SLeonid Yegoshin if (insn.i_format.rs == bc_op) { 811102cedc3SLeonid Yegoshin preempt_disable(); 812102cedc3SLeonid Yegoshin if (is_fpu_owner()) 813102cedc3SLeonid Yegoshin asm volatile("cfc1\t%0,$31" : "=r" (fcr31)); 814102cedc3SLeonid Yegoshin else 815102cedc3SLeonid Yegoshin fcr31 = current->thread.fpu.fcr31; 816102cedc3SLeonid Yegoshin preempt_enable(); 817102cedc3SLeonid Yegoshin 818102cedc3SLeonid Yegoshin bit = (insn.i_format.rt >> 2); 819102cedc3SLeonid Yegoshin bit += (bit != 0); 820102cedc3SLeonid Yegoshin bit += 23; 821102cedc3SLeonid Yegoshin switch (insn.i_format.rt & 3) { 822102cedc3SLeonid Yegoshin case 0: /* bc1f */ 823102cedc3SLeonid Yegoshin case 2: /* bc1fl */ 824102cedc3SLeonid Yegoshin if (~fcr31 & (1 << bit)) 825102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 826102cedc3SLeonid Yegoshin dec_insn.pc_inc + 827102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 828102cedc3SLeonid Yegoshin else 829102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 830102cedc3SLeonid Yegoshin dec_insn.pc_inc + 831102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 832102cedc3SLeonid Yegoshin return 1; 833102cedc3SLeonid Yegoshin break; 834102cedc3SLeonid Yegoshin case 1: /* bc1t */ 835102cedc3SLeonid Yegoshin case 3: /* bc1tl */ 836102cedc3SLeonid Yegoshin if (fcr31 & (1 << bit)) 837102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 838102cedc3SLeonid Yegoshin dec_insn.pc_inc + 839102cedc3SLeonid Yegoshin (insn.i_format.simmediate << 2); 840102cedc3SLeonid Yegoshin else 841102cedc3SLeonid Yegoshin *contpc = regs->cp0_epc + 842102cedc3SLeonid Yegoshin dec_insn.pc_inc + 843102cedc3SLeonid Yegoshin dec_insn.next_pc_inc; 8441da177e4SLinus Torvalds return 1; 8451da177e4SLinus Torvalds break; 8461da177e4SLinus Torvalds } 847102cedc3SLeonid Yegoshin } 848102cedc3SLeonid Yegoshin break; 849102cedc3SLeonid Yegoshin } 8501da177e4SLinus Torvalds return 0; 8511da177e4SLinus Torvalds } 8521da177e4SLinus Torvalds 8531da177e4SLinus Torvalds /* 8541da177e4SLinus Torvalds * In the Linux kernel, we support selection of FPR format on the 855da0bac33SDavid Daney * basis of the Status.FR bit. If an FPU is not present, the FR bit 856da0bac33SDavid Daney * is hardwired to zero, which would imply a 32-bit FPU even for 85751d943f0SRalf Baechle * 64-bit CPUs so we rather look at TIF_32BIT_REGS. 85851d943f0SRalf Baechle * FPU emu is slow and bulky and optimizing this function offers fairly 85951d943f0SRalf Baechle * sizeable benefits so we try to be clever and make this function return 86051d943f0SRalf Baechle * a constant whenever possible, that is on 64-bit kernels without O32 86151d943f0SRalf Baechle * compatibility enabled and on 32-bit kernels. 8621da177e4SLinus Torvalds */ 863da0bac33SDavid Daney static inline int cop1_64bit(struct pt_regs *xcp) 864da0bac33SDavid Daney { 86551d943f0SRalf Baechle #if defined(CONFIG_64BIT) && !defined(CONFIG_MIPS32_O32) 86651d943f0SRalf Baechle return 1; 86751d943f0SRalf Baechle #elif defined(CONFIG_64BIT) && defined(CONFIG_MIPS32_O32) 868da0bac33SDavid Daney return !test_thread_flag(TIF_32BIT_REGS); 8691da177e4SLinus Torvalds #else 870da0bac33SDavid Daney return 0; 8711da177e4SLinus Torvalds #endif 872da0bac33SDavid Daney } 8731da177e4SLinus Torvalds 874da0bac33SDavid Daney #define SIFROMREG(si, x) ((si) = cop1_64bit(xcp) || !(x & 1) ? \ 875da0bac33SDavid Daney (int)ctx->fpr[x] : (int)(ctx->fpr[x & ~1] >> 32)) 876da0bac33SDavid Daney 877da0bac33SDavid Daney #define SITOREG(si, x) (ctx->fpr[x & ~(cop1_64bit(xcp) == 0)] = \ 878da0bac33SDavid Daney cop1_64bit(xcp) || !(x & 1) ? \ 8791da177e4SLinus Torvalds ctx->fpr[x & ~1] >> 32 << 32 | (u32)(si) : \ 8801da177e4SLinus Torvalds ctx->fpr[x & ~1] << 32 >> 32 | (u64)(si) << 32) 8811da177e4SLinus Torvalds 882da0bac33SDavid Daney #define DIFROMREG(di, x) ((di) = ctx->fpr[x & ~(cop1_64bit(xcp) == 0)]) 883da0bac33SDavid Daney #define DITOREG(di, x) (ctx->fpr[x & ~(cop1_64bit(xcp) == 0)] = (di)) 8841da177e4SLinus Torvalds 8851da177e4SLinus Torvalds #define SPFROMREG(sp, x) SIFROMREG((sp).bits, x) 8861da177e4SLinus Torvalds #define SPTOREG(sp, x) SITOREG((sp).bits, x) 8871da177e4SLinus Torvalds #define DPFROMREG(dp, x) DIFROMREG((dp).bits, x) 8881da177e4SLinus Torvalds #define DPTOREG(dp, x) DITOREG((dp).bits, x) 8891da177e4SLinus Torvalds 8901da177e4SLinus Torvalds /* 8911da177e4SLinus Torvalds * Emulate the single floating point instruction pointed at by EPC. 8921da177e4SLinus Torvalds * Two instructions if the instruction is in a branch delay slot. 8931da177e4SLinus Torvalds */ 8941da177e4SLinus Torvalds 895515b029dSDavid Daney static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 896102cedc3SLeonid Yegoshin struct mm_decoded_insn dec_insn, void *__user *fault_addr) 8971da177e4SLinus Torvalds { 8981da177e4SLinus Torvalds mips_instruction ir; 899102cedc3SLeonid Yegoshin unsigned long contpc = xcp->cp0_epc + dec_insn.pc_inc; 9001da177e4SLinus Torvalds unsigned int cond; 901102cedc3SLeonid Yegoshin int pc_inc; 9021da177e4SLinus Torvalds 9031da177e4SLinus Torvalds /* XXX NEC Vr54xx bug workaround */ 904102cedc3SLeonid Yegoshin if (xcp->cp0_cause & CAUSEF_BD) { 905102cedc3SLeonid Yegoshin if (dec_insn.micro_mips_mode) { 906102cedc3SLeonid Yegoshin if (!mm_isBranchInstr(xcp, dec_insn, &contpc)) 9071da177e4SLinus Torvalds xcp->cp0_cause &= ~CAUSEF_BD; 908102cedc3SLeonid Yegoshin } else { 909102cedc3SLeonid Yegoshin if (!isBranchInstr(xcp, dec_insn, &contpc)) 910102cedc3SLeonid Yegoshin xcp->cp0_cause &= ~CAUSEF_BD; 911102cedc3SLeonid Yegoshin } 912102cedc3SLeonid Yegoshin } 9131da177e4SLinus Torvalds 9141da177e4SLinus Torvalds if (xcp->cp0_cause & CAUSEF_BD) { 9151da177e4SLinus Torvalds /* 9161da177e4SLinus Torvalds * The instruction to be emulated is in a branch delay slot 9171da177e4SLinus Torvalds * which means that we have to emulate the branch instruction 9181da177e4SLinus Torvalds * BEFORE we do the cop1 instruction. 9191da177e4SLinus Torvalds * 9201da177e4SLinus Torvalds * This branch could be a COP1 branch, but in that case we 9211da177e4SLinus Torvalds * would have had a trap for that instruction, and would not 9221da177e4SLinus Torvalds * come through this route. 9231da177e4SLinus Torvalds * 9241da177e4SLinus Torvalds * Linux MIPS branch emulator operates on context, updating the 9251da177e4SLinus Torvalds * cp0_epc. 9261da177e4SLinus Torvalds */ 927102cedc3SLeonid Yegoshin ir = dec_insn.next_insn; /* process delay slot instr */ 928102cedc3SLeonid Yegoshin pc_inc = dec_insn.next_pc_inc; 929333d1f67SRalf Baechle } else { 930102cedc3SLeonid Yegoshin ir = dec_insn.insn; /* process current instr */ 931102cedc3SLeonid Yegoshin pc_inc = dec_insn.pc_inc; 932102cedc3SLeonid Yegoshin } 933102cedc3SLeonid Yegoshin 934102cedc3SLeonid Yegoshin /* 935102cedc3SLeonid Yegoshin * Since microMIPS FPU instructios are a subset of MIPS32 FPU 936102cedc3SLeonid Yegoshin * instructions, we want to convert microMIPS FPU instructions 937102cedc3SLeonid Yegoshin * into MIPS32 instructions so that we could reuse all of the 938102cedc3SLeonid Yegoshin * FPU emulation code. 939102cedc3SLeonid Yegoshin * 940102cedc3SLeonid Yegoshin * NOTE: We cannot do this for branch instructions since they 941102cedc3SLeonid Yegoshin * are not a subset. Example: Cannot emulate a 16-bit 942102cedc3SLeonid Yegoshin * aligned target address with a MIPS32 instruction. 943102cedc3SLeonid Yegoshin */ 944102cedc3SLeonid Yegoshin if (dec_insn.micro_mips_mode) { 945102cedc3SLeonid Yegoshin /* 946102cedc3SLeonid Yegoshin * If next instruction is a 16-bit instruction, then it 947102cedc3SLeonid Yegoshin * it cannot be a FPU instruction. This could happen 948102cedc3SLeonid Yegoshin * since we can be called for non-FPU instructions. 949102cedc3SLeonid Yegoshin */ 950102cedc3SLeonid Yegoshin if ((pc_inc == 2) || 951102cedc3SLeonid Yegoshin (microMIPS32_to_MIPS32((union mips_instruction *)&ir) 952102cedc3SLeonid Yegoshin == SIGILL)) 953102cedc3SLeonid Yegoshin return SIGILL; 9541da177e4SLinus Torvalds } 9551da177e4SLinus Torvalds 9561da177e4SLinus Torvalds emul: 957a8b0ca17SPeter Zijlstra perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, xcp, 0); 958b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(emulated); 9591da177e4SLinus Torvalds switch (MIPSInst_OPCODE(ir)) { 9601da177e4SLinus Torvalds case ldc1_op:{ 9613fccc015SRalf Baechle u64 __user *va = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] + 9621da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 9631da177e4SLinus Torvalds u64 val; 9641da177e4SLinus Torvalds 965b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(loads); 966515b029dSDavid Daney 967515b029dSDavid Daney if (!access_ok(VERIFY_READ, va, sizeof(u64))) { 968b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 969515b029dSDavid Daney *fault_addr = va; 9701da177e4SLinus Torvalds return SIGBUS; 9711da177e4SLinus Torvalds } 972515b029dSDavid Daney if (__get_user(val, va)) { 973515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 974515b029dSDavid Daney *fault_addr = va; 975515b029dSDavid Daney return SIGSEGV; 976515b029dSDavid Daney } 9771da177e4SLinus Torvalds DITOREG(val, MIPSInst_RT(ir)); 9781da177e4SLinus Torvalds break; 9791da177e4SLinus Torvalds } 9801da177e4SLinus Torvalds 9811da177e4SLinus Torvalds case sdc1_op:{ 9823fccc015SRalf Baechle u64 __user *va = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] + 9831da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 9841da177e4SLinus Torvalds u64 val; 9851da177e4SLinus Torvalds 986b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(stores); 9871da177e4SLinus Torvalds DIFROMREG(val, MIPSInst_RT(ir)); 988515b029dSDavid Daney if (!access_ok(VERIFY_WRITE, va, sizeof(u64))) { 989b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 990515b029dSDavid Daney *fault_addr = va; 9911da177e4SLinus Torvalds return SIGBUS; 9921da177e4SLinus Torvalds } 993515b029dSDavid Daney if (__put_user(val, va)) { 994515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 995515b029dSDavid Daney *fault_addr = va; 996515b029dSDavid Daney return SIGSEGV; 997515b029dSDavid Daney } 9981da177e4SLinus Torvalds break; 9991da177e4SLinus Torvalds } 10001da177e4SLinus Torvalds 10011da177e4SLinus Torvalds case lwc1_op:{ 10023fccc015SRalf Baechle u32 __user *va = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] + 10031da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 10041da177e4SLinus Torvalds u32 val; 10051da177e4SLinus Torvalds 1006b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(loads); 1007515b029dSDavid Daney if (!access_ok(VERIFY_READ, va, sizeof(u32))) { 1008b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1009515b029dSDavid Daney *fault_addr = va; 10101da177e4SLinus Torvalds return SIGBUS; 10111da177e4SLinus Torvalds } 1012515b029dSDavid Daney if (__get_user(val, va)) { 1013515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1014515b029dSDavid Daney *fault_addr = va; 1015515b029dSDavid Daney return SIGSEGV; 1016515b029dSDavid Daney } 10171da177e4SLinus Torvalds SITOREG(val, MIPSInst_RT(ir)); 10181da177e4SLinus Torvalds break; 10191da177e4SLinus Torvalds } 10201da177e4SLinus Torvalds 10211da177e4SLinus Torvalds case swc1_op:{ 10223fccc015SRalf Baechle u32 __user *va = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] + 10231da177e4SLinus Torvalds MIPSInst_SIMM(ir)); 10241da177e4SLinus Torvalds u32 val; 10251da177e4SLinus Torvalds 1026b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(stores); 10271da177e4SLinus Torvalds SIFROMREG(val, MIPSInst_RT(ir)); 1028515b029dSDavid Daney if (!access_ok(VERIFY_WRITE, va, sizeof(u32))) { 1029b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1030515b029dSDavid Daney *fault_addr = va; 10311da177e4SLinus Torvalds return SIGBUS; 10321da177e4SLinus Torvalds } 1033515b029dSDavid Daney if (__put_user(val, va)) { 1034515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1035515b029dSDavid Daney *fault_addr = va; 1036515b029dSDavid Daney return SIGSEGV; 1037515b029dSDavid Daney } 10381da177e4SLinus Torvalds break; 10391da177e4SLinus Torvalds } 10401da177e4SLinus Torvalds 10411da177e4SLinus Torvalds case cop1_op: 10421da177e4SLinus Torvalds switch (MIPSInst_RS(ir)) { 10431da177e4SLinus Torvalds 10444b724efdSRalf Baechle #if defined(__mips64) 10451da177e4SLinus Torvalds case dmfc_op: 10461da177e4SLinus Torvalds /* copregister fs -> gpr[rt] */ 10471da177e4SLinus Torvalds if (MIPSInst_RT(ir) != 0) { 10481da177e4SLinus Torvalds DIFROMREG(xcp->regs[MIPSInst_RT(ir)], 10491da177e4SLinus Torvalds MIPSInst_RD(ir)); 10501da177e4SLinus Torvalds } 10511da177e4SLinus Torvalds break; 10521da177e4SLinus Torvalds 10531da177e4SLinus Torvalds case dmtc_op: 10541da177e4SLinus Torvalds /* copregister fs <- rt */ 10551da177e4SLinus Torvalds DITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); 10561da177e4SLinus Torvalds break; 10571da177e4SLinus Torvalds #endif 10581da177e4SLinus Torvalds 10591da177e4SLinus Torvalds case mfc_op: 10601da177e4SLinus Torvalds /* copregister rd -> gpr[rt] */ 10611da177e4SLinus Torvalds if (MIPSInst_RT(ir) != 0) { 10621da177e4SLinus Torvalds SIFROMREG(xcp->regs[MIPSInst_RT(ir)], 10631da177e4SLinus Torvalds MIPSInst_RD(ir)); 10641da177e4SLinus Torvalds } 10651da177e4SLinus Torvalds break; 10661da177e4SLinus Torvalds 10671da177e4SLinus Torvalds case mtc_op: 10681da177e4SLinus Torvalds /* copregister rd <- rt */ 10691da177e4SLinus Torvalds SITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); 10701da177e4SLinus Torvalds break; 10711da177e4SLinus Torvalds 10721da177e4SLinus Torvalds case cfc_op:{ 10731da177e4SLinus Torvalds /* cop control register rd -> gpr[rt] */ 10741da177e4SLinus Torvalds u32 value; 10751da177e4SLinus Torvalds 10761da177e4SLinus Torvalds if (MIPSInst_RD(ir) == FPCREG_CSR) { 10771da177e4SLinus Torvalds value = ctx->fcr31; 10783f135530SShane McDonald value = (value & ~FPU_CSR_RM) | 10793f135530SShane McDonald mips_rm[modeindex(value)]; 10801da177e4SLinus Torvalds #ifdef CSRTRACE 10811da177e4SLinus Torvalds printk("%p gpr[%d]<-csr=%08x\n", 1082333d1f67SRalf Baechle (void *) (xcp->cp0_epc), 10831da177e4SLinus Torvalds MIPSInst_RT(ir), value); 10841da177e4SLinus Torvalds #endif 10851da177e4SLinus Torvalds } 10861da177e4SLinus Torvalds else if (MIPSInst_RD(ir) == FPCREG_RID) 10871da177e4SLinus Torvalds value = 0; 10881da177e4SLinus Torvalds else 10891da177e4SLinus Torvalds value = 0; 10901da177e4SLinus Torvalds if (MIPSInst_RT(ir)) 10911da177e4SLinus Torvalds xcp->regs[MIPSInst_RT(ir)] = value; 10921da177e4SLinus Torvalds break; 10931da177e4SLinus Torvalds } 10941da177e4SLinus Torvalds 10951da177e4SLinus Torvalds case ctc_op:{ 10961da177e4SLinus Torvalds /* copregister rd <- rt */ 10971da177e4SLinus Torvalds u32 value; 10981da177e4SLinus Torvalds 10991da177e4SLinus Torvalds if (MIPSInst_RT(ir) == 0) 11001da177e4SLinus Torvalds value = 0; 11011da177e4SLinus Torvalds else 11021da177e4SLinus Torvalds value = xcp->regs[MIPSInst_RT(ir)]; 11031da177e4SLinus Torvalds 11041da177e4SLinus Torvalds /* we only have one writable control reg 11051da177e4SLinus Torvalds */ 11061da177e4SLinus Torvalds if (MIPSInst_RD(ir) == FPCREG_CSR) { 11071da177e4SLinus Torvalds #ifdef CSRTRACE 11081da177e4SLinus Torvalds printk("%p gpr[%d]->csr=%08x\n", 1109333d1f67SRalf Baechle (void *) (xcp->cp0_epc), 11101da177e4SLinus Torvalds MIPSInst_RT(ir), value); 11111da177e4SLinus Torvalds #endif 111295e8f634SShane McDonald 111395e8f634SShane McDonald /* 111495e8f634SShane McDonald * Don't write reserved bits, 111595e8f634SShane McDonald * and convert to ieee library modes 111695e8f634SShane McDonald */ 111795e8f634SShane McDonald ctx->fcr31 = (value & 111895e8f634SShane McDonald ~(FPU_CSR_RSVD | FPU_CSR_RM)) | 111995e8f634SShane McDonald ieee_rm[modeindex(value)]; 11201da177e4SLinus Torvalds } 11211da177e4SLinus Torvalds if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 11221da177e4SLinus Torvalds return SIGFPE; 11231da177e4SLinus Torvalds } 11241da177e4SLinus Torvalds break; 11251da177e4SLinus Torvalds } 11261da177e4SLinus Torvalds 11271da177e4SLinus Torvalds case bc_op:{ 11281da177e4SLinus Torvalds int likely = 0; 11291da177e4SLinus Torvalds 11301da177e4SLinus Torvalds if (xcp->cp0_cause & CAUSEF_BD) 11311da177e4SLinus Torvalds return SIGILL; 11321da177e4SLinus Torvalds 11331da177e4SLinus Torvalds #if __mips >= 4 11341da177e4SLinus Torvalds cond = ctx->fcr31 & fpucondbit[MIPSInst_RT(ir) >> 2]; 11351da177e4SLinus Torvalds #else 11361da177e4SLinus Torvalds cond = ctx->fcr31 & FPU_CSR_COND; 11371da177e4SLinus Torvalds #endif 11381da177e4SLinus Torvalds switch (MIPSInst_RT(ir) & 3) { 11391da177e4SLinus Torvalds case bcfl_op: 11401da177e4SLinus Torvalds likely = 1; 11411da177e4SLinus Torvalds case bcf_op: 11421da177e4SLinus Torvalds cond = !cond; 11431da177e4SLinus Torvalds break; 11441da177e4SLinus Torvalds case bctl_op: 11451da177e4SLinus Torvalds likely = 1; 11461da177e4SLinus Torvalds case bct_op: 11471da177e4SLinus Torvalds break; 11481da177e4SLinus Torvalds default: 11491da177e4SLinus Torvalds /* thats an illegal instruction */ 11501da177e4SLinus Torvalds return SIGILL; 11511da177e4SLinus Torvalds } 11521da177e4SLinus Torvalds 11531da177e4SLinus Torvalds xcp->cp0_cause |= CAUSEF_BD; 11541da177e4SLinus Torvalds if (cond) { 11551da177e4SLinus Torvalds /* branch taken: emulate dslot 11561da177e4SLinus Torvalds * instruction 11571da177e4SLinus Torvalds */ 1158102cedc3SLeonid Yegoshin xcp->cp0_epc += dec_insn.pc_inc; 11591da177e4SLinus Torvalds 1160102cedc3SLeonid Yegoshin contpc = MIPSInst_SIMM(ir); 1161102cedc3SLeonid Yegoshin ir = dec_insn.next_insn; 1162102cedc3SLeonid Yegoshin if (dec_insn.micro_mips_mode) { 1163102cedc3SLeonid Yegoshin contpc = (xcp->cp0_epc + (contpc << 1)); 1164102cedc3SLeonid Yegoshin 1165102cedc3SLeonid Yegoshin /* If 16-bit instruction, not FPU. */ 1166102cedc3SLeonid Yegoshin if ((dec_insn.next_pc_inc == 2) || 1167102cedc3SLeonid Yegoshin (microMIPS32_to_MIPS32((union mips_instruction *)&ir) == SIGILL)) { 1168102cedc3SLeonid Yegoshin 1169102cedc3SLeonid Yegoshin /* 1170102cedc3SLeonid Yegoshin * Since this instruction will 1171102cedc3SLeonid Yegoshin * be put on the stack with 1172102cedc3SLeonid Yegoshin * 32-bit words, get around 1173102cedc3SLeonid Yegoshin * this problem by putting a 1174102cedc3SLeonid Yegoshin * NOP16 as the second one. 1175102cedc3SLeonid Yegoshin */ 1176102cedc3SLeonid Yegoshin if (dec_insn.next_pc_inc == 2) 1177102cedc3SLeonid Yegoshin ir = (ir & (~0xffff)) | MM_NOP16; 1178102cedc3SLeonid Yegoshin 1179102cedc3SLeonid Yegoshin /* 1180102cedc3SLeonid Yegoshin * Single step the non-CP1 1181102cedc3SLeonid Yegoshin * instruction in the dslot. 1182102cedc3SLeonid Yegoshin */ 1183102cedc3SLeonid Yegoshin return mips_dsemul(xcp, ir, contpc); 1184515b029dSDavid Daney } 1185102cedc3SLeonid Yegoshin } else 1186102cedc3SLeonid Yegoshin contpc = (xcp->cp0_epc + (contpc << 2)); 11871da177e4SLinus Torvalds 11881da177e4SLinus Torvalds switch (MIPSInst_OPCODE(ir)) { 11891da177e4SLinus Torvalds case lwc1_op: 11901da177e4SLinus Torvalds case swc1_op: 11914b724efdSRalf Baechle #if (__mips >= 2 || defined(__mips64)) 11921da177e4SLinus Torvalds case ldc1_op: 11931da177e4SLinus Torvalds case sdc1_op: 11941da177e4SLinus Torvalds #endif 11951da177e4SLinus Torvalds case cop1_op: 11961da177e4SLinus Torvalds #if __mips >= 4 && __mips != 32 11971da177e4SLinus Torvalds case cop1x_op: 11981da177e4SLinus Torvalds #endif 11991da177e4SLinus Torvalds /* its one of ours */ 12001da177e4SLinus Torvalds goto emul; 12011da177e4SLinus Torvalds #if __mips >= 4 12021da177e4SLinus Torvalds case spec_op: 12031da177e4SLinus Torvalds if (MIPSInst_FUNC(ir) == movc_op) 12041da177e4SLinus Torvalds goto emul; 12051da177e4SLinus Torvalds break; 12061da177e4SLinus Torvalds #endif 12071da177e4SLinus Torvalds } 12081da177e4SLinus Torvalds 12091da177e4SLinus Torvalds /* 12101da177e4SLinus Torvalds * Single step the non-cp1 12111da177e4SLinus Torvalds * instruction in the dslot 12121da177e4SLinus Torvalds */ 1213e70dfc10SAtsushi Nemoto return mips_dsemul(xcp, ir, contpc); 12141da177e4SLinus Torvalds } 12151da177e4SLinus Torvalds else { 12161da177e4SLinus Torvalds /* branch not taken */ 12171da177e4SLinus Torvalds if (likely) { 12181da177e4SLinus Torvalds /* 12191da177e4SLinus Torvalds * branch likely nullifies 12201da177e4SLinus Torvalds * dslot if not taken 12211da177e4SLinus Torvalds */ 1222102cedc3SLeonid Yegoshin xcp->cp0_epc += dec_insn.pc_inc; 1223102cedc3SLeonid Yegoshin contpc += dec_insn.pc_inc; 12241da177e4SLinus Torvalds /* 12251da177e4SLinus Torvalds * else continue & execute 12261da177e4SLinus Torvalds * dslot as normal insn 12271da177e4SLinus Torvalds */ 12281da177e4SLinus Torvalds } 12291da177e4SLinus Torvalds } 12301da177e4SLinus Torvalds break; 12311da177e4SLinus Torvalds } 12321da177e4SLinus Torvalds 12331da177e4SLinus Torvalds default: 12341da177e4SLinus Torvalds if (!(MIPSInst_RS(ir) & 0x10)) 12351da177e4SLinus Torvalds return SIGILL; 12361da177e4SLinus Torvalds { 12371da177e4SLinus Torvalds int sig; 12381da177e4SLinus Torvalds 12391da177e4SLinus Torvalds /* a real fpu computation instruction */ 12401da177e4SLinus Torvalds if ((sig = fpu_emu(xcp, ctx, ir))) 12411da177e4SLinus Torvalds return sig; 12421da177e4SLinus Torvalds } 12431da177e4SLinus Torvalds } 12441da177e4SLinus Torvalds break; 12451da177e4SLinus Torvalds 12461da177e4SLinus Torvalds #if __mips >= 4 && __mips != 32 12471da177e4SLinus Torvalds case cop1x_op:{ 1248515b029dSDavid Daney int sig = fpux_emu(xcp, ctx, ir, fault_addr); 1249515b029dSDavid Daney if (sig) 12501da177e4SLinus Torvalds return sig; 12511da177e4SLinus Torvalds break; 12521da177e4SLinus Torvalds } 12531da177e4SLinus Torvalds #endif 12541da177e4SLinus Torvalds 12551da177e4SLinus Torvalds #if __mips >= 4 12561da177e4SLinus Torvalds case spec_op: 12571da177e4SLinus Torvalds if (MIPSInst_FUNC(ir) != movc_op) 12581da177e4SLinus Torvalds return SIGILL; 12591da177e4SLinus Torvalds cond = fpucondbit[MIPSInst_RT(ir) >> 2]; 12601da177e4SLinus Torvalds if (((ctx->fcr31 & cond) != 0) == ((MIPSInst_RT(ir) & 1) != 0)) 12611da177e4SLinus Torvalds xcp->regs[MIPSInst_RD(ir)] = 12621da177e4SLinus Torvalds xcp->regs[MIPSInst_RS(ir)]; 12631da177e4SLinus Torvalds break; 12641da177e4SLinus Torvalds #endif 12651da177e4SLinus Torvalds 12661da177e4SLinus Torvalds default: 12671da177e4SLinus Torvalds return SIGILL; 12681da177e4SLinus Torvalds } 12691da177e4SLinus Torvalds 12701da177e4SLinus Torvalds /* we did it !! */ 1271e70dfc10SAtsushi Nemoto xcp->cp0_epc = contpc; 12721da177e4SLinus Torvalds xcp->cp0_cause &= ~CAUSEF_BD; 1273333d1f67SRalf Baechle 12741da177e4SLinus Torvalds return 0; 12751da177e4SLinus Torvalds } 12761da177e4SLinus Torvalds 12771da177e4SLinus Torvalds /* 12781da177e4SLinus Torvalds * Conversion table from MIPS compare ops 48-63 12791da177e4SLinus Torvalds * cond = ieee754dp_cmp(x,y,IEEE754_UN,sig); 12801da177e4SLinus Torvalds */ 12811da177e4SLinus Torvalds static const unsigned char cmptab[8] = { 12821da177e4SLinus Torvalds 0, /* cmp_0 (sig) cmp_sf */ 12831da177e4SLinus Torvalds IEEE754_CUN, /* cmp_un (sig) cmp_ngle */ 12841da177e4SLinus Torvalds IEEE754_CEQ, /* cmp_eq (sig) cmp_seq */ 12851da177e4SLinus Torvalds IEEE754_CEQ | IEEE754_CUN, /* cmp_ueq (sig) cmp_ngl */ 12861da177e4SLinus Torvalds IEEE754_CLT, /* cmp_olt (sig) cmp_lt */ 12871da177e4SLinus Torvalds IEEE754_CLT | IEEE754_CUN, /* cmp_ult (sig) cmp_nge */ 12881da177e4SLinus Torvalds IEEE754_CLT | IEEE754_CEQ, /* cmp_ole (sig) cmp_le */ 12891da177e4SLinus Torvalds IEEE754_CLT | IEEE754_CEQ | IEEE754_CUN, /* cmp_ule (sig) cmp_ngt */ 12901da177e4SLinus Torvalds }; 12911da177e4SLinus Torvalds 12921da177e4SLinus Torvalds 12931da177e4SLinus Torvalds #if __mips >= 4 && __mips != 32 12941da177e4SLinus Torvalds 12951da177e4SLinus Torvalds /* 12961da177e4SLinus Torvalds * Additional MIPS4 instructions 12971da177e4SLinus Torvalds */ 12981da177e4SLinus Torvalds 12991da177e4SLinus Torvalds #define DEF3OP(name, p, f1, f2, f3) \ 13001da177e4SLinus Torvalds static ieee754##p fpemu_##p##_##name(ieee754##p r, ieee754##p s, \ 13011da177e4SLinus Torvalds ieee754##p t) \ 13021da177e4SLinus Torvalds { \ 1303cd21dfcfSRalf Baechle struct _ieee754_csr ieee754_csr_save; \ 13041da177e4SLinus Torvalds s = f1(s, t); \ 13051da177e4SLinus Torvalds ieee754_csr_save = ieee754_csr; \ 13061da177e4SLinus Torvalds s = f2(s, r); \ 13071da177e4SLinus Torvalds ieee754_csr_save.cx |= ieee754_csr.cx; \ 13081da177e4SLinus Torvalds ieee754_csr_save.sx |= ieee754_csr.sx; \ 13091da177e4SLinus Torvalds s = f3(s); \ 13101da177e4SLinus Torvalds ieee754_csr.cx |= ieee754_csr_save.cx; \ 13111da177e4SLinus Torvalds ieee754_csr.sx |= ieee754_csr_save.sx; \ 13121da177e4SLinus Torvalds return s; \ 13131da177e4SLinus Torvalds } 13141da177e4SLinus Torvalds 13151da177e4SLinus Torvalds static ieee754dp fpemu_dp_recip(ieee754dp d) 13161da177e4SLinus Torvalds { 13171da177e4SLinus Torvalds return ieee754dp_div(ieee754dp_one(0), d); 13181da177e4SLinus Torvalds } 13191da177e4SLinus Torvalds 13201da177e4SLinus Torvalds static ieee754dp fpemu_dp_rsqrt(ieee754dp d) 13211da177e4SLinus Torvalds { 13221da177e4SLinus Torvalds return ieee754dp_div(ieee754dp_one(0), ieee754dp_sqrt(d)); 13231da177e4SLinus Torvalds } 13241da177e4SLinus Torvalds 13251da177e4SLinus Torvalds static ieee754sp fpemu_sp_recip(ieee754sp s) 13261da177e4SLinus Torvalds { 13271da177e4SLinus Torvalds return ieee754sp_div(ieee754sp_one(0), s); 13281da177e4SLinus Torvalds } 13291da177e4SLinus Torvalds 13301da177e4SLinus Torvalds static ieee754sp fpemu_sp_rsqrt(ieee754sp s) 13311da177e4SLinus Torvalds { 13321da177e4SLinus Torvalds return ieee754sp_div(ieee754sp_one(0), ieee754sp_sqrt(s)); 13331da177e4SLinus Torvalds } 13341da177e4SLinus Torvalds 13351da177e4SLinus Torvalds DEF3OP(madd, sp, ieee754sp_mul, ieee754sp_add, ); 13361da177e4SLinus Torvalds DEF3OP(msub, sp, ieee754sp_mul, ieee754sp_sub, ); 13371da177e4SLinus Torvalds DEF3OP(nmadd, sp, ieee754sp_mul, ieee754sp_add, ieee754sp_neg); 13381da177e4SLinus Torvalds DEF3OP(nmsub, sp, ieee754sp_mul, ieee754sp_sub, ieee754sp_neg); 13391da177e4SLinus Torvalds DEF3OP(madd, dp, ieee754dp_mul, ieee754dp_add, ); 13401da177e4SLinus Torvalds DEF3OP(msub, dp, ieee754dp_mul, ieee754dp_sub, ); 13411da177e4SLinus Torvalds DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg); 13421da177e4SLinus Torvalds DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg); 13431da177e4SLinus Torvalds 1344eae89076SAtsushi Nemoto static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 1345515b029dSDavid Daney mips_instruction ir, void *__user *fault_addr) 13461da177e4SLinus Torvalds { 13471da177e4SLinus Torvalds unsigned rcsr = 0; /* resulting csr */ 13481da177e4SLinus Torvalds 1349b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(cp1xops); 13501da177e4SLinus Torvalds 13511da177e4SLinus Torvalds switch (MIPSInst_FMA_FFMT(ir)) { 13521da177e4SLinus Torvalds case s_fmt:{ /* 0 */ 13531da177e4SLinus Torvalds 13541da177e4SLinus Torvalds ieee754sp(*handler) (ieee754sp, ieee754sp, ieee754sp); 13551da177e4SLinus Torvalds ieee754sp fd, fr, fs, ft; 13563fccc015SRalf Baechle u32 __user *va; 13571da177e4SLinus Torvalds u32 val; 13581da177e4SLinus Torvalds 13591da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 13601da177e4SLinus Torvalds case lwxc1_op: 13613fccc015SRalf Baechle va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 13621da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 13631da177e4SLinus Torvalds 1364b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(loads); 1365515b029dSDavid Daney if (!access_ok(VERIFY_READ, va, sizeof(u32))) { 1366b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1367515b029dSDavid Daney *fault_addr = va; 13681da177e4SLinus Torvalds return SIGBUS; 13691da177e4SLinus Torvalds } 1370515b029dSDavid Daney if (__get_user(val, va)) { 1371515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1372515b029dSDavid Daney *fault_addr = va; 1373515b029dSDavid Daney return SIGSEGV; 1374515b029dSDavid Daney } 13751da177e4SLinus Torvalds SITOREG(val, MIPSInst_FD(ir)); 13761da177e4SLinus Torvalds break; 13771da177e4SLinus Torvalds 13781da177e4SLinus Torvalds case swxc1_op: 13793fccc015SRalf Baechle va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 13801da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 13811da177e4SLinus Torvalds 1382b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(stores); 13831da177e4SLinus Torvalds 13841da177e4SLinus Torvalds SIFROMREG(val, MIPSInst_FS(ir)); 1385515b029dSDavid Daney if (!access_ok(VERIFY_WRITE, va, sizeof(u32))) { 1386515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1387515b029dSDavid Daney *fault_addr = va; 1388515b029dSDavid Daney return SIGBUS; 1389515b029dSDavid Daney } 13901da177e4SLinus Torvalds if (put_user(val, va)) { 1391b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1392515b029dSDavid Daney *fault_addr = va; 1393515b029dSDavid Daney return SIGSEGV; 13941da177e4SLinus Torvalds } 13951da177e4SLinus Torvalds break; 13961da177e4SLinus Torvalds 13971da177e4SLinus Torvalds case madd_s_op: 13981da177e4SLinus Torvalds handler = fpemu_sp_madd; 13991da177e4SLinus Torvalds goto scoptop; 14001da177e4SLinus Torvalds case msub_s_op: 14011da177e4SLinus Torvalds handler = fpemu_sp_msub; 14021da177e4SLinus Torvalds goto scoptop; 14031da177e4SLinus Torvalds case nmadd_s_op: 14041da177e4SLinus Torvalds handler = fpemu_sp_nmadd; 14051da177e4SLinus Torvalds goto scoptop; 14061da177e4SLinus Torvalds case nmsub_s_op: 14071da177e4SLinus Torvalds handler = fpemu_sp_nmsub; 14081da177e4SLinus Torvalds goto scoptop; 14091da177e4SLinus Torvalds 14101da177e4SLinus Torvalds scoptop: 14111da177e4SLinus Torvalds SPFROMREG(fr, MIPSInst_FR(ir)); 14121da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 14131da177e4SLinus Torvalds SPFROMREG(ft, MIPSInst_FT(ir)); 14141da177e4SLinus Torvalds fd = (*handler) (fr, fs, ft); 14151da177e4SLinus Torvalds SPTOREG(fd, MIPSInst_FD(ir)); 14161da177e4SLinus Torvalds 14171da177e4SLinus Torvalds copcsr: 14181da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_INEXACT)) 14191da177e4SLinus Torvalds rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S; 14201da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_UNDERFLOW)) 14211da177e4SLinus Torvalds rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S; 14221da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_OVERFLOW)) 14231da177e4SLinus Torvalds rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S; 14241da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_INVALID_OPERATION)) 14251da177e4SLinus Torvalds rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S; 14261da177e4SLinus Torvalds 14271da177e4SLinus Torvalds ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr; 14281da177e4SLinus Torvalds if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 14291da177e4SLinus Torvalds /*printk ("SIGFPE: fpu csr = %08x\n", 14301da177e4SLinus Torvalds ctx->fcr31); */ 14311da177e4SLinus Torvalds return SIGFPE; 14321da177e4SLinus Torvalds } 14331da177e4SLinus Torvalds 14341da177e4SLinus Torvalds break; 14351da177e4SLinus Torvalds 14361da177e4SLinus Torvalds default: 14371da177e4SLinus Torvalds return SIGILL; 14381da177e4SLinus Torvalds } 14391da177e4SLinus Torvalds break; 14401da177e4SLinus Torvalds } 14411da177e4SLinus Torvalds 14421da177e4SLinus Torvalds case d_fmt:{ /* 1 */ 14431da177e4SLinus Torvalds ieee754dp(*handler) (ieee754dp, ieee754dp, ieee754dp); 14441da177e4SLinus Torvalds ieee754dp fd, fr, fs, ft; 14453fccc015SRalf Baechle u64 __user *va; 14461da177e4SLinus Torvalds u64 val; 14471da177e4SLinus Torvalds 14481da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 14491da177e4SLinus Torvalds case ldxc1_op: 14503fccc015SRalf Baechle va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 14511da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 14521da177e4SLinus Torvalds 1453b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(loads); 1454515b029dSDavid Daney if (!access_ok(VERIFY_READ, va, sizeof(u64))) { 1455b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1456515b029dSDavid Daney *fault_addr = va; 14571da177e4SLinus Torvalds return SIGBUS; 14581da177e4SLinus Torvalds } 1459515b029dSDavid Daney if (__get_user(val, va)) { 1460515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1461515b029dSDavid Daney *fault_addr = va; 1462515b029dSDavid Daney return SIGSEGV; 1463515b029dSDavid Daney } 14641da177e4SLinus Torvalds DITOREG(val, MIPSInst_FD(ir)); 14651da177e4SLinus Torvalds break; 14661da177e4SLinus Torvalds 14671da177e4SLinus Torvalds case sdxc1_op: 14683fccc015SRalf Baechle va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 14691da177e4SLinus Torvalds xcp->regs[MIPSInst_FT(ir)]); 14701da177e4SLinus Torvalds 1471b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(stores); 14721da177e4SLinus Torvalds DIFROMREG(val, MIPSInst_FS(ir)); 1473515b029dSDavid Daney if (!access_ok(VERIFY_WRITE, va, sizeof(u64))) { 1474b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1475515b029dSDavid Daney *fault_addr = va; 14761da177e4SLinus Torvalds return SIGBUS; 14771da177e4SLinus Torvalds } 1478515b029dSDavid Daney if (__put_user(val, va)) { 1479515b029dSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 1480515b029dSDavid Daney *fault_addr = va; 1481515b029dSDavid Daney return SIGSEGV; 1482515b029dSDavid Daney } 14831da177e4SLinus Torvalds break; 14841da177e4SLinus Torvalds 14851da177e4SLinus Torvalds case madd_d_op: 14861da177e4SLinus Torvalds handler = fpemu_dp_madd; 14871da177e4SLinus Torvalds goto dcoptop; 14881da177e4SLinus Torvalds case msub_d_op: 14891da177e4SLinus Torvalds handler = fpemu_dp_msub; 14901da177e4SLinus Torvalds goto dcoptop; 14911da177e4SLinus Torvalds case nmadd_d_op: 14921da177e4SLinus Torvalds handler = fpemu_dp_nmadd; 14931da177e4SLinus Torvalds goto dcoptop; 14941da177e4SLinus Torvalds case nmsub_d_op: 14951da177e4SLinus Torvalds handler = fpemu_dp_nmsub; 14961da177e4SLinus Torvalds goto dcoptop; 14971da177e4SLinus Torvalds 14981da177e4SLinus Torvalds dcoptop: 14991da177e4SLinus Torvalds DPFROMREG(fr, MIPSInst_FR(ir)); 15001da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 15011da177e4SLinus Torvalds DPFROMREG(ft, MIPSInst_FT(ir)); 15021da177e4SLinus Torvalds fd = (*handler) (fr, fs, ft); 15031da177e4SLinus Torvalds DPTOREG(fd, MIPSInst_FD(ir)); 15041da177e4SLinus Torvalds goto copcsr; 15051da177e4SLinus Torvalds 15061da177e4SLinus Torvalds default: 15071da177e4SLinus Torvalds return SIGILL; 15081da177e4SLinus Torvalds } 15091da177e4SLinus Torvalds break; 15101da177e4SLinus Torvalds } 15111da177e4SLinus Torvalds 15121da177e4SLinus Torvalds case 0x7: /* 7 */ 15131da177e4SLinus Torvalds if (MIPSInst_FUNC(ir) != pfetch_op) { 15141da177e4SLinus Torvalds return SIGILL; 15151da177e4SLinus Torvalds } 15161da177e4SLinus Torvalds /* ignore prefx operation */ 15171da177e4SLinus Torvalds break; 15181da177e4SLinus Torvalds 15191da177e4SLinus Torvalds default: 15201da177e4SLinus Torvalds return SIGILL; 15211da177e4SLinus Torvalds } 15221da177e4SLinus Torvalds 15231da177e4SLinus Torvalds return 0; 15241da177e4SLinus Torvalds } 15251da177e4SLinus Torvalds #endif 15261da177e4SLinus Torvalds 15271da177e4SLinus Torvalds 15281da177e4SLinus Torvalds 15291da177e4SLinus Torvalds /* 15301da177e4SLinus Torvalds * Emulate a single COP1 arithmetic instruction. 15311da177e4SLinus Torvalds */ 1532eae89076SAtsushi Nemoto static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 15331da177e4SLinus Torvalds mips_instruction ir) 15341da177e4SLinus Torvalds { 15351da177e4SLinus Torvalds int rfmt; /* resulting format */ 15361da177e4SLinus Torvalds unsigned rcsr = 0; /* resulting csr */ 15371da177e4SLinus Torvalds unsigned cond; 15381da177e4SLinus Torvalds union { 15391da177e4SLinus Torvalds ieee754dp d; 15401da177e4SLinus Torvalds ieee754sp s; 15411da177e4SLinus Torvalds int w; 1542766160c2SYoichi Yuasa #ifdef __mips64 15431da177e4SLinus Torvalds s64 l; 15441da177e4SLinus Torvalds #endif 15451da177e4SLinus Torvalds } rv; /* resulting value */ 15461da177e4SLinus Torvalds 1547b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(cp1ops); 15481da177e4SLinus Torvalds switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) { 15491da177e4SLinus Torvalds case s_fmt:{ /* 0 */ 15501da177e4SLinus Torvalds union { 15511da177e4SLinus Torvalds ieee754sp(*b) (ieee754sp, ieee754sp); 15521da177e4SLinus Torvalds ieee754sp(*u) (ieee754sp); 15531da177e4SLinus Torvalds } handler; 15541da177e4SLinus Torvalds 15551da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 15561da177e4SLinus Torvalds /* binary ops */ 15571da177e4SLinus Torvalds case fadd_op: 15581da177e4SLinus Torvalds handler.b = ieee754sp_add; 15591da177e4SLinus Torvalds goto scopbop; 15601da177e4SLinus Torvalds case fsub_op: 15611da177e4SLinus Torvalds handler.b = ieee754sp_sub; 15621da177e4SLinus Torvalds goto scopbop; 15631da177e4SLinus Torvalds case fmul_op: 15641da177e4SLinus Torvalds handler.b = ieee754sp_mul; 15651da177e4SLinus Torvalds goto scopbop; 15661da177e4SLinus Torvalds case fdiv_op: 15671da177e4SLinus Torvalds handler.b = ieee754sp_div; 15681da177e4SLinus Torvalds goto scopbop; 15691da177e4SLinus Torvalds 15701da177e4SLinus Torvalds /* unary ops */ 1571587cb98fSRalf Baechle #if __mips >= 2 || defined(__mips64) 15721da177e4SLinus Torvalds case fsqrt_op: 15731da177e4SLinus Torvalds handler.u = ieee754sp_sqrt; 15741da177e4SLinus Torvalds goto scopuop; 15751da177e4SLinus Torvalds #endif 15761da177e4SLinus Torvalds #if __mips >= 4 && __mips != 32 15771da177e4SLinus Torvalds case frsqrt_op: 15781da177e4SLinus Torvalds handler.u = fpemu_sp_rsqrt; 15791da177e4SLinus Torvalds goto scopuop; 15801da177e4SLinus Torvalds case frecip_op: 15811da177e4SLinus Torvalds handler.u = fpemu_sp_recip; 15821da177e4SLinus Torvalds goto scopuop; 15831da177e4SLinus Torvalds #endif 15841da177e4SLinus Torvalds #if __mips >= 4 15851da177e4SLinus Torvalds case fmovc_op: 15861da177e4SLinus Torvalds cond = fpucondbit[MIPSInst_FT(ir) >> 2]; 15871da177e4SLinus Torvalds if (((ctx->fcr31 & cond) != 0) != 15881da177e4SLinus Torvalds ((MIPSInst_FT(ir) & 1) != 0)) 15891da177e4SLinus Torvalds return 0; 15901da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 15911da177e4SLinus Torvalds break; 15921da177e4SLinus Torvalds case fmovz_op: 15931da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] != 0) 15941da177e4SLinus Torvalds return 0; 15951da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 15961da177e4SLinus Torvalds break; 15971da177e4SLinus Torvalds case fmovn_op: 15981da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] == 0) 15991da177e4SLinus Torvalds return 0; 16001da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 16011da177e4SLinus Torvalds break; 16021da177e4SLinus Torvalds #endif 16031da177e4SLinus Torvalds case fabs_op: 16041da177e4SLinus Torvalds handler.u = ieee754sp_abs; 16051da177e4SLinus Torvalds goto scopuop; 16061da177e4SLinus Torvalds case fneg_op: 16071da177e4SLinus Torvalds handler.u = ieee754sp_neg; 16081da177e4SLinus Torvalds goto scopuop; 16091da177e4SLinus Torvalds case fmov_op: 16101da177e4SLinus Torvalds /* an easy one */ 16111da177e4SLinus Torvalds SPFROMREG(rv.s, MIPSInst_FS(ir)); 16121da177e4SLinus Torvalds goto copcsr; 16131da177e4SLinus Torvalds 16141da177e4SLinus Torvalds /* binary op on handler */ 16151da177e4SLinus Torvalds scopbop: 16161da177e4SLinus Torvalds { 16171da177e4SLinus Torvalds ieee754sp fs, ft; 16181da177e4SLinus Torvalds 16191da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 16201da177e4SLinus Torvalds SPFROMREG(ft, MIPSInst_FT(ir)); 16211da177e4SLinus Torvalds 16221da177e4SLinus Torvalds rv.s = (*handler.b) (fs, ft); 16231da177e4SLinus Torvalds goto copcsr; 16241da177e4SLinus Torvalds } 16251da177e4SLinus Torvalds scopuop: 16261da177e4SLinus Torvalds { 16271da177e4SLinus Torvalds ieee754sp fs; 16281da177e4SLinus Torvalds 16291da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 16301da177e4SLinus Torvalds rv.s = (*handler.u) (fs); 16311da177e4SLinus Torvalds goto copcsr; 16321da177e4SLinus Torvalds } 16331da177e4SLinus Torvalds copcsr: 16341da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_INEXACT)) 16351da177e4SLinus Torvalds rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S; 16361da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_UNDERFLOW)) 16371da177e4SLinus Torvalds rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S; 16381da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_OVERFLOW)) 16391da177e4SLinus Torvalds rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S; 16401da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_ZERO_DIVIDE)) 16411da177e4SLinus Torvalds rcsr |= FPU_CSR_DIV_X | FPU_CSR_DIV_S; 16421da177e4SLinus Torvalds if (ieee754_cxtest(IEEE754_INVALID_OPERATION)) 16431da177e4SLinus Torvalds rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S; 16441da177e4SLinus Torvalds break; 16451da177e4SLinus Torvalds 16461da177e4SLinus Torvalds /* unary conv ops */ 16471da177e4SLinus Torvalds case fcvts_op: 16481da177e4SLinus Torvalds return SIGILL; /* not defined */ 16491da177e4SLinus Torvalds case fcvtd_op:{ 16501da177e4SLinus Torvalds ieee754sp fs; 16511da177e4SLinus Torvalds 16521da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 16531da177e4SLinus Torvalds rv.d = ieee754dp_fsp(fs); 16541da177e4SLinus Torvalds rfmt = d_fmt; 16551da177e4SLinus Torvalds goto copcsr; 16561da177e4SLinus Torvalds } 16571da177e4SLinus Torvalds case fcvtw_op:{ 16581da177e4SLinus Torvalds ieee754sp fs; 16591da177e4SLinus Torvalds 16601da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 16611da177e4SLinus Torvalds rv.w = ieee754sp_tint(fs); 16621da177e4SLinus Torvalds rfmt = w_fmt; 16631da177e4SLinus Torvalds goto copcsr; 16641da177e4SLinus Torvalds } 16651da177e4SLinus Torvalds 1666587cb98fSRalf Baechle #if __mips >= 2 || defined(__mips64) 16671da177e4SLinus Torvalds case fround_op: 16681da177e4SLinus Torvalds case ftrunc_op: 16691da177e4SLinus Torvalds case fceil_op: 16701da177e4SLinus Torvalds case ffloor_op:{ 16711da177e4SLinus Torvalds unsigned int oldrm = ieee754_csr.rm; 16721da177e4SLinus Torvalds ieee754sp fs; 16731da177e4SLinus Torvalds 16741da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 16753f135530SShane McDonald ieee754_csr.rm = ieee_rm[modeindex(MIPSInst_FUNC(ir))]; 16761da177e4SLinus Torvalds rv.w = ieee754sp_tint(fs); 16771da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 16781da177e4SLinus Torvalds rfmt = w_fmt; 16791da177e4SLinus Torvalds goto copcsr; 16801da177e4SLinus Torvalds } 16811da177e4SLinus Torvalds #endif /* __mips >= 2 */ 16821da177e4SLinus Torvalds 16834b724efdSRalf Baechle #if defined(__mips64) 16841da177e4SLinus Torvalds case fcvtl_op:{ 16851da177e4SLinus Torvalds ieee754sp fs; 16861da177e4SLinus Torvalds 16871da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 16881da177e4SLinus Torvalds rv.l = ieee754sp_tlong(fs); 16891da177e4SLinus Torvalds rfmt = l_fmt; 16901da177e4SLinus Torvalds goto copcsr; 16911da177e4SLinus Torvalds } 16921da177e4SLinus Torvalds 16931da177e4SLinus Torvalds case froundl_op: 16941da177e4SLinus Torvalds case ftruncl_op: 16951da177e4SLinus Torvalds case fceill_op: 16961da177e4SLinus Torvalds case ffloorl_op:{ 16971da177e4SLinus Torvalds unsigned int oldrm = ieee754_csr.rm; 16981da177e4SLinus Torvalds ieee754sp fs; 16991da177e4SLinus Torvalds 17001da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 17013f135530SShane McDonald ieee754_csr.rm = ieee_rm[modeindex(MIPSInst_FUNC(ir))]; 17021da177e4SLinus Torvalds rv.l = ieee754sp_tlong(fs); 17031da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 17041da177e4SLinus Torvalds rfmt = l_fmt; 17051da177e4SLinus Torvalds goto copcsr; 17061da177e4SLinus Torvalds } 17074b724efdSRalf Baechle #endif /* defined(__mips64) */ 17081da177e4SLinus Torvalds 17091da177e4SLinus Torvalds default: 17101da177e4SLinus Torvalds if (MIPSInst_FUNC(ir) >= fcmp_op) { 17111da177e4SLinus Torvalds unsigned cmpop = MIPSInst_FUNC(ir) - fcmp_op; 17121da177e4SLinus Torvalds ieee754sp fs, ft; 17131da177e4SLinus Torvalds 17141da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 17151da177e4SLinus Torvalds SPFROMREG(ft, MIPSInst_FT(ir)); 17161da177e4SLinus Torvalds rv.w = ieee754sp_cmp(fs, ft, 17171da177e4SLinus Torvalds cmptab[cmpop & 0x7], cmpop & 0x8); 17181da177e4SLinus Torvalds rfmt = -1; 17191da177e4SLinus Torvalds if ((cmpop & 0x8) && ieee754_cxtest 17201da177e4SLinus Torvalds (IEEE754_INVALID_OPERATION)) 17211da177e4SLinus Torvalds rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 17221da177e4SLinus Torvalds else 17231da177e4SLinus Torvalds goto copcsr; 17241da177e4SLinus Torvalds 17251da177e4SLinus Torvalds } 17261da177e4SLinus Torvalds else { 17271da177e4SLinus Torvalds return SIGILL; 17281da177e4SLinus Torvalds } 17291da177e4SLinus Torvalds break; 17301da177e4SLinus Torvalds } 17311da177e4SLinus Torvalds break; 17321da177e4SLinus Torvalds } 17331da177e4SLinus Torvalds 17341da177e4SLinus Torvalds case d_fmt:{ 17351da177e4SLinus Torvalds union { 17361da177e4SLinus Torvalds ieee754dp(*b) (ieee754dp, ieee754dp); 17371da177e4SLinus Torvalds ieee754dp(*u) (ieee754dp); 17381da177e4SLinus Torvalds } handler; 17391da177e4SLinus Torvalds 17401da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 17411da177e4SLinus Torvalds /* binary ops */ 17421da177e4SLinus Torvalds case fadd_op: 17431da177e4SLinus Torvalds handler.b = ieee754dp_add; 17441da177e4SLinus Torvalds goto dcopbop; 17451da177e4SLinus Torvalds case fsub_op: 17461da177e4SLinus Torvalds handler.b = ieee754dp_sub; 17471da177e4SLinus Torvalds goto dcopbop; 17481da177e4SLinus Torvalds case fmul_op: 17491da177e4SLinus Torvalds handler.b = ieee754dp_mul; 17501da177e4SLinus Torvalds goto dcopbop; 17511da177e4SLinus Torvalds case fdiv_op: 17521da177e4SLinus Torvalds handler.b = ieee754dp_div; 17531da177e4SLinus Torvalds goto dcopbop; 17541da177e4SLinus Torvalds 17551da177e4SLinus Torvalds /* unary ops */ 1756587cb98fSRalf Baechle #if __mips >= 2 || defined(__mips64) 17571da177e4SLinus Torvalds case fsqrt_op: 17581da177e4SLinus Torvalds handler.u = ieee754dp_sqrt; 17591da177e4SLinus Torvalds goto dcopuop; 17601da177e4SLinus Torvalds #endif 17611da177e4SLinus Torvalds #if __mips >= 4 && __mips != 32 17621da177e4SLinus Torvalds case frsqrt_op: 17631da177e4SLinus Torvalds handler.u = fpemu_dp_rsqrt; 17641da177e4SLinus Torvalds goto dcopuop; 17651da177e4SLinus Torvalds case frecip_op: 17661da177e4SLinus Torvalds handler.u = fpemu_dp_recip; 17671da177e4SLinus Torvalds goto dcopuop; 17681da177e4SLinus Torvalds #endif 17691da177e4SLinus Torvalds #if __mips >= 4 17701da177e4SLinus Torvalds case fmovc_op: 17711da177e4SLinus Torvalds cond = fpucondbit[MIPSInst_FT(ir) >> 2]; 17721da177e4SLinus Torvalds if (((ctx->fcr31 & cond) != 0) != 17731da177e4SLinus Torvalds ((MIPSInst_FT(ir) & 1) != 0)) 17741da177e4SLinus Torvalds return 0; 17751da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 17761da177e4SLinus Torvalds break; 17771da177e4SLinus Torvalds case fmovz_op: 17781da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] != 0) 17791da177e4SLinus Torvalds return 0; 17801da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 17811da177e4SLinus Torvalds break; 17821da177e4SLinus Torvalds case fmovn_op: 17831da177e4SLinus Torvalds if (xcp->regs[MIPSInst_FT(ir)] == 0) 17841da177e4SLinus Torvalds return 0; 17851da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 17861da177e4SLinus Torvalds break; 17871da177e4SLinus Torvalds #endif 17881da177e4SLinus Torvalds case fabs_op: 17891da177e4SLinus Torvalds handler.u = ieee754dp_abs; 17901da177e4SLinus Torvalds goto dcopuop; 17911da177e4SLinus Torvalds 17921da177e4SLinus Torvalds case fneg_op: 17931da177e4SLinus Torvalds handler.u = ieee754dp_neg; 17941da177e4SLinus Torvalds goto dcopuop; 17951da177e4SLinus Torvalds 17961da177e4SLinus Torvalds case fmov_op: 17971da177e4SLinus Torvalds /* an easy one */ 17981da177e4SLinus Torvalds DPFROMREG(rv.d, MIPSInst_FS(ir)); 17991da177e4SLinus Torvalds goto copcsr; 18001da177e4SLinus Torvalds 18011da177e4SLinus Torvalds /* binary op on handler */ 18021da177e4SLinus Torvalds dcopbop:{ 18031da177e4SLinus Torvalds ieee754dp fs, ft; 18041da177e4SLinus Torvalds 18051da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 18061da177e4SLinus Torvalds DPFROMREG(ft, MIPSInst_FT(ir)); 18071da177e4SLinus Torvalds 18081da177e4SLinus Torvalds rv.d = (*handler.b) (fs, ft); 18091da177e4SLinus Torvalds goto copcsr; 18101da177e4SLinus Torvalds } 18111da177e4SLinus Torvalds dcopuop:{ 18121da177e4SLinus Torvalds ieee754dp fs; 18131da177e4SLinus Torvalds 18141da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 18151da177e4SLinus Torvalds rv.d = (*handler.u) (fs); 18161da177e4SLinus Torvalds goto copcsr; 18171da177e4SLinus Torvalds } 18181da177e4SLinus Torvalds 18191da177e4SLinus Torvalds /* unary conv ops */ 18201da177e4SLinus Torvalds case fcvts_op:{ 18211da177e4SLinus Torvalds ieee754dp fs; 18221da177e4SLinus Torvalds 18231da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 18241da177e4SLinus Torvalds rv.s = ieee754sp_fdp(fs); 18251da177e4SLinus Torvalds rfmt = s_fmt; 18261da177e4SLinus Torvalds goto copcsr; 18271da177e4SLinus Torvalds } 18281da177e4SLinus Torvalds case fcvtd_op: 18291da177e4SLinus Torvalds return SIGILL; /* not defined */ 18301da177e4SLinus Torvalds 18311da177e4SLinus Torvalds case fcvtw_op:{ 18321da177e4SLinus Torvalds ieee754dp fs; 18331da177e4SLinus Torvalds 18341da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 18351da177e4SLinus Torvalds rv.w = ieee754dp_tint(fs); /* wrong */ 18361da177e4SLinus Torvalds rfmt = w_fmt; 18371da177e4SLinus Torvalds goto copcsr; 18381da177e4SLinus Torvalds } 18391da177e4SLinus Torvalds 1840587cb98fSRalf Baechle #if __mips >= 2 || defined(__mips64) 18411da177e4SLinus Torvalds case fround_op: 18421da177e4SLinus Torvalds case ftrunc_op: 18431da177e4SLinus Torvalds case fceil_op: 18441da177e4SLinus Torvalds case ffloor_op:{ 18451da177e4SLinus Torvalds unsigned int oldrm = ieee754_csr.rm; 18461da177e4SLinus Torvalds ieee754dp fs; 18471da177e4SLinus Torvalds 18481da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 18493f135530SShane McDonald ieee754_csr.rm = ieee_rm[modeindex(MIPSInst_FUNC(ir))]; 18501da177e4SLinus Torvalds rv.w = ieee754dp_tint(fs); 18511da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 18521da177e4SLinus Torvalds rfmt = w_fmt; 18531da177e4SLinus Torvalds goto copcsr; 18541da177e4SLinus Torvalds } 18551da177e4SLinus Torvalds #endif 18561da177e4SLinus Torvalds 18574b724efdSRalf Baechle #if defined(__mips64) 18581da177e4SLinus Torvalds case fcvtl_op:{ 18591da177e4SLinus Torvalds ieee754dp fs; 18601da177e4SLinus Torvalds 18611da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 18621da177e4SLinus Torvalds rv.l = ieee754dp_tlong(fs); 18631da177e4SLinus Torvalds rfmt = l_fmt; 18641da177e4SLinus Torvalds goto copcsr; 18651da177e4SLinus Torvalds } 18661da177e4SLinus Torvalds 18671da177e4SLinus Torvalds case froundl_op: 18681da177e4SLinus Torvalds case ftruncl_op: 18691da177e4SLinus Torvalds case fceill_op: 18701da177e4SLinus Torvalds case ffloorl_op:{ 18711da177e4SLinus Torvalds unsigned int oldrm = ieee754_csr.rm; 18721da177e4SLinus Torvalds ieee754dp fs; 18731da177e4SLinus Torvalds 18741da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 18753f135530SShane McDonald ieee754_csr.rm = ieee_rm[modeindex(MIPSInst_FUNC(ir))]; 18761da177e4SLinus Torvalds rv.l = ieee754dp_tlong(fs); 18771da177e4SLinus Torvalds ieee754_csr.rm = oldrm; 18781da177e4SLinus Torvalds rfmt = l_fmt; 18791da177e4SLinus Torvalds goto copcsr; 18801da177e4SLinus Torvalds } 18814b724efdSRalf Baechle #endif /* __mips >= 3 */ 18821da177e4SLinus Torvalds 18831da177e4SLinus Torvalds default: 18841da177e4SLinus Torvalds if (MIPSInst_FUNC(ir) >= fcmp_op) { 18851da177e4SLinus Torvalds unsigned cmpop = MIPSInst_FUNC(ir) - fcmp_op; 18861da177e4SLinus Torvalds ieee754dp fs, ft; 18871da177e4SLinus Torvalds 18881da177e4SLinus Torvalds DPFROMREG(fs, MIPSInst_FS(ir)); 18891da177e4SLinus Torvalds DPFROMREG(ft, MIPSInst_FT(ir)); 18901da177e4SLinus Torvalds rv.w = ieee754dp_cmp(fs, ft, 18911da177e4SLinus Torvalds cmptab[cmpop & 0x7], cmpop & 0x8); 18921da177e4SLinus Torvalds rfmt = -1; 18931da177e4SLinus Torvalds if ((cmpop & 0x8) 18941da177e4SLinus Torvalds && 18951da177e4SLinus Torvalds ieee754_cxtest 18961da177e4SLinus Torvalds (IEEE754_INVALID_OPERATION)) 18971da177e4SLinus Torvalds rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 18981da177e4SLinus Torvalds else 18991da177e4SLinus Torvalds goto copcsr; 19001da177e4SLinus Torvalds 19011da177e4SLinus Torvalds } 19021da177e4SLinus Torvalds else { 19031da177e4SLinus Torvalds return SIGILL; 19041da177e4SLinus Torvalds } 19051da177e4SLinus Torvalds break; 19061da177e4SLinus Torvalds } 19071da177e4SLinus Torvalds break; 19081da177e4SLinus Torvalds } 19091da177e4SLinus Torvalds 19101da177e4SLinus Torvalds case w_fmt:{ 19111da177e4SLinus Torvalds ieee754sp fs; 19121da177e4SLinus Torvalds 19131da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 19141da177e4SLinus Torvalds case fcvts_op: 19151da177e4SLinus Torvalds /* convert word to single precision real */ 19161da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 19171da177e4SLinus Torvalds rv.s = ieee754sp_fint(fs.bits); 19181da177e4SLinus Torvalds rfmt = s_fmt; 19191da177e4SLinus Torvalds goto copcsr; 19201da177e4SLinus Torvalds case fcvtd_op: 19211da177e4SLinus Torvalds /* convert word to double precision real */ 19221da177e4SLinus Torvalds SPFROMREG(fs, MIPSInst_FS(ir)); 19231da177e4SLinus Torvalds rv.d = ieee754dp_fint(fs.bits); 19241da177e4SLinus Torvalds rfmt = d_fmt; 19251da177e4SLinus Torvalds goto copcsr; 19261da177e4SLinus Torvalds default: 19271da177e4SLinus Torvalds return SIGILL; 19281da177e4SLinus Torvalds } 19291da177e4SLinus Torvalds break; 19301da177e4SLinus Torvalds } 19311da177e4SLinus Torvalds 19324b724efdSRalf Baechle #if defined(__mips64) 19331da177e4SLinus Torvalds case l_fmt:{ 19341da177e4SLinus Torvalds switch (MIPSInst_FUNC(ir)) { 19351da177e4SLinus Torvalds case fcvts_op: 19361da177e4SLinus Torvalds /* convert long to single precision real */ 19371da177e4SLinus Torvalds rv.s = ieee754sp_flong(ctx->fpr[MIPSInst_FS(ir)]); 19381da177e4SLinus Torvalds rfmt = s_fmt; 19391da177e4SLinus Torvalds goto copcsr; 19401da177e4SLinus Torvalds case fcvtd_op: 19411da177e4SLinus Torvalds /* convert long to double precision real */ 19421da177e4SLinus Torvalds rv.d = ieee754dp_flong(ctx->fpr[MIPSInst_FS(ir)]); 19431da177e4SLinus Torvalds rfmt = d_fmt; 19441da177e4SLinus Torvalds goto copcsr; 19451da177e4SLinus Torvalds default: 19461da177e4SLinus Torvalds return SIGILL; 19471da177e4SLinus Torvalds } 19481da177e4SLinus Torvalds break; 19491da177e4SLinus Torvalds } 19501da177e4SLinus Torvalds #endif 19511da177e4SLinus Torvalds 19521da177e4SLinus Torvalds default: 19531da177e4SLinus Torvalds return SIGILL; 19541da177e4SLinus Torvalds } 19551da177e4SLinus Torvalds 19561da177e4SLinus Torvalds /* 19571da177e4SLinus Torvalds * Update the fpu CSR register for this operation. 19581da177e4SLinus Torvalds * If an exception is required, generate a tidy SIGFPE exception, 19591da177e4SLinus Torvalds * without updating the result register. 19601da177e4SLinus Torvalds * Note: cause exception bits do not accumulate, they are rewritten 19611da177e4SLinus Torvalds * for each op; only the flag/sticky bits accumulate. 19621da177e4SLinus Torvalds */ 19631da177e4SLinus Torvalds ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr; 19641da177e4SLinus Torvalds if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 19651da177e4SLinus Torvalds /*printk ("SIGFPE: fpu csr = %08x\n",ctx->fcr31); */ 19661da177e4SLinus Torvalds return SIGFPE; 19671da177e4SLinus Torvalds } 19681da177e4SLinus Torvalds 19691da177e4SLinus Torvalds /* 19701da177e4SLinus Torvalds * Now we can safely write the result back to the register file. 19711da177e4SLinus Torvalds */ 19721da177e4SLinus Torvalds switch (rfmt) { 19731da177e4SLinus Torvalds case -1:{ 19741da177e4SLinus Torvalds #if __mips >= 4 19751da177e4SLinus Torvalds cond = fpucondbit[MIPSInst_FD(ir) >> 2]; 19761da177e4SLinus Torvalds #else 19771da177e4SLinus Torvalds cond = FPU_CSR_COND; 19781da177e4SLinus Torvalds #endif 19791da177e4SLinus Torvalds if (rv.w) 19801da177e4SLinus Torvalds ctx->fcr31 |= cond; 19811da177e4SLinus Torvalds else 19821da177e4SLinus Torvalds ctx->fcr31 &= ~cond; 19831da177e4SLinus Torvalds break; 19841da177e4SLinus Torvalds } 19851da177e4SLinus Torvalds case d_fmt: 19861da177e4SLinus Torvalds DPTOREG(rv.d, MIPSInst_FD(ir)); 19871da177e4SLinus Torvalds break; 19881da177e4SLinus Torvalds case s_fmt: 19891da177e4SLinus Torvalds SPTOREG(rv.s, MIPSInst_FD(ir)); 19901da177e4SLinus Torvalds break; 19911da177e4SLinus Torvalds case w_fmt: 19921da177e4SLinus Torvalds SITOREG(rv.w, MIPSInst_FD(ir)); 19931da177e4SLinus Torvalds break; 19944b724efdSRalf Baechle #if defined(__mips64) 19951da177e4SLinus Torvalds case l_fmt: 19961da177e4SLinus Torvalds DITOREG(rv.l, MIPSInst_FD(ir)); 19971da177e4SLinus Torvalds break; 19981da177e4SLinus Torvalds #endif 19991da177e4SLinus Torvalds default: 20001da177e4SLinus Torvalds return SIGILL; 20011da177e4SLinus Torvalds } 20021da177e4SLinus Torvalds 20031da177e4SLinus Torvalds return 0; 20041da177e4SLinus Torvalds } 20051da177e4SLinus Torvalds 2006e04582b7SAtsushi Nemoto int fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 2007515b029dSDavid Daney int has_fpu, void *__user *fault_addr) 20081da177e4SLinus Torvalds { 2009333d1f67SRalf Baechle unsigned long oldepc, prevepc; 2010102cedc3SLeonid Yegoshin struct mm_decoded_insn dec_insn; 2011102cedc3SLeonid Yegoshin u16 instr[4]; 2012102cedc3SLeonid Yegoshin u16 *instr_ptr; 20131da177e4SLinus Torvalds int sig = 0; 20141da177e4SLinus Torvalds 20151da177e4SLinus Torvalds oldepc = xcp->cp0_epc; 20161da177e4SLinus Torvalds do { 20171da177e4SLinus Torvalds prevepc = xcp->cp0_epc; 20181da177e4SLinus Torvalds 2019102cedc3SLeonid Yegoshin if (get_isa16_mode(prevepc) && cpu_has_mmips) { 2020102cedc3SLeonid Yegoshin /* 2021102cedc3SLeonid Yegoshin * Get next 2 microMIPS instructions and convert them 2022102cedc3SLeonid Yegoshin * into 32-bit instructions. 2023102cedc3SLeonid Yegoshin */ 2024102cedc3SLeonid Yegoshin if ((get_user(instr[0], (u16 __user *)msk_isa16_mode(xcp->cp0_epc))) || 2025102cedc3SLeonid Yegoshin (get_user(instr[1], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 2))) || 2026102cedc3SLeonid Yegoshin (get_user(instr[2], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 4))) || 2027102cedc3SLeonid Yegoshin (get_user(instr[3], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 6)))) { 2028b6ee75edSDavid Daney MIPS_FPU_EMU_INC_STATS(errors); 20291da177e4SLinus Torvalds return SIGBUS; 20301da177e4SLinus Torvalds } 2031102cedc3SLeonid Yegoshin instr_ptr = instr; 2032102cedc3SLeonid Yegoshin 2033102cedc3SLeonid Yegoshin /* Get first instruction. */ 2034102cedc3SLeonid Yegoshin if (mm_insn_16bit(*instr_ptr)) { 2035102cedc3SLeonid Yegoshin /* Duplicate the half-word. */ 2036102cedc3SLeonid Yegoshin dec_insn.insn = (*instr_ptr << 16) | 2037102cedc3SLeonid Yegoshin (*instr_ptr); 2038102cedc3SLeonid Yegoshin /* 16-bit instruction. */ 2039102cedc3SLeonid Yegoshin dec_insn.pc_inc = 2; 2040102cedc3SLeonid Yegoshin instr_ptr += 1; 2041102cedc3SLeonid Yegoshin } else { 2042102cedc3SLeonid Yegoshin dec_insn.insn = (*instr_ptr << 16) | 2043102cedc3SLeonid Yegoshin *(instr_ptr+1); 2044102cedc3SLeonid Yegoshin /* 32-bit instruction. */ 2045102cedc3SLeonid Yegoshin dec_insn.pc_inc = 4; 2046102cedc3SLeonid Yegoshin instr_ptr += 2; 2047515b029dSDavid Daney } 2048102cedc3SLeonid Yegoshin /* Get second instruction. */ 2049102cedc3SLeonid Yegoshin if (mm_insn_16bit(*instr_ptr)) { 2050102cedc3SLeonid Yegoshin /* Duplicate the half-word. */ 2051102cedc3SLeonid Yegoshin dec_insn.next_insn = (*instr_ptr << 16) | 2052102cedc3SLeonid Yegoshin (*instr_ptr); 2053102cedc3SLeonid Yegoshin /* 16-bit instruction. */ 2054102cedc3SLeonid Yegoshin dec_insn.next_pc_inc = 2; 2055102cedc3SLeonid Yegoshin } else { 2056102cedc3SLeonid Yegoshin dec_insn.next_insn = (*instr_ptr << 16) | 2057102cedc3SLeonid Yegoshin *(instr_ptr+1); 2058102cedc3SLeonid Yegoshin /* 32-bit instruction. */ 2059102cedc3SLeonid Yegoshin dec_insn.next_pc_inc = 4; 2060102cedc3SLeonid Yegoshin } 2061102cedc3SLeonid Yegoshin dec_insn.micro_mips_mode = 1; 2062102cedc3SLeonid Yegoshin } else { 2063102cedc3SLeonid Yegoshin if ((get_user(dec_insn.insn, 2064102cedc3SLeonid Yegoshin (mips_instruction __user *) xcp->cp0_epc)) || 2065102cedc3SLeonid Yegoshin (get_user(dec_insn.next_insn, 2066102cedc3SLeonid Yegoshin (mips_instruction __user *)(xcp->cp0_epc+4)))) { 2067102cedc3SLeonid Yegoshin MIPS_FPU_EMU_INC_STATS(errors); 2068102cedc3SLeonid Yegoshin return SIGBUS; 2069102cedc3SLeonid Yegoshin } 2070102cedc3SLeonid Yegoshin dec_insn.pc_inc = 4; 2071102cedc3SLeonid Yegoshin dec_insn.next_pc_inc = 4; 2072102cedc3SLeonid Yegoshin dec_insn.micro_mips_mode = 0; 2073102cedc3SLeonid Yegoshin } 2074102cedc3SLeonid Yegoshin 2075102cedc3SLeonid Yegoshin if ((dec_insn.insn == 0) || 2076102cedc3SLeonid Yegoshin ((dec_insn.pc_inc == 2) && 2077102cedc3SLeonid Yegoshin ((dec_insn.insn & 0xffff) == MM_NOP16))) 2078102cedc3SLeonid Yegoshin xcp->cp0_epc += dec_insn.pc_inc; /* Skip NOPs */ 20791da177e4SLinus Torvalds else { 2080cd21dfcfSRalf Baechle /* 2081cd21dfcfSRalf Baechle * The 'ieee754_csr' is an alias of 2082cd21dfcfSRalf Baechle * ctx->fcr31. No need to copy ctx->fcr31 to 2083cd21dfcfSRalf Baechle * ieee754_csr. But ieee754_csr.rm is ieee 2084cd21dfcfSRalf Baechle * library modes. (not mips rounding mode) 2085cd21dfcfSRalf Baechle */ 2086cd21dfcfSRalf Baechle /* convert to ieee library modes */ 2087cd21dfcfSRalf Baechle ieee754_csr.rm = ieee_rm[ieee754_csr.rm]; 2088102cedc3SLeonid Yegoshin sig = cop1Emulate(xcp, ctx, dec_insn, fault_addr); 2089cd21dfcfSRalf Baechle /* revert to mips rounding mode */ 2090cd21dfcfSRalf Baechle ieee754_csr.rm = mips_rm[ieee754_csr.rm]; 20911da177e4SLinus Torvalds } 20921da177e4SLinus Torvalds 2093e04582b7SAtsushi Nemoto if (has_fpu) 20941da177e4SLinus Torvalds break; 20951da177e4SLinus Torvalds if (sig) 20961da177e4SLinus Torvalds break; 20971da177e4SLinus Torvalds 20981da177e4SLinus Torvalds cond_resched(); 20991da177e4SLinus Torvalds } while (xcp->cp0_epc > prevepc); 21001da177e4SLinus Torvalds 21011da177e4SLinus Torvalds /* SIGILL indicates a non-fpu instruction */ 21021da177e4SLinus Torvalds if (sig == SIGILL && xcp->cp0_epc != oldepc) 21031da177e4SLinus Torvalds /* but if epc has advanced, then ignore it */ 21041da177e4SLinus Torvalds sig = 0; 21051da177e4SLinus Torvalds 21061da177e4SLinus Torvalds return sig; 21071da177e4SLinus Torvalds } 210883fd38caSAtsushi Nemoto 210983fd38caSAtsushi Nemoto #ifdef CONFIG_DEBUG_FS 2110b6ee75edSDavid Daney 2111b6ee75edSDavid Daney static int fpuemu_stat_get(void *data, u64 *val) 2112b6ee75edSDavid Daney { 2113b6ee75edSDavid Daney int cpu; 2114b6ee75edSDavid Daney unsigned long sum = 0; 2115b6ee75edSDavid Daney for_each_online_cpu(cpu) { 2116b6ee75edSDavid Daney struct mips_fpu_emulator_stats *ps; 2117b6ee75edSDavid Daney local_t *pv; 2118b6ee75edSDavid Daney ps = &per_cpu(fpuemustats, cpu); 2119b6ee75edSDavid Daney pv = (void *)ps + (unsigned long)data; 2120b6ee75edSDavid Daney sum += local_read(pv); 2121b6ee75edSDavid Daney } 2122b6ee75edSDavid Daney *val = sum; 2123b6ee75edSDavid Daney return 0; 2124b6ee75edSDavid Daney } 2125b6ee75edSDavid Daney DEFINE_SIMPLE_ATTRIBUTE(fops_fpuemu_stat, fpuemu_stat_get, NULL, "%llu\n"); 2126b6ee75edSDavid Daney 212783fd38caSAtsushi Nemoto extern struct dentry *mips_debugfs_dir; 212883fd38caSAtsushi Nemoto static int __init debugfs_fpuemu(void) 212983fd38caSAtsushi Nemoto { 213083fd38caSAtsushi Nemoto struct dentry *d, *dir; 213183fd38caSAtsushi Nemoto 213283fd38caSAtsushi Nemoto if (!mips_debugfs_dir) 213383fd38caSAtsushi Nemoto return -ENODEV; 213483fd38caSAtsushi Nemoto dir = debugfs_create_dir("fpuemustats", mips_debugfs_dir); 2135ecab1f44SZhaolei if (!dir) 2136ecab1f44SZhaolei return -ENOMEM; 2137b6ee75edSDavid Daney 2138b6ee75edSDavid Daney #define FPU_STAT_CREATE(M) \ 2139b6ee75edSDavid Daney do { \ 2140b6ee75edSDavid Daney d = debugfs_create_file(#M , S_IRUGO, dir, \ 2141b6ee75edSDavid Daney (void *)offsetof(struct mips_fpu_emulator_stats, M), \ 2142b6ee75edSDavid Daney &fops_fpuemu_stat); \ 2143b6ee75edSDavid Daney if (!d) \ 2144b6ee75edSDavid Daney return -ENOMEM; \ 2145b6ee75edSDavid Daney } while (0) 2146b6ee75edSDavid Daney 2147b6ee75edSDavid Daney FPU_STAT_CREATE(emulated); 2148b6ee75edSDavid Daney FPU_STAT_CREATE(loads); 2149b6ee75edSDavid Daney FPU_STAT_CREATE(stores); 2150b6ee75edSDavid Daney FPU_STAT_CREATE(cp1ops); 2151b6ee75edSDavid Daney FPU_STAT_CREATE(cp1xops); 2152b6ee75edSDavid Daney FPU_STAT_CREATE(errors); 2153b6ee75edSDavid Daney 215483fd38caSAtsushi Nemoto return 0; 215583fd38caSAtsushi Nemoto } 215683fd38caSAtsushi Nemoto __initcall(debugfs_fpuemu); 215783fd38caSAtsushi Nemoto #endif 2158