162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * cp1emu.c: a MIPS coprocessor 1 (FPU) instruction emulator 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * MIPS floating point support 662306a36Sopenharmony_ci * Copyright (C) 1994-2000 Algorithmics Ltd. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com 962306a36Sopenharmony_ci * Copyright (C) 2000 MIPS Technologies, Inc. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * A complete emulator for MIPS coprocessor 1 instructions. This is 1262306a36Sopenharmony_ci * required for #float(switch) or #float(trap), where it catches all 1362306a36Sopenharmony_ci * COP1 instructions via the "CoProcessor Unusable" exception. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * More surprisingly it is also required for #float(ieee), to help out 1662306a36Sopenharmony_ci * the hardware FPU at the boundaries of the IEEE-754 representation 1762306a36Sopenharmony_ci * (denormalised values, infinities, underflow, etc). It is made 1862306a36Sopenharmony_ci * quite nasty because emulation of some non-COP1 instructions is 1962306a36Sopenharmony_ci * required, e.g. in branch delay slots. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * Note if you know that you won't have an FPU, then you'll get much 2262306a36Sopenharmony_ci * better performance by compiling with -msoft-float! 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci#include <linux/sched.h> 2562306a36Sopenharmony_ci#include <linux/debugfs.h> 2662306a36Sopenharmony_ci#include <linux/percpu-defs.h> 2762306a36Sopenharmony_ci#include <linux/perf_event.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <asm/branch.h> 3062306a36Sopenharmony_ci#include <asm/inst.h> 3162306a36Sopenharmony_ci#include <asm/ptrace.h> 3262306a36Sopenharmony_ci#include <asm/signal.h> 3362306a36Sopenharmony_ci#include <linux/uaccess.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <asm/cpu-info.h> 3662306a36Sopenharmony_ci#include <asm/processor.h> 3762306a36Sopenharmony_ci#include <asm/fpu_emulator.h> 3862306a36Sopenharmony_ci#include <asm/fpu.h> 3962306a36Sopenharmony_ci#include <asm/mips-r2-to-r6-emul.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#include "ieee754.h" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Function which emulates a floating point instruction. */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int fpu_emu(struct pt_regs *, struct mips_fpu_struct *, 4662306a36Sopenharmony_ci mips_instruction); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int fpux_emu(struct pt_regs *, 4962306a36Sopenharmony_ci struct mips_fpu_struct *, mips_instruction, void __user **); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* Control registers */ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define FPCREG_RID 0 /* $0 = revision id */ 5462306a36Sopenharmony_ci#define FPCREG_FCCR 25 /* $25 = fccr */ 5562306a36Sopenharmony_ci#define FPCREG_FEXR 26 /* $26 = fexr */ 5662306a36Sopenharmony_ci#define FPCREG_FENR 28 /* $28 = fenr */ 5762306a36Sopenharmony_ci#define FPCREG_CSR 31 /* $31 = csr */ 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* convert condition code register number to csr bit */ 6062306a36Sopenharmony_ciconst unsigned int fpucondbit[8] = { 6162306a36Sopenharmony_ci FPU_CSR_COND, 6262306a36Sopenharmony_ci FPU_CSR_COND1, 6362306a36Sopenharmony_ci FPU_CSR_COND2, 6462306a36Sopenharmony_ci FPU_CSR_COND3, 6562306a36Sopenharmony_ci FPU_CSR_COND4, 6662306a36Sopenharmony_ci FPU_CSR_COND5, 6762306a36Sopenharmony_ci FPU_CSR_COND6, 6862306a36Sopenharmony_ci FPU_CSR_COND7 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* (microMIPS) Convert certain microMIPS instructions to MIPS32 format. */ 7262306a36Sopenharmony_cistatic const int sd_format[] = {16, 17, 0, 0, 0, 0, 0, 0}; 7362306a36Sopenharmony_cistatic const int sdps_format[] = {16, 17, 22, 0, 0, 0, 0, 0}; 7462306a36Sopenharmony_cistatic const int dwl_format[] = {17, 20, 21, 0, 0, 0, 0, 0}; 7562306a36Sopenharmony_cistatic const int swl_format[] = {16, 20, 21, 0, 0, 0, 0, 0}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* 7862306a36Sopenharmony_ci * This functions translates a 32-bit microMIPS instruction 7962306a36Sopenharmony_ci * into a 32-bit MIPS32 instruction. Returns 0 on success 8062306a36Sopenharmony_ci * and SIGILL otherwise. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_cistatic int microMIPS32_to_MIPS32(union mips_instruction *insn_ptr) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci union mips_instruction insn = *insn_ptr; 8562306a36Sopenharmony_ci union mips_instruction mips32_insn = insn; 8662306a36Sopenharmony_ci int func, fmt, op; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci switch (insn.mm_i_format.opcode) { 8962306a36Sopenharmony_ci case mm_ldc132_op: 9062306a36Sopenharmony_ci mips32_insn.mm_i_format.opcode = ldc1_op; 9162306a36Sopenharmony_ci mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 9262306a36Sopenharmony_ci mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 9362306a36Sopenharmony_ci break; 9462306a36Sopenharmony_ci case mm_lwc132_op: 9562306a36Sopenharmony_ci mips32_insn.mm_i_format.opcode = lwc1_op; 9662306a36Sopenharmony_ci mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 9762306a36Sopenharmony_ci mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 9862306a36Sopenharmony_ci break; 9962306a36Sopenharmony_ci case mm_sdc132_op: 10062306a36Sopenharmony_ci mips32_insn.mm_i_format.opcode = sdc1_op; 10162306a36Sopenharmony_ci mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 10262306a36Sopenharmony_ci mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci case mm_swc132_op: 10562306a36Sopenharmony_ci mips32_insn.mm_i_format.opcode = swc1_op; 10662306a36Sopenharmony_ci mips32_insn.mm_i_format.rt = insn.mm_i_format.rs; 10762306a36Sopenharmony_ci mips32_insn.mm_i_format.rs = insn.mm_i_format.rt; 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci case mm_pool32i_op: 11062306a36Sopenharmony_ci /* NOTE: offset is << by 1 if in microMIPS mode. */ 11162306a36Sopenharmony_ci if ((insn.mm_i_format.rt == mm_bc1f_op) || 11262306a36Sopenharmony_ci (insn.mm_i_format.rt == mm_bc1t_op)) { 11362306a36Sopenharmony_ci mips32_insn.fb_format.opcode = cop1_op; 11462306a36Sopenharmony_ci mips32_insn.fb_format.bc = bc_op; 11562306a36Sopenharmony_ci mips32_insn.fb_format.flag = 11662306a36Sopenharmony_ci (insn.mm_i_format.rt == mm_bc1t_op) ? 1 : 0; 11762306a36Sopenharmony_ci } else 11862306a36Sopenharmony_ci return SIGILL; 11962306a36Sopenharmony_ci break; 12062306a36Sopenharmony_ci case mm_pool32f_op: 12162306a36Sopenharmony_ci switch (insn.mm_fp0_format.func) { 12262306a36Sopenharmony_ci case mm_32f_01_op: 12362306a36Sopenharmony_ci case mm_32f_11_op: 12462306a36Sopenharmony_ci case mm_32f_02_op: 12562306a36Sopenharmony_ci case mm_32f_12_op: 12662306a36Sopenharmony_ci case mm_32f_41_op: 12762306a36Sopenharmony_ci case mm_32f_51_op: 12862306a36Sopenharmony_ci case mm_32f_42_op: 12962306a36Sopenharmony_ci case mm_32f_52_op: 13062306a36Sopenharmony_ci op = insn.mm_fp0_format.func; 13162306a36Sopenharmony_ci if (op == mm_32f_01_op) 13262306a36Sopenharmony_ci func = madd_s_op; 13362306a36Sopenharmony_ci else if (op == mm_32f_11_op) 13462306a36Sopenharmony_ci func = madd_d_op; 13562306a36Sopenharmony_ci else if (op == mm_32f_02_op) 13662306a36Sopenharmony_ci func = nmadd_s_op; 13762306a36Sopenharmony_ci else if (op == mm_32f_12_op) 13862306a36Sopenharmony_ci func = nmadd_d_op; 13962306a36Sopenharmony_ci else if (op == mm_32f_41_op) 14062306a36Sopenharmony_ci func = msub_s_op; 14162306a36Sopenharmony_ci else if (op == mm_32f_51_op) 14262306a36Sopenharmony_ci func = msub_d_op; 14362306a36Sopenharmony_ci else if (op == mm_32f_42_op) 14462306a36Sopenharmony_ci func = nmsub_s_op; 14562306a36Sopenharmony_ci else 14662306a36Sopenharmony_ci func = nmsub_d_op; 14762306a36Sopenharmony_ci mips32_insn.fp6_format.opcode = cop1x_op; 14862306a36Sopenharmony_ci mips32_insn.fp6_format.fr = insn.mm_fp6_format.fr; 14962306a36Sopenharmony_ci mips32_insn.fp6_format.ft = insn.mm_fp6_format.ft; 15062306a36Sopenharmony_ci mips32_insn.fp6_format.fs = insn.mm_fp6_format.fs; 15162306a36Sopenharmony_ci mips32_insn.fp6_format.fd = insn.mm_fp6_format.fd; 15262306a36Sopenharmony_ci mips32_insn.fp6_format.func = func; 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci case mm_32f_10_op: 15562306a36Sopenharmony_ci func = -1; /* Invalid */ 15662306a36Sopenharmony_ci op = insn.mm_fp5_format.op & 0x7; 15762306a36Sopenharmony_ci if (op == mm_ldxc1_op) 15862306a36Sopenharmony_ci func = ldxc1_op; 15962306a36Sopenharmony_ci else if (op == mm_sdxc1_op) 16062306a36Sopenharmony_ci func = sdxc1_op; 16162306a36Sopenharmony_ci else if (op == mm_lwxc1_op) 16262306a36Sopenharmony_ci func = lwxc1_op; 16362306a36Sopenharmony_ci else if (op == mm_swxc1_op) 16462306a36Sopenharmony_ci func = swxc1_op; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (func != -1) { 16762306a36Sopenharmony_ci mips32_insn.r_format.opcode = cop1x_op; 16862306a36Sopenharmony_ci mips32_insn.r_format.rs = 16962306a36Sopenharmony_ci insn.mm_fp5_format.base; 17062306a36Sopenharmony_ci mips32_insn.r_format.rt = 17162306a36Sopenharmony_ci insn.mm_fp5_format.index; 17262306a36Sopenharmony_ci mips32_insn.r_format.rd = 0; 17362306a36Sopenharmony_ci mips32_insn.r_format.re = insn.mm_fp5_format.fd; 17462306a36Sopenharmony_ci mips32_insn.r_format.func = func; 17562306a36Sopenharmony_ci } else 17662306a36Sopenharmony_ci return SIGILL; 17762306a36Sopenharmony_ci break; 17862306a36Sopenharmony_ci case mm_32f_40_op: 17962306a36Sopenharmony_ci op = -1; /* Invalid */ 18062306a36Sopenharmony_ci if (insn.mm_fp2_format.op == mm_fmovt_op) 18162306a36Sopenharmony_ci op = 1; 18262306a36Sopenharmony_ci else if (insn.mm_fp2_format.op == mm_fmovf_op) 18362306a36Sopenharmony_ci op = 0; 18462306a36Sopenharmony_ci if (op != -1) { 18562306a36Sopenharmony_ci mips32_insn.fp0_format.opcode = cop1_op; 18662306a36Sopenharmony_ci mips32_insn.fp0_format.fmt = 18762306a36Sopenharmony_ci sdps_format[insn.mm_fp2_format.fmt]; 18862306a36Sopenharmony_ci mips32_insn.fp0_format.ft = 18962306a36Sopenharmony_ci (insn.mm_fp2_format.cc<<2) + op; 19062306a36Sopenharmony_ci mips32_insn.fp0_format.fs = 19162306a36Sopenharmony_ci insn.mm_fp2_format.fs; 19262306a36Sopenharmony_ci mips32_insn.fp0_format.fd = 19362306a36Sopenharmony_ci insn.mm_fp2_format.fd; 19462306a36Sopenharmony_ci mips32_insn.fp0_format.func = fmovc_op; 19562306a36Sopenharmony_ci } else 19662306a36Sopenharmony_ci return SIGILL; 19762306a36Sopenharmony_ci break; 19862306a36Sopenharmony_ci case mm_32f_60_op: 19962306a36Sopenharmony_ci func = -1; /* Invalid */ 20062306a36Sopenharmony_ci if (insn.mm_fp0_format.op == mm_fadd_op) 20162306a36Sopenharmony_ci func = fadd_op; 20262306a36Sopenharmony_ci else if (insn.mm_fp0_format.op == mm_fsub_op) 20362306a36Sopenharmony_ci func = fsub_op; 20462306a36Sopenharmony_ci else if (insn.mm_fp0_format.op == mm_fmul_op) 20562306a36Sopenharmony_ci func = fmul_op; 20662306a36Sopenharmony_ci else if (insn.mm_fp0_format.op == mm_fdiv_op) 20762306a36Sopenharmony_ci func = fdiv_op; 20862306a36Sopenharmony_ci if (func != -1) { 20962306a36Sopenharmony_ci mips32_insn.fp0_format.opcode = cop1_op; 21062306a36Sopenharmony_ci mips32_insn.fp0_format.fmt = 21162306a36Sopenharmony_ci sdps_format[insn.mm_fp0_format.fmt]; 21262306a36Sopenharmony_ci mips32_insn.fp0_format.ft = 21362306a36Sopenharmony_ci insn.mm_fp0_format.ft; 21462306a36Sopenharmony_ci mips32_insn.fp0_format.fs = 21562306a36Sopenharmony_ci insn.mm_fp0_format.fs; 21662306a36Sopenharmony_ci mips32_insn.fp0_format.fd = 21762306a36Sopenharmony_ci insn.mm_fp0_format.fd; 21862306a36Sopenharmony_ci mips32_insn.fp0_format.func = func; 21962306a36Sopenharmony_ci } else 22062306a36Sopenharmony_ci return SIGILL; 22162306a36Sopenharmony_ci break; 22262306a36Sopenharmony_ci case mm_32f_70_op: 22362306a36Sopenharmony_ci func = -1; /* Invalid */ 22462306a36Sopenharmony_ci if (insn.mm_fp0_format.op == mm_fmovn_op) 22562306a36Sopenharmony_ci func = fmovn_op; 22662306a36Sopenharmony_ci else if (insn.mm_fp0_format.op == mm_fmovz_op) 22762306a36Sopenharmony_ci func = fmovz_op; 22862306a36Sopenharmony_ci if (func != -1) { 22962306a36Sopenharmony_ci mips32_insn.fp0_format.opcode = cop1_op; 23062306a36Sopenharmony_ci mips32_insn.fp0_format.fmt = 23162306a36Sopenharmony_ci sdps_format[insn.mm_fp0_format.fmt]; 23262306a36Sopenharmony_ci mips32_insn.fp0_format.ft = 23362306a36Sopenharmony_ci insn.mm_fp0_format.ft; 23462306a36Sopenharmony_ci mips32_insn.fp0_format.fs = 23562306a36Sopenharmony_ci insn.mm_fp0_format.fs; 23662306a36Sopenharmony_ci mips32_insn.fp0_format.fd = 23762306a36Sopenharmony_ci insn.mm_fp0_format.fd; 23862306a36Sopenharmony_ci mips32_insn.fp0_format.func = func; 23962306a36Sopenharmony_ci } else 24062306a36Sopenharmony_ci return SIGILL; 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci case mm_32f_73_op: /* POOL32FXF */ 24362306a36Sopenharmony_ci switch (insn.mm_fp1_format.op) { 24462306a36Sopenharmony_ci case mm_movf0_op: 24562306a36Sopenharmony_ci case mm_movf1_op: 24662306a36Sopenharmony_ci case mm_movt0_op: 24762306a36Sopenharmony_ci case mm_movt1_op: 24862306a36Sopenharmony_ci if ((insn.mm_fp1_format.op & 0x7f) == 24962306a36Sopenharmony_ci mm_movf0_op) 25062306a36Sopenharmony_ci op = 0; 25162306a36Sopenharmony_ci else 25262306a36Sopenharmony_ci op = 1; 25362306a36Sopenharmony_ci mips32_insn.r_format.opcode = spec_op; 25462306a36Sopenharmony_ci mips32_insn.r_format.rs = insn.mm_fp4_format.fs; 25562306a36Sopenharmony_ci mips32_insn.r_format.rt = 25662306a36Sopenharmony_ci (insn.mm_fp4_format.cc << 2) + op; 25762306a36Sopenharmony_ci mips32_insn.r_format.rd = insn.mm_fp4_format.rt; 25862306a36Sopenharmony_ci mips32_insn.r_format.re = 0; 25962306a36Sopenharmony_ci mips32_insn.r_format.func = movc_op; 26062306a36Sopenharmony_ci break; 26162306a36Sopenharmony_ci case mm_fcvtd0_op: 26262306a36Sopenharmony_ci case mm_fcvtd1_op: 26362306a36Sopenharmony_ci case mm_fcvts0_op: 26462306a36Sopenharmony_ci case mm_fcvts1_op: 26562306a36Sopenharmony_ci if ((insn.mm_fp1_format.op & 0x7f) == 26662306a36Sopenharmony_ci mm_fcvtd0_op) { 26762306a36Sopenharmony_ci func = fcvtd_op; 26862306a36Sopenharmony_ci fmt = swl_format[insn.mm_fp3_format.fmt]; 26962306a36Sopenharmony_ci } else { 27062306a36Sopenharmony_ci func = fcvts_op; 27162306a36Sopenharmony_ci fmt = dwl_format[insn.mm_fp3_format.fmt]; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci mips32_insn.fp0_format.opcode = cop1_op; 27462306a36Sopenharmony_ci mips32_insn.fp0_format.fmt = fmt; 27562306a36Sopenharmony_ci mips32_insn.fp0_format.ft = 0; 27662306a36Sopenharmony_ci mips32_insn.fp0_format.fs = 27762306a36Sopenharmony_ci insn.mm_fp3_format.fs; 27862306a36Sopenharmony_ci mips32_insn.fp0_format.fd = 27962306a36Sopenharmony_ci insn.mm_fp3_format.rt; 28062306a36Sopenharmony_ci mips32_insn.fp0_format.func = func; 28162306a36Sopenharmony_ci break; 28262306a36Sopenharmony_ci case mm_fmov0_op: 28362306a36Sopenharmony_ci case mm_fmov1_op: 28462306a36Sopenharmony_ci case mm_fabs0_op: 28562306a36Sopenharmony_ci case mm_fabs1_op: 28662306a36Sopenharmony_ci case mm_fneg0_op: 28762306a36Sopenharmony_ci case mm_fneg1_op: 28862306a36Sopenharmony_ci if ((insn.mm_fp1_format.op & 0x7f) == 28962306a36Sopenharmony_ci mm_fmov0_op) 29062306a36Sopenharmony_ci func = fmov_op; 29162306a36Sopenharmony_ci else if ((insn.mm_fp1_format.op & 0x7f) == 29262306a36Sopenharmony_ci mm_fabs0_op) 29362306a36Sopenharmony_ci func = fabs_op; 29462306a36Sopenharmony_ci else 29562306a36Sopenharmony_ci func = fneg_op; 29662306a36Sopenharmony_ci mips32_insn.fp0_format.opcode = cop1_op; 29762306a36Sopenharmony_ci mips32_insn.fp0_format.fmt = 29862306a36Sopenharmony_ci sdps_format[insn.mm_fp3_format.fmt]; 29962306a36Sopenharmony_ci mips32_insn.fp0_format.ft = 0; 30062306a36Sopenharmony_ci mips32_insn.fp0_format.fs = 30162306a36Sopenharmony_ci insn.mm_fp3_format.fs; 30262306a36Sopenharmony_ci mips32_insn.fp0_format.fd = 30362306a36Sopenharmony_ci insn.mm_fp3_format.rt; 30462306a36Sopenharmony_ci mips32_insn.fp0_format.func = func; 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci case mm_ffloorl_op: 30762306a36Sopenharmony_ci case mm_ffloorw_op: 30862306a36Sopenharmony_ci case mm_fceill_op: 30962306a36Sopenharmony_ci case mm_fceilw_op: 31062306a36Sopenharmony_ci case mm_ftruncl_op: 31162306a36Sopenharmony_ci case mm_ftruncw_op: 31262306a36Sopenharmony_ci case mm_froundl_op: 31362306a36Sopenharmony_ci case mm_froundw_op: 31462306a36Sopenharmony_ci case mm_fcvtl_op: 31562306a36Sopenharmony_ci case mm_fcvtw_op: 31662306a36Sopenharmony_ci if (insn.mm_fp1_format.op == mm_ffloorl_op) 31762306a36Sopenharmony_ci func = ffloorl_op; 31862306a36Sopenharmony_ci else if (insn.mm_fp1_format.op == mm_ffloorw_op) 31962306a36Sopenharmony_ci func = ffloor_op; 32062306a36Sopenharmony_ci else if (insn.mm_fp1_format.op == mm_fceill_op) 32162306a36Sopenharmony_ci func = fceill_op; 32262306a36Sopenharmony_ci else if (insn.mm_fp1_format.op == mm_fceilw_op) 32362306a36Sopenharmony_ci func = fceil_op; 32462306a36Sopenharmony_ci else if (insn.mm_fp1_format.op == mm_ftruncl_op) 32562306a36Sopenharmony_ci func = ftruncl_op; 32662306a36Sopenharmony_ci else if (insn.mm_fp1_format.op == mm_ftruncw_op) 32762306a36Sopenharmony_ci func = ftrunc_op; 32862306a36Sopenharmony_ci else if (insn.mm_fp1_format.op == mm_froundl_op) 32962306a36Sopenharmony_ci func = froundl_op; 33062306a36Sopenharmony_ci else if (insn.mm_fp1_format.op == mm_froundw_op) 33162306a36Sopenharmony_ci func = fround_op; 33262306a36Sopenharmony_ci else if (insn.mm_fp1_format.op == mm_fcvtl_op) 33362306a36Sopenharmony_ci func = fcvtl_op; 33462306a36Sopenharmony_ci else 33562306a36Sopenharmony_ci func = fcvtw_op; 33662306a36Sopenharmony_ci mips32_insn.fp0_format.opcode = cop1_op; 33762306a36Sopenharmony_ci mips32_insn.fp0_format.fmt = 33862306a36Sopenharmony_ci sd_format[insn.mm_fp1_format.fmt]; 33962306a36Sopenharmony_ci mips32_insn.fp0_format.ft = 0; 34062306a36Sopenharmony_ci mips32_insn.fp0_format.fs = 34162306a36Sopenharmony_ci insn.mm_fp1_format.fs; 34262306a36Sopenharmony_ci mips32_insn.fp0_format.fd = 34362306a36Sopenharmony_ci insn.mm_fp1_format.rt; 34462306a36Sopenharmony_ci mips32_insn.fp0_format.func = func; 34562306a36Sopenharmony_ci break; 34662306a36Sopenharmony_ci case mm_frsqrt_op: 34762306a36Sopenharmony_ci case mm_fsqrt_op: 34862306a36Sopenharmony_ci case mm_frecip_op: 34962306a36Sopenharmony_ci if (insn.mm_fp1_format.op == mm_frsqrt_op) 35062306a36Sopenharmony_ci func = frsqrt_op; 35162306a36Sopenharmony_ci else if (insn.mm_fp1_format.op == mm_fsqrt_op) 35262306a36Sopenharmony_ci func = fsqrt_op; 35362306a36Sopenharmony_ci else 35462306a36Sopenharmony_ci func = frecip_op; 35562306a36Sopenharmony_ci mips32_insn.fp0_format.opcode = cop1_op; 35662306a36Sopenharmony_ci mips32_insn.fp0_format.fmt = 35762306a36Sopenharmony_ci sdps_format[insn.mm_fp1_format.fmt]; 35862306a36Sopenharmony_ci mips32_insn.fp0_format.ft = 0; 35962306a36Sopenharmony_ci mips32_insn.fp0_format.fs = 36062306a36Sopenharmony_ci insn.mm_fp1_format.fs; 36162306a36Sopenharmony_ci mips32_insn.fp0_format.fd = 36262306a36Sopenharmony_ci insn.mm_fp1_format.rt; 36362306a36Sopenharmony_ci mips32_insn.fp0_format.func = func; 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci case mm_mfc1_op: 36662306a36Sopenharmony_ci case mm_mtc1_op: 36762306a36Sopenharmony_ci case mm_cfc1_op: 36862306a36Sopenharmony_ci case mm_ctc1_op: 36962306a36Sopenharmony_ci case mm_mfhc1_op: 37062306a36Sopenharmony_ci case mm_mthc1_op: 37162306a36Sopenharmony_ci if (insn.mm_fp1_format.op == mm_mfc1_op) 37262306a36Sopenharmony_ci op = mfc_op; 37362306a36Sopenharmony_ci else if (insn.mm_fp1_format.op == mm_mtc1_op) 37462306a36Sopenharmony_ci op = mtc_op; 37562306a36Sopenharmony_ci else if (insn.mm_fp1_format.op == mm_cfc1_op) 37662306a36Sopenharmony_ci op = cfc_op; 37762306a36Sopenharmony_ci else if (insn.mm_fp1_format.op == mm_ctc1_op) 37862306a36Sopenharmony_ci op = ctc_op; 37962306a36Sopenharmony_ci else if (insn.mm_fp1_format.op == mm_mfhc1_op) 38062306a36Sopenharmony_ci op = mfhc_op; 38162306a36Sopenharmony_ci else 38262306a36Sopenharmony_ci op = mthc_op; 38362306a36Sopenharmony_ci mips32_insn.fp1_format.opcode = cop1_op; 38462306a36Sopenharmony_ci mips32_insn.fp1_format.op = op; 38562306a36Sopenharmony_ci mips32_insn.fp1_format.rt = 38662306a36Sopenharmony_ci insn.mm_fp1_format.rt; 38762306a36Sopenharmony_ci mips32_insn.fp1_format.fs = 38862306a36Sopenharmony_ci insn.mm_fp1_format.fs; 38962306a36Sopenharmony_ci mips32_insn.fp1_format.fd = 0; 39062306a36Sopenharmony_ci mips32_insn.fp1_format.func = 0; 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci default: 39362306a36Sopenharmony_ci return SIGILL; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci case mm_32f_74_op: /* c.cond.fmt */ 39762306a36Sopenharmony_ci mips32_insn.fp0_format.opcode = cop1_op; 39862306a36Sopenharmony_ci mips32_insn.fp0_format.fmt = 39962306a36Sopenharmony_ci sdps_format[insn.mm_fp4_format.fmt]; 40062306a36Sopenharmony_ci mips32_insn.fp0_format.ft = insn.mm_fp4_format.rt; 40162306a36Sopenharmony_ci mips32_insn.fp0_format.fs = insn.mm_fp4_format.fs; 40262306a36Sopenharmony_ci mips32_insn.fp0_format.fd = insn.mm_fp4_format.cc << 2; 40362306a36Sopenharmony_ci mips32_insn.fp0_format.func = 40462306a36Sopenharmony_ci insn.mm_fp4_format.cond | MM_MIPS32_COND_FC; 40562306a36Sopenharmony_ci break; 40662306a36Sopenharmony_ci default: 40762306a36Sopenharmony_ci return SIGILL; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci default: 41162306a36Sopenharmony_ci return SIGILL; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci *insn_ptr = mips32_insn; 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci/* 41962306a36Sopenharmony_ci * Redundant with logic already in kernel/branch.c, 42062306a36Sopenharmony_ci * embedded in compute_return_epc. At some point, 42162306a36Sopenharmony_ci * a single subroutine should be used across both 42262306a36Sopenharmony_ci * modules. 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_ciint isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, 42562306a36Sopenharmony_ci unsigned long *contpc) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci union mips_instruction insn = (union mips_instruction)dec_insn.insn; 42862306a36Sopenharmony_ci unsigned int fcr31; 42962306a36Sopenharmony_ci unsigned int bit = 0; 43062306a36Sopenharmony_ci unsigned int bit0; 43162306a36Sopenharmony_ci union fpureg *fpr; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci switch (insn.i_format.opcode) { 43462306a36Sopenharmony_ci case spec_op: 43562306a36Sopenharmony_ci switch (insn.r_format.func) { 43662306a36Sopenharmony_ci case jalr_op: 43762306a36Sopenharmony_ci if (insn.r_format.rd != 0) { 43862306a36Sopenharmony_ci regs->regs[insn.r_format.rd] = 43962306a36Sopenharmony_ci regs->cp0_epc + dec_insn.pc_inc + 44062306a36Sopenharmony_ci dec_insn.next_pc_inc; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci fallthrough; 44362306a36Sopenharmony_ci case jr_op: 44462306a36Sopenharmony_ci /* For R6, JR already emulated in jalr_op */ 44562306a36Sopenharmony_ci if (NO_R6EMU && insn.r_format.func == jr_op) 44662306a36Sopenharmony_ci break; 44762306a36Sopenharmony_ci *contpc = regs->regs[insn.r_format.rs]; 44862306a36Sopenharmony_ci return 1; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci break; 45162306a36Sopenharmony_ci case bcond_op: 45262306a36Sopenharmony_ci switch (insn.i_format.rt) { 45362306a36Sopenharmony_ci case bltzal_op: 45462306a36Sopenharmony_ci case bltzall_op: 45562306a36Sopenharmony_ci if (NO_R6EMU && (insn.i_format.rs || 45662306a36Sopenharmony_ci insn.i_format.rt == bltzall_op)) 45762306a36Sopenharmony_ci break; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci regs->regs[31] = regs->cp0_epc + 46062306a36Sopenharmony_ci dec_insn.pc_inc + 46162306a36Sopenharmony_ci dec_insn.next_pc_inc; 46262306a36Sopenharmony_ci fallthrough; 46362306a36Sopenharmony_ci case bltzl_op: 46462306a36Sopenharmony_ci if (NO_R6EMU) 46562306a36Sopenharmony_ci break; 46662306a36Sopenharmony_ci fallthrough; 46762306a36Sopenharmony_ci case bltz_op: 46862306a36Sopenharmony_ci if ((long)regs->regs[insn.i_format.rs] < 0) 46962306a36Sopenharmony_ci *contpc = regs->cp0_epc + 47062306a36Sopenharmony_ci dec_insn.pc_inc + 47162306a36Sopenharmony_ci (insn.i_format.simmediate << 2); 47262306a36Sopenharmony_ci else 47362306a36Sopenharmony_ci *contpc = regs->cp0_epc + 47462306a36Sopenharmony_ci dec_insn.pc_inc + 47562306a36Sopenharmony_ci dec_insn.next_pc_inc; 47662306a36Sopenharmony_ci return 1; 47762306a36Sopenharmony_ci case bgezal_op: 47862306a36Sopenharmony_ci case bgezall_op: 47962306a36Sopenharmony_ci if (NO_R6EMU && (insn.i_format.rs || 48062306a36Sopenharmony_ci insn.i_format.rt == bgezall_op)) 48162306a36Sopenharmony_ci break; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci regs->regs[31] = regs->cp0_epc + 48462306a36Sopenharmony_ci dec_insn.pc_inc + 48562306a36Sopenharmony_ci dec_insn.next_pc_inc; 48662306a36Sopenharmony_ci fallthrough; 48762306a36Sopenharmony_ci case bgezl_op: 48862306a36Sopenharmony_ci if (NO_R6EMU) 48962306a36Sopenharmony_ci break; 49062306a36Sopenharmony_ci fallthrough; 49162306a36Sopenharmony_ci case bgez_op: 49262306a36Sopenharmony_ci if ((long)regs->regs[insn.i_format.rs] >= 0) 49362306a36Sopenharmony_ci *contpc = regs->cp0_epc + 49462306a36Sopenharmony_ci dec_insn.pc_inc + 49562306a36Sopenharmony_ci (insn.i_format.simmediate << 2); 49662306a36Sopenharmony_ci else 49762306a36Sopenharmony_ci *contpc = regs->cp0_epc + 49862306a36Sopenharmony_ci dec_insn.pc_inc + 49962306a36Sopenharmony_ci dec_insn.next_pc_inc; 50062306a36Sopenharmony_ci return 1; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci break; 50362306a36Sopenharmony_ci case jalx_op: 50462306a36Sopenharmony_ci set_isa16_mode(bit); 50562306a36Sopenharmony_ci fallthrough; 50662306a36Sopenharmony_ci case jal_op: 50762306a36Sopenharmony_ci regs->regs[31] = regs->cp0_epc + 50862306a36Sopenharmony_ci dec_insn.pc_inc + 50962306a36Sopenharmony_ci dec_insn.next_pc_inc; 51062306a36Sopenharmony_ci fallthrough; 51162306a36Sopenharmony_ci case j_op: 51262306a36Sopenharmony_ci *contpc = regs->cp0_epc + dec_insn.pc_inc; 51362306a36Sopenharmony_ci *contpc >>= 28; 51462306a36Sopenharmony_ci *contpc <<= 28; 51562306a36Sopenharmony_ci *contpc |= (insn.j_format.target << 2); 51662306a36Sopenharmony_ci /* Set microMIPS mode bit: XOR for jalx. */ 51762306a36Sopenharmony_ci *contpc ^= bit; 51862306a36Sopenharmony_ci return 1; 51962306a36Sopenharmony_ci case beql_op: 52062306a36Sopenharmony_ci if (NO_R6EMU) 52162306a36Sopenharmony_ci break; 52262306a36Sopenharmony_ci fallthrough; 52362306a36Sopenharmony_ci case beq_op: 52462306a36Sopenharmony_ci if (regs->regs[insn.i_format.rs] == 52562306a36Sopenharmony_ci regs->regs[insn.i_format.rt]) 52662306a36Sopenharmony_ci *contpc = regs->cp0_epc + 52762306a36Sopenharmony_ci dec_insn.pc_inc + 52862306a36Sopenharmony_ci (insn.i_format.simmediate << 2); 52962306a36Sopenharmony_ci else 53062306a36Sopenharmony_ci *contpc = regs->cp0_epc + 53162306a36Sopenharmony_ci dec_insn.pc_inc + 53262306a36Sopenharmony_ci dec_insn.next_pc_inc; 53362306a36Sopenharmony_ci return 1; 53462306a36Sopenharmony_ci case bnel_op: 53562306a36Sopenharmony_ci if (NO_R6EMU) 53662306a36Sopenharmony_ci break; 53762306a36Sopenharmony_ci fallthrough; 53862306a36Sopenharmony_ci case bne_op: 53962306a36Sopenharmony_ci if (regs->regs[insn.i_format.rs] != 54062306a36Sopenharmony_ci regs->regs[insn.i_format.rt]) 54162306a36Sopenharmony_ci *contpc = regs->cp0_epc + 54262306a36Sopenharmony_ci dec_insn.pc_inc + 54362306a36Sopenharmony_ci (insn.i_format.simmediate << 2); 54462306a36Sopenharmony_ci else 54562306a36Sopenharmony_ci *contpc = regs->cp0_epc + 54662306a36Sopenharmony_ci dec_insn.pc_inc + 54762306a36Sopenharmony_ci dec_insn.next_pc_inc; 54862306a36Sopenharmony_ci return 1; 54962306a36Sopenharmony_ci case blezl_op: 55062306a36Sopenharmony_ci if (!insn.i_format.rt && NO_R6EMU) 55162306a36Sopenharmony_ci break; 55262306a36Sopenharmony_ci fallthrough; 55362306a36Sopenharmony_ci case blez_op: 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* 55662306a36Sopenharmony_ci * Compact branches for R6 for the 55762306a36Sopenharmony_ci * blez and blezl opcodes. 55862306a36Sopenharmony_ci * BLEZ | rs = 0 | rt != 0 == BLEZALC 55962306a36Sopenharmony_ci * BLEZ | rs = rt != 0 == BGEZALC 56062306a36Sopenharmony_ci * BLEZ | rs != 0 | rt != 0 == BGEUC 56162306a36Sopenharmony_ci * BLEZL | rs = 0 | rt != 0 == BLEZC 56262306a36Sopenharmony_ci * BLEZL | rs = rt != 0 == BGEZC 56362306a36Sopenharmony_ci * BLEZL | rs != 0 | rt != 0 == BGEC 56462306a36Sopenharmony_ci * 56562306a36Sopenharmony_ci * For real BLEZ{,L}, rt is always 0. 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci if (cpu_has_mips_r6 && insn.i_format.rt) { 56862306a36Sopenharmony_ci if ((insn.i_format.opcode == blez_op) && 56962306a36Sopenharmony_ci ((!insn.i_format.rs && insn.i_format.rt) || 57062306a36Sopenharmony_ci (insn.i_format.rs == insn.i_format.rt))) 57162306a36Sopenharmony_ci regs->regs[31] = regs->cp0_epc + 57262306a36Sopenharmony_ci dec_insn.pc_inc; 57362306a36Sopenharmony_ci *contpc = regs->cp0_epc + dec_insn.pc_inc + 57462306a36Sopenharmony_ci dec_insn.next_pc_inc; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci return 1; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci if ((long)regs->regs[insn.i_format.rs] <= 0) 57962306a36Sopenharmony_ci *contpc = regs->cp0_epc + 58062306a36Sopenharmony_ci dec_insn.pc_inc + 58162306a36Sopenharmony_ci (insn.i_format.simmediate << 2); 58262306a36Sopenharmony_ci else 58362306a36Sopenharmony_ci *contpc = regs->cp0_epc + 58462306a36Sopenharmony_ci dec_insn.pc_inc + 58562306a36Sopenharmony_ci dec_insn.next_pc_inc; 58662306a36Sopenharmony_ci return 1; 58762306a36Sopenharmony_ci case bgtzl_op: 58862306a36Sopenharmony_ci if (!insn.i_format.rt && NO_R6EMU) 58962306a36Sopenharmony_ci break; 59062306a36Sopenharmony_ci fallthrough; 59162306a36Sopenharmony_ci case bgtz_op: 59262306a36Sopenharmony_ci /* 59362306a36Sopenharmony_ci * Compact branches for R6 for the 59462306a36Sopenharmony_ci * bgtz and bgtzl opcodes. 59562306a36Sopenharmony_ci * BGTZ | rs = 0 | rt != 0 == BGTZALC 59662306a36Sopenharmony_ci * BGTZ | rs = rt != 0 == BLTZALC 59762306a36Sopenharmony_ci * BGTZ | rs != 0 | rt != 0 == BLTUC 59862306a36Sopenharmony_ci * BGTZL | rs = 0 | rt != 0 == BGTZC 59962306a36Sopenharmony_ci * BGTZL | rs = rt != 0 == BLTZC 60062306a36Sopenharmony_ci * BGTZL | rs != 0 | rt != 0 == BLTC 60162306a36Sopenharmony_ci * 60262306a36Sopenharmony_ci * *ZALC varint for BGTZ &&& rt != 0 60362306a36Sopenharmony_ci * For real GTZ{,L}, rt is always 0. 60462306a36Sopenharmony_ci */ 60562306a36Sopenharmony_ci if (cpu_has_mips_r6 && insn.i_format.rt) { 60662306a36Sopenharmony_ci if ((insn.i_format.opcode == blez_op) && 60762306a36Sopenharmony_ci ((!insn.i_format.rs && insn.i_format.rt) || 60862306a36Sopenharmony_ci (insn.i_format.rs == insn.i_format.rt))) 60962306a36Sopenharmony_ci regs->regs[31] = regs->cp0_epc + 61062306a36Sopenharmony_ci dec_insn.pc_inc; 61162306a36Sopenharmony_ci *contpc = regs->cp0_epc + dec_insn.pc_inc + 61262306a36Sopenharmony_ci dec_insn.next_pc_inc; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci return 1; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if ((long)regs->regs[insn.i_format.rs] > 0) 61862306a36Sopenharmony_ci *contpc = regs->cp0_epc + 61962306a36Sopenharmony_ci dec_insn.pc_inc + 62062306a36Sopenharmony_ci (insn.i_format.simmediate << 2); 62162306a36Sopenharmony_ci else 62262306a36Sopenharmony_ci *contpc = regs->cp0_epc + 62362306a36Sopenharmony_ci dec_insn.pc_inc + 62462306a36Sopenharmony_ci dec_insn.next_pc_inc; 62562306a36Sopenharmony_ci return 1; 62662306a36Sopenharmony_ci case pop10_op: 62762306a36Sopenharmony_ci case pop30_op: 62862306a36Sopenharmony_ci if (!cpu_has_mips_r6) 62962306a36Sopenharmony_ci break; 63062306a36Sopenharmony_ci if (insn.i_format.rt && !insn.i_format.rs) 63162306a36Sopenharmony_ci regs->regs[31] = regs->cp0_epc + 4; 63262306a36Sopenharmony_ci *contpc = regs->cp0_epc + dec_insn.pc_inc + 63362306a36Sopenharmony_ci dec_insn.next_pc_inc; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci return 1; 63662306a36Sopenharmony_ci#ifdef CONFIG_CPU_CAVIUM_OCTEON 63762306a36Sopenharmony_ci case lwc2_op: /* This is bbit0 on Octeon */ 63862306a36Sopenharmony_ci if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) == 0) 63962306a36Sopenharmony_ci *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); 64062306a36Sopenharmony_ci else 64162306a36Sopenharmony_ci *contpc = regs->cp0_epc + 8; 64262306a36Sopenharmony_ci return 1; 64362306a36Sopenharmony_ci case ldc2_op: /* This is bbit032 on Octeon */ 64462306a36Sopenharmony_ci if ((regs->regs[insn.i_format.rs] & (1ull<<(insn.i_format.rt + 32))) == 0) 64562306a36Sopenharmony_ci *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); 64662306a36Sopenharmony_ci else 64762306a36Sopenharmony_ci *contpc = regs->cp0_epc + 8; 64862306a36Sopenharmony_ci return 1; 64962306a36Sopenharmony_ci case swc2_op: /* This is bbit1 on Octeon */ 65062306a36Sopenharmony_ci if (regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) 65162306a36Sopenharmony_ci *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); 65262306a36Sopenharmony_ci else 65362306a36Sopenharmony_ci *contpc = regs->cp0_epc + 8; 65462306a36Sopenharmony_ci return 1; 65562306a36Sopenharmony_ci case sdc2_op: /* This is bbit132 on Octeon */ 65662306a36Sopenharmony_ci if (regs->regs[insn.i_format.rs] & (1ull<<(insn.i_format.rt + 32))) 65762306a36Sopenharmony_ci *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2); 65862306a36Sopenharmony_ci else 65962306a36Sopenharmony_ci *contpc = regs->cp0_epc + 8; 66062306a36Sopenharmony_ci return 1; 66162306a36Sopenharmony_ci#else 66262306a36Sopenharmony_ci case bc6_op: 66362306a36Sopenharmony_ci /* 66462306a36Sopenharmony_ci * Only valid for MIPS R6 but we can still end up 66562306a36Sopenharmony_ci * here from a broken userland so just tell emulator 66662306a36Sopenharmony_ci * this is not a branch and let it break later on. 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_ci if (!cpu_has_mips_r6) 66962306a36Sopenharmony_ci break; 67062306a36Sopenharmony_ci *contpc = regs->cp0_epc + dec_insn.pc_inc + 67162306a36Sopenharmony_ci dec_insn.next_pc_inc; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci return 1; 67462306a36Sopenharmony_ci case balc6_op: 67562306a36Sopenharmony_ci if (!cpu_has_mips_r6) 67662306a36Sopenharmony_ci break; 67762306a36Sopenharmony_ci regs->regs[31] = regs->cp0_epc + 4; 67862306a36Sopenharmony_ci *contpc = regs->cp0_epc + dec_insn.pc_inc + 67962306a36Sopenharmony_ci dec_insn.next_pc_inc; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci return 1; 68262306a36Sopenharmony_ci case pop66_op: 68362306a36Sopenharmony_ci if (!cpu_has_mips_r6) 68462306a36Sopenharmony_ci break; 68562306a36Sopenharmony_ci *contpc = regs->cp0_epc + dec_insn.pc_inc + 68662306a36Sopenharmony_ci dec_insn.next_pc_inc; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci return 1; 68962306a36Sopenharmony_ci case pop76_op: 69062306a36Sopenharmony_ci if (!cpu_has_mips_r6) 69162306a36Sopenharmony_ci break; 69262306a36Sopenharmony_ci if (!insn.i_format.rs) 69362306a36Sopenharmony_ci regs->regs[31] = regs->cp0_epc + 4; 69462306a36Sopenharmony_ci *contpc = regs->cp0_epc + dec_insn.pc_inc + 69562306a36Sopenharmony_ci dec_insn.next_pc_inc; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci return 1; 69862306a36Sopenharmony_ci#endif 69962306a36Sopenharmony_ci case cop0_op: 70062306a36Sopenharmony_ci case cop1_op: 70162306a36Sopenharmony_ci /* Need to check for R6 bc1nez and bc1eqz branches */ 70262306a36Sopenharmony_ci if (cpu_has_mips_r6 && 70362306a36Sopenharmony_ci ((insn.i_format.rs == bc1eqz_op) || 70462306a36Sopenharmony_ci (insn.i_format.rs == bc1nez_op))) { 70562306a36Sopenharmony_ci bit = 0; 70662306a36Sopenharmony_ci fpr = ¤t->thread.fpu.fpr[insn.i_format.rt]; 70762306a36Sopenharmony_ci bit0 = get_fpr32(fpr, 0) & 0x1; 70862306a36Sopenharmony_ci switch (insn.i_format.rs) { 70962306a36Sopenharmony_ci case bc1eqz_op: 71062306a36Sopenharmony_ci bit = bit0 == 0; 71162306a36Sopenharmony_ci break; 71262306a36Sopenharmony_ci case bc1nez_op: 71362306a36Sopenharmony_ci bit = bit0 != 0; 71462306a36Sopenharmony_ci break; 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci if (bit) 71762306a36Sopenharmony_ci *contpc = regs->cp0_epc + 71862306a36Sopenharmony_ci dec_insn.pc_inc + 71962306a36Sopenharmony_ci (insn.i_format.simmediate << 2); 72062306a36Sopenharmony_ci else 72162306a36Sopenharmony_ci *contpc = regs->cp0_epc + 72262306a36Sopenharmony_ci dec_insn.pc_inc + 72362306a36Sopenharmony_ci dec_insn.next_pc_inc; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci return 1; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci /* R2/R6 compatible cop1 instruction */ 72862306a36Sopenharmony_ci fallthrough; 72962306a36Sopenharmony_ci case cop2_op: 73062306a36Sopenharmony_ci case cop1x_op: 73162306a36Sopenharmony_ci if (insn.i_format.rs == bc_op) { 73262306a36Sopenharmony_ci preempt_disable(); 73362306a36Sopenharmony_ci if (is_fpu_owner()) 73462306a36Sopenharmony_ci fcr31 = read_32bit_cp1_register(CP1_STATUS); 73562306a36Sopenharmony_ci else 73662306a36Sopenharmony_ci fcr31 = current->thread.fpu.fcr31; 73762306a36Sopenharmony_ci preempt_enable(); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci bit = (insn.i_format.rt >> 2); 74062306a36Sopenharmony_ci bit += (bit != 0); 74162306a36Sopenharmony_ci bit += 23; 74262306a36Sopenharmony_ci switch (insn.i_format.rt & 3) { 74362306a36Sopenharmony_ci case 0: /* bc1f */ 74462306a36Sopenharmony_ci case 2: /* bc1fl */ 74562306a36Sopenharmony_ci if (~fcr31 & (1 << bit)) 74662306a36Sopenharmony_ci *contpc = regs->cp0_epc + 74762306a36Sopenharmony_ci dec_insn.pc_inc + 74862306a36Sopenharmony_ci (insn.i_format.simmediate << 2); 74962306a36Sopenharmony_ci else 75062306a36Sopenharmony_ci *contpc = regs->cp0_epc + 75162306a36Sopenharmony_ci dec_insn.pc_inc + 75262306a36Sopenharmony_ci dec_insn.next_pc_inc; 75362306a36Sopenharmony_ci return 1; 75462306a36Sopenharmony_ci case 1: /* bc1t */ 75562306a36Sopenharmony_ci case 3: /* bc1tl */ 75662306a36Sopenharmony_ci if (fcr31 & (1 << bit)) 75762306a36Sopenharmony_ci *contpc = regs->cp0_epc + 75862306a36Sopenharmony_ci dec_insn.pc_inc + 75962306a36Sopenharmony_ci (insn.i_format.simmediate << 2); 76062306a36Sopenharmony_ci else 76162306a36Sopenharmony_ci *contpc = regs->cp0_epc + 76262306a36Sopenharmony_ci dec_insn.pc_inc + 76362306a36Sopenharmony_ci dec_insn.next_pc_inc; 76462306a36Sopenharmony_ci return 1; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci break; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci return 0; 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci/* 77362306a36Sopenharmony_ci * In the Linux kernel, we support selection of FPR format on the 77462306a36Sopenharmony_ci * basis of the Status.FR bit. If an FPU is not present, the FR bit 77562306a36Sopenharmony_ci * is hardwired to zero, which would imply a 32-bit FPU even for 77662306a36Sopenharmony_ci * 64-bit CPUs so we rather look at TIF_32BIT_FPREGS. 77762306a36Sopenharmony_ci * FPU emu is slow and bulky and optimizing this function offers fairly 77862306a36Sopenharmony_ci * sizeable benefits so we try to be clever and make this function return 77962306a36Sopenharmony_ci * a constant whenever possible, that is on 64-bit kernels without O32 78062306a36Sopenharmony_ci * compatibility enabled and on 32-bit without 64-bit FPU support. 78162306a36Sopenharmony_ci */ 78262306a36Sopenharmony_cistatic inline int cop1_64bit(struct pt_regs *xcp) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_64BIT) && !IS_ENABLED(CONFIG_MIPS32_O32)) 78562306a36Sopenharmony_ci return 1; 78662306a36Sopenharmony_ci else if (IS_ENABLED(CONFIG_32BIT) && 78762306a36Sopenharmony_ci !IS_ENABLED(CONFIG_MIPS_O32_FP64_SUPPORT)) 78862306a36Sopenharmony_ci return 0; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci return !test_thread_flag(TIF_32BIT_FPREGS); 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic inline bool hybrid_fprs(void) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci return test_thread_flag(TIF_HYBRID_FPREGS); 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci#define SIFROMREG(si, x) \ 79962306a36Sopenharmony_cido { \ 80062306a36Sopenharmony_ci if (cop1_64bit(xcp) && !hybrid_fprs()) \ 80162306a36Sopenharmony_ci (si) = (int)get_fpr32(&ctx->fpr[x], 0); \ 80262306a36Sopenharmony_ci else \ 80362306a36Sopenharmony_ci (si) = (int)get_fpr32(&ctx->fpr[(x) & ~1], (x) & 1); \ 80462306a36Sopenharmony_ci} while (0) 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci#define SITOREG(si, x) \ 80762306a36Sopenharmony_cido { \ 80862306a36Sopenharmony_ci if (cop1_64bit(xcp) && !hybrid_fprs()) { \ 80962306a36Sopenharmony_ci unsigned int i; \ 81062306a36Sopenharmony_ci set_fpr32(&ctx->fpr[x], 0, si); \ 81162306a36Sopenharmony_ci for (i = 1; i < ARRAY_SIZE(ctx->fpr[x].val32); i++) \ 81262306a36Sopenharmony_ci set_fpr32(&ctx->fpr[x], i, 0); \ 81362306a36Sopenharmony_ci } else { \ 81462306a36Sopenharmony_ci set_fpr32(&ctx->fpr[(x) & ~1], (x) & 1, si); \ 81562306a36Sopenharmony_ci } \ 81662306a36Sopenharmony_ci} while (0) 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci#define SIFROMHREG(si, x) ((si) = (int)get_fpr32(&ctx->fpr[x], 1)) 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci#define SITOHREG(si, x) \ 82162306a36Sopenharmony_cido { \ 82262306a36Sopenharmony_ci unsigned int i; \ 82362306a36Sopenharmony_ci set_fpr32(&ctx->fpr[x], 1, si); \ 82462306a36Sopenharmony_ci for (i = 2; i < ARRAY_SIZE(ctx->fpr[x].val32); i++) \ 82562306a36Sopenharmony_ci set_fpr32(&ctx->fpr[x], i, 0); \ 82662306a36Sopenharmony_ci} while (0) 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci#define DIFROMREG(di, x) \ 82962306a36Sopenharmony_ci ((di) = get_fpr64(&ctx->fpr[(x) & ~(cop1_64bit(xcp) ^ 1)], 0)) 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci#define DITOREG(di, x) \ 83262306a36Sopenharmony_cido { \ 83362306a36Sopenharmony_ci unsigned int fpr, i; \ 83462306a36Sopenharmony_ci fpr = (x) & ~(cop1_64bit(xcp) ^ 1); \ 83562306a36Sopenharmony_ci set_fpr64(&ctx->fpr[fpr], 0, di); \ 83662306a36Sopenharmony_ci for (i = 1; i < ARRAY_SIZE(ctx->fpr[x].val64); i++) \ 83762306a36Sopenharmony_ci set_fpr64(&ctx->fpr[fpr], i, 0); \ 83862306a36Sopenharmony_ci} while (0) 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci#define SPFROMREG(sp, x) SIFROMREG((sp).bits, x) 84162306a36Sopenharmony_ci#define SPTOREG(sp, x) SITOREG((sp).bits, x) 84262306a36Sopenharmony_ci#define DPFROMREG(dp, x) DIFROMREG((dp).bits, x) 84362306a36Sopenharmony_ci#define DPTOREG(dp, x) DITOREG((dp).bits, x) 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci/* 84662306a36Sopenharmony_ci * Emulate a CFC1 instruction. 84762306a36Sopenharmony_ci */ 84862306a36Sopenharmony_cistatic inline void cop1_cfc(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 84962306a36Sopenharmony_ci mips_instruction ir) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci u32 fcr31 = ctx->fcr31; 85262306a36Sopenharmony_ci u32 value = 0; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci switch (MIPSInst_RD(ir)) { 85562306a36Sopenharmony_ci case FPCREG_CSR: 85662306a36Sopenharmony_ci value = fcr31; 85762306a36Sopenharmony_ci pr_debug("%p gpr[%d]<-csr=%08x\n", 85862306a36Sopenharmony_ci (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 85962306a36Sopenharmony_ci break; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci case FPCREG_FENR: 86262306a36Sopenharmony_ci if (!cpu_has_mips_r) 86362306a36Sopenharmony_ci break; 86462306a36Sopenharmony_ci value = (fcr31 >> (FPU_CSR_FS_S - MIPS_FENR_FS_S)) & 86562306a36Sopenharmony_ci MIPS_FENR_FS; 86662306a36Sopenharmony_ci value |= fcr31 & (FPU_CSR_ALL_E | FPU_CSR_RM); 86762306a36Sopenharmony_ci pr_debug("%p gpr[%d]<-enr=%08x\n", 86862306a36Sopenharmony_ci (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 86962306a36Sopenharmony_ci break; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci case FPCREG_FEXR: 87262306a36Sopenharmony_ci if (!cpu_has_mips_r) 87362306a36Sopenharmony_ci break; 87462306a36Sopenharmony_ci value = fcr31 & (FPU_CSR_ALL_X | FPU_CSR_ALL_S); 87562306a36Sopenharmony_ci pr_debug("%p gpr[%d]<-exr=%08x\n", 87662306a36Sopenharmony_ci (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 87762306a36Sopenharmony_ci break; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci case FPCREG_FCCR: 88062306a36Sopenharmony_ci if (!cpu_has_mips_r) 88162306a36Sopenharmony_ci break; 88262306a36Sopenharmony_ci value = (fcr31 >> (FPU_CSR_COND_S - MIPS_FCCR_COND0_S)) & 88362306a36Sopenharmony_ci MIPS_FCCR_COND0; 88462306a36Sopenharmony_ci value |= (fcr31 >> (FPU_CSR_COND1_S - MIPS_FCCR_COND1_S)) & 88562306a36Sopenharmony_ci (MIPS_FCCR_CONDX & ~MIPS_FCCR_COND0); 88662306a36Sopenharmony_ci pr_debug("%p gpr[%d]<-ccr=%08x\n", 88762306a36Sopenharmony_ci (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 88862306a36Sopenharmony_ci break; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci case FPCREG_RID: 89162306a36Sopenharmony_ci value = boot_cpu_data.fpu_id; 89262306a36Sopenharmony_ci break; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci default: 89562306a36Sopenharmony_ci break; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (MIPSInst_RT(ir)) 89962306a36Sopenharmony_ci xcp->regs[MIPSInst_RT(ir)] = value; 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci/* 90362306a36Sopenharmony_ci * Emulate a CTC1 instruction. 90462306a36Sopenharmony_ci */ 90562306a36Sopenharmony_cistatic inline void cop1_ctc(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 90662306a36Sopenharmony_ci mips_instruction ir) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci u32 fcr31 = ctx->fcr31; 90962306a36Sopenharmony_ci u32 value; 91062306a36Sopenharmony_ci u32 mask; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci if (MIPSInst_RT(ir) == 0) 91362306a36Sopenharmony_ci value = 0; 91462306a36Sopenharmony_ci else 91562306a36Sopenharmony_ci value = xcp->regs[MIPSInst_RT(ir)]; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci switch (MIPSInst_RD(ir)) { 91862306a36Sopenharmony_ci case FPCREG_CSR: 91962306a36Sopenharmony_ci pr_debug("%p gpr[%d]->csr=%08x\n", 92062306a36Sopenharmony_ci (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci /* Preserve read-only bits. */ 92362306a36Sopenharmony_ci mask = boot_cpu_data.fpu_msk31; 92462306a36Sopenharmony_ci fcr31 = (value & ~mask) | (fcr31 & mask); 92562306a36Sopenharmony_ci break; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci case FPCREG_FENR: 92862306a36Sopenharmony_ci if (!cpu_has_mips_r) 92962306a36Sopenharmony_ci break; 93062306a36Sopenharmony_ci pr_debug("%p gpr[%d]->enr=%08x\n", 93162306a36Sopenharmony_ci (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 93262306a36Sopenharmony_ci fcr31 &= ~(FPU_CSR_FS | FPU_CSR_ALL_E | FPU_CSR_RM); 93362306a36Sopenharmony_ci fcr31 |= (value << (FPU_CSR_FS_S - MIPS_FENR_FS_S)) & 93462306a36Sopenharmony_ci FPU_CSR_FS; 93562306a36Sopenharmony_ci fcr31 |= value & (FPU_CSR_ALL_E | FPU_CSR_RM); 93662306a36Sopenharmony_ci break; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci case FPCREG_FEXR: 93962306a36Sopenharmony_ci if (!cpu_has_mips_r) 94062306a36Sopenharmony_ci break; 94162306a36Sopenharmony_ci pr_debug("%p gpr[%d]->exr=%08x\n", 94262306a36Sopenharmony_ci (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 94362306a36Sopenharmony_ci fcr31 &= ~(FPU_CSR_ALL_X | FPU_CSR_ALL_S); 94462306a36Sopenharmony_ci fcr31 |= value & (FPU_CSR_ALL_X | FPU_CSR_ALL_S); 94562306a36Sopenharmony_ci break; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci case FPCREG_FCCR: 94862306a36Sopenharmony_ci if (!cpu_has_mips_r) 94962306a36Sopenharmony_ci break; 95062306a36Sopenharmony_ci pr_debug("%p gpr[%d]->ccr=%08x\n", 95162306a36Sopenharmony_ci (void *)xcp->cp0_epc, MIPSInst_RT(ir), value); 95262306a36Sopenharmony_ci fcr31 &= ~(FPU_CSR_CONDX | FPU_CSR_COND); 95362306a36Sopenharmony_ci fcr31 |= (value << (FPU_CSR_COND_S - MIPS_FCCR_COND0_S)) & 95462306a36Sopenharmony_ci FPU_CSR_COND; 95562306a36Sopenharmony_ci fcr31 |= (value << (FPU_CSR_COND1_S - MIPS_FCCR_COND1_S)) & 95662306a36Sopenharmony_ci FPU_CSR_CONDX; 95762306a36Sopenharmony_ci break; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci default: 96062306a36Sopenharmony_ci break; 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci ctx->fcr31 = fcr31; 96462306a36Sopenharmony_ci} 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci/* 96762306a36Sopenharmony_ci * Emulate the single floating point instruction pointed at by EPC. 96862306a36Sopenharmony_ci * Two instructions if the instruction is in a branch delay slot. 96962306a36Sopenharmony_ci */ 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cistatic int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 97262306a36Sopenharmony_ci struct mm_decoded_insn dec_insn, void __user **fault_addr) 97362306a36Sopenharmony_ci{ 97462306a36Sopenharmony_ci unsigned long contpc = xcp->cp0_epc + dec_insn.pc_inc; 97562306a36Sopenharmony_ci unsigned int cond, cbit, bit0; 97662306a36Sopenharmony_ci mips_instruction ir; 97762306a36Sopenharmony_ci int likely, pc_inc; 97862306a36Sopenharmony_ci union fpureg *fpr; 97962306a36Sopenharmony_ci u32 __user *wva; 98062306a36Sopenharmony_ci u64 __user *dva; 98162306a36Sopenharmony_ci u32 wval; 98262306a36Sopenharmony_ci u64 dval; 98362306a36Sopenharmony_ci int sig; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci /* 98662306a36Sopenharmony_ci * These are giving gcc a gentle hint about what to expect in 98762306a36Sopenharmony_ci * dec_inst in order to do better optimization. 98862306a36Sopenharmony_ci */ 98962306a36Sopenharmony_ci if (!cpu_has_mmips && dec_insn.micro_mips_mode) 99062306a36Sopenharmony_ci unreachable(); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci /* XXX NEC Vr54xx bug workaround */ 99362306a36Sopenharmony_ci if (delay_slot(xcp)) { 99462306a36Sopenharmony_ci if (dec_insn.micro_mips_mode) { 99562306a36Sopenharmony_ci if (!mm_isBranchInstr(xcp, dec_insn, &contpc)) 99662306a36Sopenharmony_ci clear_delay_slot(xcp); 99762306a36Sopenharmony_ci } else { 99862306a36Sopenharmony_ci if (!isBranchInstr(xcp, dec_insn, &contpc)) 99962306a36Sopenharmony_ci clear_delay_slot(xcp); 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci if (delay_slot(xcp)) { 100462306a36Sopenharmony_ci /* 100562306a36Sopenharmony_ci * The instruction to be emulated is in a branch delay slot 100662306a36Sopenharmony_ci * which means that we have to emulate the branch instruction 100762306a36Sopenharmony_ci * BEFORE we do the cop1 instruction. 100862306a36Sopenharmony_ci * 100962306a36Sopenharmony_ci * This branch could be a COP1 branch, but in that case we 101062306a36Sopenharmony_ci * would have had a trap for that instruction, and would not 101162306a36Sopenharmony_ci * come through this route. 101262306a36Sopenharmony_ci * 101362306a36Sopenharmony_ci * Linux MIPS branch emulator operates on context, updating the 101462306a36Sopenharmony_ci * cp0_epc. 101562306a36Sopenharmony_ci */ 101662306a36Sopenharmony_ci ir = dec_insn.next_insn; /* process delay slot instr */ 101762306a36Sopenharmony_ci pc_inc = dec_insn.next_pc_inc; 101862306a36Sopenharmony_ci } else { 101962306a36Sopenharmony_ci ir = dec_insn.insn; /* process current instr */ 102062306a36Sopenharmony_ci pc_inc = dec_insn.pc_inc; 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci /* 102462306a36Sopenharmony_ci * Since microMIPS FPU instructios are a subset of MIPS32 FPU 102562306a36Sopenharmony_ci * instructions, we want to convert microMIPS FPU instructions 102662306a36Sopenharmony_ci * into MIPS32 instructions so that we could reuse all of the 102762306a36Sopenharmony_ci * FPU emulation code. 102862306a36Sopenharmony_ci * 102962306a36Sopenharmony_ci * NOTE: We cannot do this for branch instructions since they 103062306a36Sopenharmony_ci * are not a subset. Example: Cannot emulate a 16-bit 103162306a36Sopenharmony_ci * aligned target address with a MIPS32 instruction. 103262306a36Sopenharmony_ci */ 103362306a36Sopenharmony_ci if (dec_insn.micro_mips_mode) { 103462306a36Sopenharmony_ci /* 103562306a36Sopenharmony_ci * If next instruction is a 16-bit instruction, then 103662306a36Sopenharmony_ci * it cannot be a FPU instruction. This could happen 103762306a36Sopenharmony_ci * since we can be called for non-FPU instructions. 103862306a36Sopenharmony_ci */ 103962306a36Sopenharmony_ci if ((pc_inc == 2) || 104062306a36Sopenharmony_ci (microMIPS32_to_MIPS32((union mips_instruction *)&ir) 104162306a36Sopenharmony_ci == SIGILL)) 104262306a36Sopenharmony_ci return SIGILL; 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ciemul: 104662306a36Sopenharmony_ci perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, xcp, 0); 104762306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(emulated); 104862306a36Sopenharmony_ci switch (MIPSInst_OPCODE(ir)) { 104962306a36Sopenharmony_ci case ldc1_op: 105062306a36Sopenharmony_ci dva = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] + 105162306a36Sopenharmony_ci MIPSInst_SIMM(ir)); 105262306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(loads); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci if (!access_ok(dva, sizeof(u64))) { 105562306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(errors); 105662306a36Sopenharmony_ci *fault_addr = dva; 105762306a36Sopenharmony_ci return SIGBUS; 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci if (__get_user(dval, dva)) { 106062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(errors); 106162306a36Sopenharmony_ci *fault_addr = dva; 106262306a36Sopenharmony_ci return SIGSEGV; 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci DITOREG(dval, MIPSInst_RT(ir)); 106562306a36Sopenharmony_ci break; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci case sdc1_op: 106862306a36Sopenharmony_ci dva = (u64 __user *) (xcp->regs[MIPSInst_RS(ir)] + 106962306a36Sopenharmony_ci MIPSInst_SIMM(ir)); 107062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(stores); 107162306a36Sopenharmony_ci DIFROMREG(dval, MIPSInst_RT(ir)); 107262306a36Sopenharmony_ci if (!access_ok(dva, sizeof(u64))) { 107362306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(errors); 107462306a36Sopenharmony_ci *fault_addr = dva; 107562306a36Sopenharmony_ci return SIGBUS; 107662306a36Sopenharmony_ci } 107762306a36Sopenharmony_ci if (__put_user(dval, dva)) { 107862306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(errors); 107962306a36Sopenharmony_ci *fault_addr = dva; 108062306a36Sopenharmony_ci return SIGSEGV; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci break; 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci case lwc1_op: 108562306a36Sopenharmony_ci wva = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] + 108662306a36Sopenharmony_ci MIPSInst_SIMM(ir)); 108762306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(loads); 108862306a36Sopenharmony_ci if (!access_ok(wva, sizeof(u32))) { 108962306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(errors); 109062306a36Sopenharmony_ci *fault_addr = wva; 109162306a36Sopenharmony_ci return SIGBUS; 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci if (__get_user(wval, wva)) { 109462306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(errors); 109562306a36Sopenharmony_ci *fault_addr = wva; 109662306a36Sopenharmony_ci return SIGSEGV; 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci SITOREG(wval, MIPSInst_RT(ir)); 109962306a36Sopenharmony_ci break; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci case swc1_op: 110262306a36Sopenharmony_ci wva = (u32 __user *) (xcp->regs[MIPSInst_RS(ir)] + 110362306a36Sopenharmony_ci MIPSInst_SIMM(ir)); 110462306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(stores); 110562306a36Sopenharmony_ci SIFROMREG(wval, MIPSInst_RT(ir)); 110662306a36Sopenharmony_ci if (!access_ok(wva, sizeof(u32))) { 110762306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(errors); 110862306a36Sopenharmony_ci *fault_addr = wva; 110962306a36Sopenharmony_ci return SIGBUS; 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci if (__put_user(wval, wva)) { 111262306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(errors); 111362306a36Sopenharmony_ci *fault_addr = wva; 111462306a36Sopenharmony_ci return SIGSEGV; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci break; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci case cop1_op: 111962306a36Sopenharmony_ci switch (MIPSInst_RS(ir)) { 112062306a36Sopenharmony_ci case dmfc_op: 112162306a36Sopenharmony_ci if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) 112262306a36Sopenharmony_ci return SIGILL; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci /* copregister fs -> gpr[rt] */ 112562306a36Sopenharmony_ci if (MIPSInst_RT(ir) != 0) { 112662306a36Sopenharmony_ci DIFROMREG(xcp->regs[MIPSInst_RT(ir)], 112762306a36Sopenharmony_ci MIPSInst_RD(ir)); 112862306a36Sopenharmony_ci } 112962306a36Sopenharmony_ci break; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci case dmtc_op: 113262306a36Sopenharmony_ci if (!cpu_has_mips_3_4_5 && !cpu_has_mips64) 113362306a36Sopenharmony_ci return SIGILL; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci /* copregister fs <- rt */ 113662306a36Sopenharmony_ci DITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); 113762306a36Sopenharmony_ci break; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci case mfhc_op: 114062306a36Sopenharmony_ci if (!cpu_has_mips_r2_r6) 114162306a36Sopenharmony_ci return SIGILL; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci /* copregister rd -> gpr[rt] */ 114462306a36Sopenharmony_ci if (MIPSInst_RT(ir) != 0) { 114562306a36Sopenharmony_ci SIFROMHREG(xcp->regs[MIPSInst_RT(ir)], 114662306a36Sopenharmony_ci MIPSInst_RD(ir)); 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci break; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci case mthc_op: 115162306a36Sopenharmony_ci if (!cpu_has_mips_r2_r6) 115262306a36Sopenharmony_ci return SIGILL; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci /* copregister rd <- gpr[rt] */ 115562306a36Sopenharmony_ci SITOHREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); 115662306a36Sopenharmony_ci break; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci case mfc_op: 115962306a36Sopenharmony_ci /* copregister rd -> gpr[rt] */ 116062306a36Sopenharmony_ci if (MIPSInst_RT(ir) != 0) { 116162306a36Sopenharmony_ci SIFROMREG(xcp->regs[MIPSInst_RT(ir)], 116262306a36Sopenharmony_ci MIPSInst_RD(ir)); 116362306a36Sopenharmony_ci } 116462306a36Sopenharmony_ci break; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci case mtc_op: 116762306a36Sopenharmony_ci /* copregister rd <- rt */ 116862306a36Sopenharmony_ci SITOREG(xcp->regs[MIPSInst_RT(ir)], MIPSInst_RD(ir)); 116962306a36Sopenharmony_ci break; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci case cfc_op: 117262306a36Sopenharmony_ci /* cop control register rd -> gpr[rt] */ 117362306a36Sopenharmony_ci cop1_cfc(xcp, ctx, ir); 117462306a36Sopenharmony_ci break; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci case ctc_op: 117762306a36Sopenharmony_ci /* copregister rd <- rt */ 117862306a36Sopenharmony_ci cop1_ctc(xcp, ctx, ir); 117962306a36Sopenharmony_ci if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 118062306a36Sopenharmony_ci return SIGFPE; 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci break; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci case bc1eqz_op: 118562306a36Sopenharmony_ci case bc1nez_op: 118662306a36Sopenharmony_ci if (!cpu_has_mips_r6 || delay_slot(xcp)) 118762306a36Sopenharmony_ci return SIGILL; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci likely = 0; 119062306a36Sopenharmony_ci cond = 0; 119162306a36Sopenharmony_ci fpr = ¤t->thread.fpu.fpr[MIPSInst_RT(ir)]; 119262306a36Sopenharmony_ci bit0 = get_fpr32(fpr, 0) & 0x1; 119362306a36Sopenharmony_ci switch (MIPSInst_RS(ir)) { 119462306a36Sopenharmony_ci case bc1eqz_op: 119562306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(bc1eqz); 119662306a36Sopenharmony_ci cond = bit0 == 0; 119762306a36Sopenharmony_ci break; 119862306a36Sopenharmony_ci case bc1nez_op: 119962306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(bc1nez); 120062306a36Sopenharmony_ci cond = bit0 != 0; 120162306a36Sopenharmony_ci break; 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci goto branch_common; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci case bc_op: 120662306a36Sopenharmony_ci if (delay_slot(xcp)) 120762306a36Sopenharmony_ci return SIGILL; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci if (cpu_has_mips_4_5_r) 121062306a36Sopenharmony_ci cbit = fpucondbit[MIPSInst_RT(ir) >> 2]; 121162306a36Sopenharmony_ci else 121262306a36Sopenharmony_ci cbit = FPU_CSR_COND; 121362306a36Sopenharmony_ci cond = ctx->fcr31 & cbit; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci likely = 0; 121662306a36Sopenharmony_ci switch (MIPSInst_RT(ir) & 3) { 121762306a36Sopenharmony_ci case bcfl_op: 121862306a36Sopenharmony_ci if (cpu_has_mips_2_3_4_5_r) 121962306a36Sopenharmony_ci likely = 1; 122062306a36Sopenharmony_ci fallthrough; 122162306a36Sopenharmony_ci case bcf_op: 122262306a36Sopenharmony_ci cond = !cond; 122362306a36Sopenharmony_ci break; 122462306a36Sopenharmony_ci case bctl_op: 122562306a36Sopenharmony_ci if (cpu_has_mips_2_3_4_5_r) 122662306a36Sopenharmony_ci likely = 1; 122762306a36Sopenharmony_ci fallthrough; 122862306a36Sopenharmony_ci case bct_op: 122962306a36Sopenharmony_ci break; 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_cibranch_common: 123262306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(branches); 123362306a36Sopenharmony_ci set_delay_slot(xcp); 123462306a36Sopenharmony_ci if (cond) { 123562306a36Sopenharmony_ci /* 123662306a36Sopenharmony_ci * Branch taken: emulate dslot instruction 123762306a36Sopenharmony_ci */ 123862306a36Sopenharmony_ci unsigned long bcpc; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci /* 124162306a36Sopenharmony_ci * Remember EPC at the branch to point back 124262306a36Sopenharmony_ci * at so that any delay-slot instruction 124362306a36Sopenharmony_ci * signal is not silently ignored. 124462306a36Sopenharmony_ci */ 124562306a36Sopenharmony_ci bcpc = xcp->cp0_epc; 124662306a36Sopenharmony_ci xcp->cp0_epc += dec_insn.pc_inc; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci contpc = MIPSInst_SIMM(ir); 124962306a36Sopenharmony_ci ir = dec_insn.next_insn; 125062306a36Sopenharmony_ci if (dec_insn.micro_mips_mode) { 125162306a36Sopenharmony_ci contpc = (xcp->cp0_epc + (contpc << 1)); 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci /* If 16-bit instruction, not FPU. */ 125462306a36Sopenharmony_ci if ((dec_insn.next_pc_inc == 2) || 125562306a36Sopenharmony_ci (microMIPS32_to_MIPS32((union mips_instruction *)&ir) == SIGILL)) { 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci /* 125862306a36Sopenharmony_ci * Since this instruction will 125962306a36Sopenharmony_ci * be put on the stack with 126062306a36Sopenharmony_ci * 32-bit words, get around 126162306a36Sopenharmony_ci * this problem by putting a 126262306a36Sopenharmony_ci * NOP16 as the second one. 126362306a36Sopenharmony_ci */ 126462306a36Sopenharmony_ci if (dec_insn.next_pc_inc == 2) 126562306a36Sopenharmony_ci ir = (ir & (~0xffff)) | MM_NOP16; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci /* 126862306a36Sopenharmony_ci * Single step the non-CP1 126962306a36Sopenharmony_ci * instruction in the dslot. 127062306a36Sopenharmony_ci */ 127162306a36Sopenharmony_ci sig = mips_dsemul(xcp, ir, 127262306a36Sopenharmony_ci bcpc, contpc); 127362306a36Sopenharmony_ci if (sig < 0) 127462306a36Sopenharmony_ci break; 127562306a36Sopenharmony_ci if (sig) 127662306a36Sopenharmony_ci xcp->cp0_epc = bcpc; 127762306a36Sopenharmony_ci /* 127862306a36Sopenharmony_ci * SIGILL forces out of 127962306a36Sopenharmony_ci * the emulation loop. 128062306a36Sopenharmony_ci */ 128162306a36Sopenharmony_ci return sig ? sig : SIGILL; 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci } else 128462306a36Sopenharmony_ci contpc = (xcp->cp0_epc + (contpc << 2)); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci switch (MIPSInst_OPCODE(ir)) { 128762306a36Sopenharmony_ci case lwc1_op: 128862306a36Sopenharmony_ci case swc1_op: 128962306a36Sopenharmony_ci goto emul; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci case ldc1_op: 129262306a36Sopenharmony_ci case sdc1_op: 129362306a36Sopenharmony_ci if (cpu_has_mips_2_3_4_5_r) 129462306a36Sopenharmony_ci goto emul; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci goto bc_sigill; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci case cop1_op: 129962306a36Sopenharmony_ci goto emul; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci case cop1x_op: 130262306a36Sopenharmony_ci if (cpu_has_mips_4_5_64_r2_r6) 130362306a36Sopenharmony_ci /* its one of ours */ 130462306a36Sopenharmony_ci goto emul; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci goto bc_sigill; 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci case spec_op: 130962306a36Sopenharmony_ci switch (MIPSInst_FUNC(ir)) { 131062306a36Sopenharmony_ci case movc_op: 131162306a36Sopenharmony_ci if (cpu_has_mips_4_5_r) 131262306a36Sopenharmony_ci goto emul; 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci goto bc_sigill; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci break; 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci bc_sigill: 131962306a36Sopenharmony_ci xcp->cp0_epc = bcpc; 132062306a36Sopenharmony_ci return SIGILL; 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci /* 132462306a36Sopenharmony_ci * Single step the non-cp1 132562306a36Sopenharmony_ci * instruction in the dslot 132662306a36Sopenharmony_ci */ 132762306a36Sopenharmony_ci sig = mips_dsemul(xcp, ir, bcpc, contpc); 132862306a36Sopenharmony_ci if (sig < 0) 132962306a36Sopenharmony_ci break; 133062306a36Sopenharmony_ci if (sig) 133162306a36Sopenharmony_ci xcp->cp0_epc = bcpc; 133262306a36Sopenharmony_ci /* SIGILL forces out of the emulation loop. */ 133362306a36Sopenharmony_ci return sig ? sig : SIGILL; 133462306a36Sopenharmony_ci } else if (likely) { /* branch not taken */ 133562306a36Sopenharmony_ci /* 133662306a36Sopenharmony_ci * branch likely nullifies 133762306a36Sopenharmony_ci * dslot if not taken 133862306a36Sopenharmony_ci */ 133962306a36Sopenharmony_ci xcp->cp0_epc += dec_insn.pc_inc; 134062306a36Sopenharmony_ci contpc += dec_insn.pc_inc; 134162306a36Sopenharmony_ci /* 134262306a36Sopenharmony_ci * else continue & execute 134362306a36Sopenharmony_ci * dslot as normal insn 134462306a36Sopenharmony_ci */ 134562306a36Sopenharmony_ci } 134662306a36Sopenharmony_ci break; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci default: 134962306a36Sopenharmony_ci if (!(MIPSInst_RS(ir) & 0x10)) 135062306a36Sopenharmony_ci return SIGILL; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci /* a real fpu computation instruction */ 135362306a36Sopenharmony_ci sig = fpu_emu(xcp, ctx, ir); 135462306a36Sopenharmony_ci if (sig) 135562306a36Sopenharmony_ci return sig; 135662306a36Sopenharmony_ci } 135762306a36Sopenharmony_ci break; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci case cop1x_op: 136062306a36Sopenharmony_ci if (!cpu_has_mips_4_5_64_r2_r6) 136162306a36Sopenharmony_ci return SIGILL; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci sig = fpux_emu(xcp, ctx, ir, fault_addr); 136462306a36Sopenharmony_ci if (sig) 136562306a36Sopenharmony_ci return sig; 136662306a36Sopenharmony_ci break; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci case spec_op: 136962306a36Sopenharmony_ci if (!cpu_has_mips_4_5_r) 137062306a36Sopenharmony_ci return SIGILL; 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci if (MIPSInst_FUNC(ir) != movc_op) 137362306a36Sopenharmony_ci return SIGILL; 137462306a36Sopenharmony_ci cond = fpucondbit[MIPSInst_RT(ir) >> 2]; 137562306a36Sopenharmony_ci if (((ctx->fcr31 & cond) != 0) == ((MIPSInst_RT(ir) & 1) != 0)) 137662306a36Sopenharmony_ci xcp->regs[MIPSInst_RD(ir)] = 137762306a36Sopenharmony_ci xcp->regs[MIPSInst_RS(ir)]; 137862306a36Sopenharmony_ci break; 137962306a36Sopenharmony_ci default: 138062306a36Sopenharmony_ci return SIGILL; 138162306a36Sopenharmony_ci } 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci /* we did it !! */ 138462306a36Sopenharmony_ci xcp->cp0_epc = contpc; 138562306a36Sopenharmony_ci clear_delay_slot(xcp); 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci return 0; 138862306a36Sopenharmony_ci} 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci/* 139162306a36Sopenharmony_ci * Conversion table from MIPS compare ops 48-63 139262306a36Sopenharmony_ci * cond = ieee754dp_cmp(x,y,IEEE754_UN,sig); 139362306a36Sopenharmony_ci */ 139462306a36Sopenharmony_cistatic const unsigned char cmptab[8] = { 139562306a36Sopenharmony_ci 0, /* cmp_0 (sig) cmp_sf */ 139662306a36Sopenharmony_ci IEEE754_CUN, /* cmp_un (sig) cmp_ngle */ 139762306a36Sopenharmony_ci IEEE754_CEQ, /* cmp_eq (sig) cmp_seq */ 139862306a36Sopenharmony_ci IEEE754_CEQ | IEEE754_CUN, /* cmp_ueq (sig) cmp_ngl */ 139962306a36Sopenharmony_ci IEEE754_CLT, /* cmp_olt (sig) cmp_lt */ 140062306a36Sopenharmony_ci IEEE754_CLT | IEEE754_CUN, /* cmp_ult (sig) cmp_nge */ 140162306a36Sopenharmony_ci IEEE754_CLT | IEEE754_CEQ, /* cmp_ole (sig) cmp_le */ 140262306a36Sopenharmony_ci IEEE754_CLT | IEEE754_CEQ | IEEE754_CUN, /* cmp_ule (sig) cmp_ngt */ 140362306a36Sopenharmony_ci}; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_cistatic const unsigned char negative_cmptab[8] = { 140662306a36Sopenharmony_ci 0, /* Reserved */ 140762306a36Sopenharmony_ci IEEE754_CLT | IEEE754_CGT | IEEE754_CEQ, 140862306a36Sopenharmony_ci IEEE754_CLT | IEEE754_CGT | IEEE754_CUN, 140962306a36Sopenharmony_ci IEEE754_CLT | IEEE754_CGT, 141062306a36Sopenharmony_ci /* Reserved */ 141162306a36Sopenharmony_ci}; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci/* 141562306a36Sopenharmony_ci * Additional MIPS4 instructions 141662306a36Sopenharmony_ci */ 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci#define DEF3OP(name, p, f1, f2, f3) \ 141962306a36Sopenharmony_cistatic union ieee754##p fpemu_##p##_##name(union ieee754##p r, \ 142062306a36Sopenharmony_ci union ieee754##p s, union ieee754##p t) \ 142162306a36Sopenharmony_ci{ \ 142262306a36Sopenharmony_ci struct _ieee754_csr ieee754_csr_save; \ 142362306a36Sopenharmony_ci s = f1(s, t); \ 142462306a36Sopenharmony_ci ieee754_csr_save = ieee754_csr; \ 142562306a36Sopenharmony_ci s = f2(s, r); \ 142662306a36Sopenharmony_ci ieee754_csr_save.cx |= ieee754_csr.cx; \ 142762306a36Sopenharmony_ci ieee754_csr_save.sx |= ieee754_csr.sx; \ 142862306a36Sopenharmony_ci s = f3(s); \ 142962306a36Sopenharmony_ci ieee754_csr.cx |= ieee754_csr_save.cx; \ 143062306a36Sopenharmony_ci ieee754_csr.sx |= ieee754_csr_save.sx; \ 143162306a36Sopenharmony_ci return s; \ 143262306a36Sopenharmony_ci} 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_cistatic union ieee754dp fpemu_dp_recip(union ieee754dp d) 143562306a36Sopenharmony_ci{ 143662306a36Sopenharmony_ci return ieee754dp_div(ieee754dp_one(0), d); 143762306a36Sopenharmony_ci} 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_cistatic union ieee754dp fpemu_dp_rsqrt(union ieee754dp d) 144062306a36Sopenharmony_ci{ 144162306a36Sopenharmony_ci return ieee754dp_div(ieee754dp_one(0), ieee754dp_sqrt(d)); 144262306a36Sopenharmony_ci} 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_cistatic union ieee754sp fpemu_sp_recip(union ieee754sp s) 144562306a36Sopenharmony_ci{ 144662306a36Sopenharmony_ci return ieee754sp_div(ieee754sp_one(0), s); 144762306a36Sopenharmony_ci} 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_cistatic union ieee754sp fpemu_sp_rsqrt(union ieee754sp s) 145062306a36Sopenharmony_ci{ 145162306a36Sopenharmony_ci return ieee754sp_div(ieee754sp_one(0), ieee754sp_sqrt(s)); 145262306a36Sopenharmony_ci} 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ciDEF3OP(madd, sp, ieee754sp_mul, ieee754sp_add, ); 145562306a36Sopenharmony_ciDEF3OP(msub, sp, ieee754sp_mul, ieee754sp_sub, ); 145662306a36Sopenharmony_ciDEF3OP(nmadd, sp, ieee754sp_mul, ieee754sp_add, ieee754sp_neg); 145762306a36Sopenharmony_ciDEF3OP(nmsub, sp, ieee754sp_mul, ieee754sp_sub, ieee754sp_neg); 145862306a36Sopenharmony_ciDEF3OP(madd, dp, ieee754dp_mul, ieee754dp_add, ); 145962306a36Sopenharmony_ciDEF3OP(msub, dp, ieee754dp_mul, ieee754dp_sub, ); 146062306a36Sopenharmony_ciDEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg); 146162306a36Sopenharmony_ciDEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg); 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_cistatic int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 146462306a36Sopenharmony_ci mips_instruction ir, void __user **fault_addr) 146562306a36Sopenharmony_ci{ 146662306a36Sopenharmony_ci unsigned int rcsr = 0; /* resulting csr */ 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cp1xops); 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci switch (MIPSInst_FMA_FFMT(ir)) { 147162306a36Sopenharmony_ci case s_fmt:{ /* 0 */ 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci union ieee754sp(*handler) (union ieee754sp, union ieee754sp, union ieee754sp); 147462306a36Sopenharmony_ci union ieee754sp fd, fr, fs, ft; 147562306a36Sopenharmony_ci u32 __user *va; 147662306a36Sopenharmony_ci u32 val; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci switch (MIPSInst_FUNC(ir)) { 147962306a36Sopenharmony_ci case lwxc1_op: 148062306a36Sopenharmony_ci va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 148162306a36Sopenharmony_ci xcp->regs[MIPSInst_FT(ir)]); 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(loads); 148462306a36Sopenharmony_ci if (!access_ok(va, sizeof(u32))) { 148562306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(errors); 148662306a36Sopenharmony_ci *fault_addr = va; 148762306a36Sopenharmony_ci return SIGBUS; 148862306a36Sopenharmony_ci } 148962306a36Sopenharmony_ci if (__get_user(val, va)) { 149062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(errors); 149162306a36Sopenharmony_ci *fault_addr = va; 149262306a36Sopenharmony_ci return SIGSEGV; 149362306a36Sopenharmony_ci } 149462306a36Sopenharmony_ci SITOREG(val, MIPSInst_FD(ir)); 149562306a36Sopenharmony_ci break; 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci case swxc1_op: 149862306a36Sopenharmony_ci va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 149962306a36Sopenharmony_ci xcp->regs[MIPSInst_FT(ir)]); 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(stores); 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci SIFROMREG(val, MIPSInst_FS(ir)); 150462306a36Sopenharmony_ci if (!access_ok(va, sizeof(u32))) { 150562306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(errors); 150662306a36Sopenharmony_ci *fault_addr = va; 150762306a36Sopenharmony_ci return SIGBUS; 150862306a36Sopenharmony_ci } 150962306a36Sopenharmony_ci if (put_user(val, va)) { 151062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(errors); 151162306a36Sopenharmony_ci *fault_addr = va; 151262306a36Sopenharmony_ci return SIGSEGV; 151362306a36Sopenharmony_ci } 151462306a36Sopenharmony_ci break; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci case madd_s_op: 151762306a36Sopenharmony_ci if (cpu_has_mac2008_only) 151862306a36Sopenharmony_ci handler = ieee754sp_madd; 151962306a36Sopenharmony_ci else 152062306a36Sopenharmony_ci handler = fpemu_sp_madd; 152162306a36Sopenharmony_ci goto scoptop; 152262306a36Sopenharmony_ci case msub_s_op: 152362306a36Sopenharmony_ci if (cpu_has_mac2008_only) 152462306a36Sopenharmony_ci handler = ieee754sp_msub; 152562306a36Sopenharmony_ci else 152662306a36Sopenharmony_ci handler = fpemu_sp_msub; 152762306a36Sopenharmony_ci goto scoptop; 152862306a36Sopenharmony_ci case nmadd_s_op: 152962306a36Sopenharmony_ci if (cpu_has_mac2008_only) 153062306a36Sopenharmony_ci handler = ieee754sp_nmadd; 153162306a36Sopenharmony_ci else 153262306a36Sopenharmony_ci handler = fpemu_sp_nmadd; 153362306a36Sopenharmony_ci goto scoptop; 153462306a36Sopenharmony_ci case nmsub_s_op: 153562306a36Sopenharmony_ci if (cpu_has_mac2008_only) 153662306a36Sopenharmony_ci handler = ieee754sp_nmsub; 153762306a36Sopenharmony_ci else 153862306a36Sopenharmony_ci handler = fpemu_sp_nmsub; 153962306a36Sopenharmony_ci goto scoptop; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci scoptop: 154262306a36Sopenharmony_ci SPFROMREG(fr, MIPSInst_FR(ir)); 154362306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 154462306a36Sopenharmony_ci SPFROMREG(ft, MIPSInst_FT(ir)); 154562306a36Sopenharmony_ci fd = (*handler) (fr, fs, ft); 154662306a36Sopenharmony_ci SPTOREG(fd, MIPSInst_FD(ir)); 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci copcsr: 154962306a36Sopenharmony_ci if (ieee754_cxtest(IEEE754_INEXACT)) { 155062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(ieee754_inexact); 155162306a36Sopenharmony_ci rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S; 155262306a36Sopenharmony_ci } 155362306a36Sopenharmony_ci if (ieee754_cxtest(IEEE754_UNDERFLOW)) { 155462306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(ieee754_underflow); 155562306a36Sopenharmony_ci rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S; 155662306a36Sopenharmony_ci } 155762306a36Sopenharmony_ci if (ieee754_cxtest(IEEE754_OVERFLOW)) { 155862306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(ieee754_overflow); 155962306a36Sopenharmony_ci rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S; 156062306a36Sopenharmony_ci } 156162306a36Sopenharmony_ci if (ieee754_cxtest(IEEE754_INVALID_OPERATION)) { 156262306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(ieee754_invalidop); 156362306a36Sopenharmony_ci rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S; 156462306a36Sopenharmony_ci } 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr; 156762306a36Sopenharmony_ci if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 156862306a36Sopenharmony_ci /*printk ("SIGFPE: FPU csr = %08x\n", 156962306a36Sopenharmony_ci ctx->fcr31); */ 157062306a36Sopenharmony_ci return SIGFPE; 157162306a36Sopenharmony_ci } 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci break; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci default: 157662306a36Sopenharmony_ci return SIGILL; 157762306a36Sopenharmony_ci } 157862306a36Sopenharmony_ci break; 157962306a36Sopenharmony_ci } 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci case d_fmt:{ /* 1 */ 158262306a36Sopenharmony_ci union ieee754dp(*handler) (union ieee754dp, union ieee754dp, union ieee754dp); 158362306a36Sopenharmony_ci union ieee754dp fd, fr, fs, ft; 158462306a36Sopenharmony_ci u64 __user *va; 158562306a36Sopenharmony_ci u64 val; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci switch (MIPSInst_FUNC(ir)) { 158862306a36Sopenharmony_ci case ldxc1_op: 158962306a36Sopenharmony_ci va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 159062306a36Sopenharmony_ci xcp->regs[MIPSInst_FT(ir)]); 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(loads); 159362306a36Sopenharmony_ci if (!access_ok(va, sizeof(u64))) { 159462306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(errors); 159562306a36Sopenharmony_ci *fault_addr = va; 159662306a36Sopenharmony_ci return SIGBUS; 159762306a36Sopenharmony_ci } 159862306a36Sopenharmony_ci if (__get_user(val, va)) { 159962306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(errors); 160062306a36Sopenharmony_ci *fault_addr = va; 160162306a36Sopenharmony_ci return SIGSEGV; 160262306a36Sopenharmony_ci } 160362306a36Sopenharmony_ci DITOREG(val, MIPSInst_FD(ir)); 160462306a36Sopenharmony_ci break; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci case sdxc1_op: 160762306a36Sopenharmony_ci va = (void __user *) (xcp->regs[MIPSInst_FR(ir)] + 160862306a36Sopenharmony_ci xcp->regs[MIPSInst_FT(ir)]); 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(stores); 161162306a36Sopenharmony_ci DIFROMREG(val, MIPSInst_FS(ir)); 161262306a36Sopenharmony_ci if (!access_ok(va, sizeof(u64))) { 161362306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(errors); 161462306a36Sopenharmony_ci *fault_addr = va; 161562306a36Sopenharmony_ci return SIGBUS; 161662306a36Sopenharmony_ci } 161762306a36Sopenharmony_ci if (__put_user(val, va)) { 161862306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(errors); 161962306a36Sopenharmony_ci *fault_addr = va; 162062306a36Sopenharmony_ci return SIGSEGV; 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci break; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci case madd_d_op: 162562306a36Sopenharmony_ci if (cpu_has_mac2008_only) 162662306a36Sopenharmony_ci handler = ieee754dp_madd; 162762306a36Sopenharmony_ci else 162862306a36Sopenharmony_ci handler = fpemu_dp_madd; 162962306a36Sopenharmony_ci goto dcoptop; 163062306a36Sopenharmony_ci case msub_d_op: 163162306a36Sopenharmony_ci if (cpu_has_mac2008_only) 163262306a36Sopenharmony_ci handler = ieee754dp_msub; 163362306a36Sopenharmony_ci else 163462306a36Sopenharmony_ci handler = fpemu_dp_msub; 163562306a36Sopenharmony_ci goto dcoptop; 163662306a36Sopenharmony_ci case nmadd_d_op: 163762306a36Sopenharmony_ci if (cpu_has_mac2008_only) 163862306a36Sopenharmony_ci handler = ieee754dp_nmadd; 163962306a36Sopenharmony_ci else 164062306a36Sopenharmony_ci handler = fpemu_dp_nmadd; 164162306a36Sopenharmony_ci goto dcoptop; 164262306a36Sopenharmony_ci case nmsub_d_op: 164362306a36Sopenharmony_ci if (cpu_has_mac2008_only) 164462306a36Sopenharmony_ci handler = ieee754dp_nmsub; 164562306a36Sopenharmony_ci else 164662306a36Sopenharmony_ci handler = fpemu_dp_nmsub; 164762306a36Sopenharmony_ci goto dcoptop; 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci dcoptop: 165062306a36Sopenharmony_ci DPFROMREG(fr, MIPSInst_FR(ir)); 165162306a36Sopenharmony_ci DPFROMREG(fs, MIPSInst_FS(ir)); 165262306a36Sopenharmony_ci DPFROMREG(ft, MIPSInst_FT(ir)); 165362306a36Sopenharmony_ci fd = (*handler) (fr, fs, ft); 165462306a36Sopenharmony_ci DPTOREG(fd, MIPSInst_FD(ir)); 165562306a36Sopenharmony_ci goto copcsr; 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci default: 165862306a36Sopenharmony_ci return SIGILL; 165962306a36Sopenharmony_ci } 166062306a36Sopenharmony_ci break; 166162306a36Sopenharmony_ci } 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci case 0x3: 166462306a36Sopenharmony_ci if (MIPSInst_FUNC(ir) != pfetch_op) 166562306a36Sopenharmony_ci return SIGILL; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci /* ignore prefx operation */ 166862306a36Sopenharmony_ci break; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci default: 167162306a36Sopenharmony_ci return SIGILL; 167262306a36Sopenharmony_ci } 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci return 0; 167562306a36Sopenharmony_ci} 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci/* 168062306a36Sopenharmony_ci * Emulate a single COP1 arithmetic instruction. 168162306a36Sopenharmony_ci */ 168262306a36Sopenharmony_cistatic int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 168362306a36Sopenharmony_ci mips_instruction ir) 168462306a36Sopenharmony_ci{ 168562306a36Sopenharmony_ci int rfmt; /* resulting format */ 168662306a36Sopenharmony_ci unsigned int rcsr = 0; /* resulting csr */ 168762306a36Sopenharmony_ci unsigned int oldrm; 168862306a36Sopenharmony_ci unsigned int cbit; 168962306a36Sopenharmony_ci unsigned int cond; 169062306a36Sopenharmony_ci union { 169162306a36Sopenharmony_ci union ieee754dp d; 169262306a36Sopenharmony_ci union ieee754sp s; 169362306a36Sopenharmony_ci int w; 169462306a36Sopenharmony_ci s64 l; 169562306a36Sopenharmony_ci } rv; /* resulting value */ 169662306a36Sopenharmony_ci u64 bits; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cp1ops); 169962306a36Sopenharmony_ci switch (rfmt = (MIPSInst_FFMT(ir) & 0xf)) { 170062306a36Sopenharmony_ci case s_fmt: { /* 0 */ 170162306a36Sopenharmony_ci union { 170262306a36Sopenharmony_ci union ieee754sp(*b) (union ieee754sp, union ieee754sp); 170362306a36Sopenharmony_ci union ieee754sp(*u) (union ieee754sp); 170462306a36Sopenharmony_ci } handler; 170562306a36Sopenharmony_ci union ieee754sp fd, fs, ft; 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci switch (MIPSInst_FUNC(ir)) { 170862306a36Sopenharmony_ci /* binary ops */ 170962306a36Sopenharmony_ci case fadd_op: 171062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(add_s); 171162306a36Sopenharmony_ci handler.b = ieee754sp_add; 171262306a36Sopenharmony_ci goto scopbop; 171362306a36Sopenharmony_ci case fsub_op: 171462306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(sub_s); 171562306a36Sopenharmony_ci handler.b = ieee754sp_sub; 171662306a36Sopenharmony_ci goto scopbop; 171762306a36Sopenharmony_ci case fmul_op: 171862306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(mul_s); 171962306a36Sopenharmony_ci handler.b = ieee754sp_mul; 172062306a36Sopenharmony_ci goto scopbop; 172162306a36Sopenharmony_ci case fdiv_op: 172262306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(div_s); 172362306a36Sopenharmony_ci handler.b = ieee754sp_div; 172462306a36Sopenharmony_ci goto scopbop; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci /* unary ops */ 172762306a36Sopenharmony_ci case fsqrt_op: 172862306a36Sopenharmony_ci if (!cpu_has_mips_2_3_4_5_r) 172962306a36Sopenharmony_ci return SIGILL; 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(sqrt_s); 173262306a36Sopenharmony_ci handler.u = ieee754sp_sqrt; 173362306a36Sopenharmony_ci goto scopuop; 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci /* 173662306a36Sopenharmony_ci * Note that on some MIPS IV implementations such as the 173762306a36Sopenharmony_ci * R5000 and R8000 the FSQRT and FRECIP instructions do not 173862306a36Sopenharmony_ci * achieve full IEEE-754 accuracy - however this emulator does. 173962306a36Sopenharmony_ci */ 174062306a36Sopenharmony_ci case frsqrt_op: 174162306a36Sopenharmony_ci if (!cpu_has_mips_4_5_64_r2_r6) 174262306a36Sopenharmony_ci return SIGILL; 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(rsqrt_s); 174562306a36Sopenharmony_ci handler.u = fpemu_sp_rsqrt; 174662306a36Sopenharmony_ci goto scopuop; 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci case frecip_op: 174962306a36Sopenharmony_ci if (!cpu_has_mips_4_5_64_r2_r6) 175062306a36Sopenharmony_ci return SIGILL; 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(recip_s); 175362306a36Sopenharmony_ci handler.u = fpemu_sp_recip; 175462306a36Sopenharmony_ci goto scopuop; 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci case fmovc_op: 175762306a36Sopenharmony_ci if (!cpu_has_mips_4_5_r) 175862306a36Sopenharmony_ci return SIGILL; 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci cond = fpucondbit[MIPSInst_FT(ir) >> 2]; 176162306a36Sopenharmony_ci if (((ctx->fcr31 & cond) != 0) != 176262306a36Sopenharmony_ci ((MIPSInst_FT(ir) & 1) != 0)) 176362306a36Sopenharmony_ci return 0; 176462306a36Sopenharmony_ci SPFROMREG(rv.s, MIPSInst_FS(ir)); 176562306a36Sopenharmony_ci break; 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci case fmovz_op: 176862306a36Sopenharmony_ci if (!cpu_has_mips_4_5_r) 176962306a36Sopenharmony_ci return SIGILL; 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci if (xcp->regs[MIPSInst_FT(ir)] != 0) 177262306a36Sopenharmony_ci return 0; 177362306a36Sopenharmony_ci SPFROMREG(rv.s, MIPSInst_FS(ir)); 177462306a36Sopenharmony_ci break; 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci case fmovn_op: 177762306a36Sopenharmony_ci if (!cpu_has_mips_4_5_r) 177862306a36Sopenharmony_ci return SIGILL; 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci if (xcp->regs[MIPSInst_FT(ir)] == 0) 178162306a36Sopenharmony_ci return 0; 178262306a36Sopenharmony_ci SPFROMREG(rv.s, MIPSInst_FS(ir)); 178362306a36Sopenharmony_ci break; 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci case fseleqz_op: 178662306a36Sopenharmony_ci if (!cpu_has_mips_r6) 178762306a36Sopenharmony_ci return SIGILL; 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(seleqz_s); 179062306a36Sopenharmony_ci SPFROMREG(rv.s, MIPSInst_FT(ir)); 179162306a36Sopenharmony_ci if (rv.w & 0x1) 179262306a36Sopenharmony_ci rv.w = 0; 179362306a36Sopenharmony_ci else 179462306a36Sopenharmony_ci SPFROMREG(rv.s, MIPSInst_FS(ir)); 179562306a36Sopenharmony_ci break; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci case fselnez_op: 179862306a36Sopenharmony_ci if (!cpu_has_mips_r6) 179962306a36Sopenharmony_ci return SIGILL; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(selnez_s); 180262306a36Sopenharmony_ci SPFROMREG(rv.s, MIPSInst_FT(ir)); 180362306a36Sopenharmony_ci if (rv.w & 0x1) 180462306a36Sopenharmony_ci SPFROMREG(rv.s, MIPSInst_FS(ir)); 180562306a36Sopenharmony_ci else 180662306a36Sopenharmony_ci rv.w = 0; 180762306a36Sopenharmony_ci break; 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci case fmaddf_op: { 181062306a36Sopenharmony_ci union ieee754sp ft, fs, fd; 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci if (!cpu_has_mips_r6) 181362306a36Sopenharmony_ci return SIGILL; 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(maddf_s); 181662306a36Sopenharmony_ci SPFROMREG(ft, MIPSInst_FT(ir)); 181762306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 181862306a36Sopenharmony_ci SPFROMREG(fd, MIPSInst_FD(ir)); 181962306a36Sopenharmony_ci rv.s = ieee754sp_maddf(fd, fs, ft); 182062306a36Sopenharmony_ci goto copcsr; 182162306a36Sopenharmony_ci } 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci case fmsubf_op: { 182462306a36Sopenharmony_ci union ieee754sp ft, fs, fd; 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci if (!cpu_has_mips_r6) 182762306a36Sopenharmony_ci return SIGILL; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(msubf_s); 183062306a36Sopenharmony_ci SPFROMREG(ft, MIPSInst_FT(ir)); 183162306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 183262306a36Sopenharmony_ci SPFROMREG(fd, MIPSInst_FD(ir)); 183362306a36Sopenharmony_ci rv.s = ieee754sp_msubf(fd, fs, ft); 183462306a36Sopenharmony_ci goto copcsr; 183562306a36Sopenharmony_ci } 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci case frint_op: { 183862306a36Sopenharmony_ci union ieee754sp fs; 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci if (!cpu_has_mips_r6) 184162306a36Sopenharmony_ci return SIGILL; 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(rint_s); 184462306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 184562306a36Sopenharmony_ci rv.s = ieee754sp_rint(fs); 184662306a36Sopenharmony_ci goto copcsr; 184762306a36Sopenharmony_ci } 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci case fclass_op: { 185062306a36Sopenharmony_ci union ieee754sp fs; 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci if (!cpu_has_mips_r6) 185362306a36Sopenharmony_ci return SIGILL; 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(class_s); 185662306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 185762306a36Sopenharmony_ci rv.w = ieee754sp_2008class(fs); 185862306a36Sopenharmony_ci rfmt = w_fmt; 185962306a36Sopenharmony_ci goto copcsr; 186062306a36Sopenharmony_ci } 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci case fmin_op: { 186362306a36Sopenharmony_ci union ieee754sp fs, ft; 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci if (!cpu_has_mips_r6) 186662306a36Sopenharmony_ci return SIGILL; 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(min_s); 186962306a36Sopenharmony_ci SPFROMREG(ft, MIPSInst_FT(ir)); 187062306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 187162306a36Sopenharmony_ci rv.s = ieee754sp_fmin(fs, ft); 187262306a36Sopenharmony_ci goto copcsr; 187362306a36Sopenharmony_ci } 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci case fmina_op: { 187662306a36Sopenharmony_ci union ieee754sp fs, ft; 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci if (!cpu_has_mips_r6) 187962306a36Sopenharmony_ci return SIGILL; 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(mina_s); 188262306a36Sopenharmony_ci SPFROMREG(ft, MIPSInst_FT(ir)); 188362306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 188462306a36Sopenharmony_ci rv.s = ieee754sp_fmina(fs, ft); 188562306a36Sopenharmony_ci goto copcsr; 188662306a36Sopenharmony_ci } 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci case fmax_op: { 188962306a36Sopenharmony_ci union ieee754sp fs, ft; 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci if (!cpu_has_mips_r6) 189262306a36Sopenharmony_ci return SIGILL; 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(max_s); 189562306a36Sopenharmony_ci SPFROMREG(ft, MIPSInst_FT(ir)); 189662306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 189762306a36Sopenharmony_ci rv.s = ieee754sp_fmax(fs, ft); 189862306a36Sopenharmony_ci goto copcsr; 189962306a36Sopenharmony_ci } 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci case fmaxa_op: { 190262306a36Sopenharmony_ci union ieee754sp fs, ft; 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci if (!cpu_has_mips_r6) 190562306a36Sopenharmony_ci return SIGILL; 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(maxa_s); 190862306a36Sopenharmony_ci SPFROMREG(ft, MIPSInst_FT(ir)); 190962306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 191062306a36Sopenharmony_ci rv.s = ieee754sp_fmaxa(fs, ft); 191162306a36Sopenharmony_ci goto copcsr; 191262306a36Sopenharmony_ci } 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci case fabs_op: 191562306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(abs_s); 191662306a36Sopenharmony_ci handler.u = ieee754sp_abs; 191762306a36Sopenharmony_ci goto scopuop; 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci case fneg_op: 192062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(neg_s); 192162306a36Sopenharmony_ci handler.u = ieee754sp_neg; 192262306a36Sopenharmony_ci goto scopuop; 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci case fmov_op: 192562306a36Sopenharmony_ci /* an easy one */ 192662306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(mov_s); 192762306a36Sopenharmony_ci SPFROMREG(rv.s, MIPSInst_FS(ir)); 192862306a36Sopenharmony_ci goto copcsr; 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci /* binary op on handler */ 193162306a36Sopenharmony_ciscopbop: 193262306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 193362306a36Sopenharmony_ci SPFROMREG(ft, MIPSInst_FT(ir)); 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci rv.s = (*handler.b) (fs, ft); 193662306a36Sopenharmony_ci goto copcsr; 193762306a36Sopenharmony_ciscopuop: 193862306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 193962306a36Sopenharmony_ci rv.s = (*handler.u) (fs); 194062306a36Sopenharmony_ci goto copcsr; 194162306a36Sopenharmony_cicopcsr: 194262306a36Sopenharmony_ci if (ieee754_cxtest(IEEE754_INEXACT)) { 194362306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(ieee754_inexact); 194462306a36Sopenharmony_ci rcsr |= FPU_CSR_INE_X | FPU_CSR_INE_S; 194562306a36Sopenharmony_ci } 194662306a36Sopenharmony_ci if (ieee754_cxtest(IEEE754_UNDERFLOW)) { 194762306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(ieee754_underflow); 194862306a36Sopenharmony_ci rcsr |= FPU_CSR_UDF_X | FPU_CSR_UDF_S; 194962306a36Sopenharmony_ci } 195062306a36Sopenharmony_ci if (ieee754_cxtest(IEEE754_OVERFLOW)) { 195162306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(ieee754_overflow); 195262306a36Sopenharmony_ci rcsr |= FPU_CSR_OVF_X | FPU_CSR_OVF_S; 195362306a36Sopenharmony_ci } 195462306a36Sopenharmony_ci if (ieee754_cxtest(IEEE754_ZERO_DIVIDE)) { 195562306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(ieee754_zerodiv); 195662306a36Sopenharmony_ci rcsr |= FPU_CSR_DIV_X | FPU_CSR_DIV_S; 195762306a36Sopenharmony_ci } 195862306a36Sopenharmony_ci if (ieee754_cxtest(IEEE754_INVALID_OPERATION)) { 195962306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(ieee754_invalidop); 196062306a36Sopenharmony_ci rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S; 196162306a36Sopenharmony_ci } 196262306a36Sopenharmony_ci break; 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci /* unary conv ops */ 196562306a36Sopenharmony_ci case fcvts_op: 196662306a36Sopenharmony_ci return SIGILL; /* not defined */ 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci case fcvtd_op: 196962306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cvt_d_s); 197062306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 197162306a36Sopenharmony_ci rv.d = ieee754dp_fsp(fs); 197262306a36Sopenharmony_ci rfmt = d_fmt; 197362306a36Sopenharmony_ci goto copcsr; 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci case fcvtw_op: 197662306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cvt_w_s); 197762306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 197862306a36Sopenharmony_ci rv.w = ieee754sp_tint(fs); 197962306a36Sopenharmony_ci rfmt = w_fmt; 198062306a36Sopenharmony_ci goto copcsr; 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci case fround_op: 198362306a36Sopenharmony_ci case ftrunc_op: 198462306a36Sopenharmony_ci case fceil_op: 198562306a36Sopenharmony_ci case ffloor_op: 198662306a36Sopenharmony_ci if (!cpu_has_mips_2_3_4_5_r) 198762306a36Sopenharmony_ci return SIGILL; 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci if (MIPSInst_FUNC(ir) == fceil_op) 199062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(ceil_w_s); 199162306a36Sopenharmony_ci if (MIPSInst_FUNC(ir) == ffloor_op) 199262306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(floor_w_s); 199362306a36Sopenharmony_ci if (MIPSInst_FUNC(ir) == fround_op) 199462306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(round_w_s); 199562306a36Sopenharmony_ci if (MIPSInst_FUNC(ir) == ftrunc_op) 199662306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(trunc_w_s); 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci oldrm = ieee754_csr.rm; 199962306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 200062306a36Sopenharmony_ci ieee754_csr.rm = MIPSInst_FUNC(ir); 200162306a36Sopenharmony_ci rv.w = ieee754sp_tint(fs); 200262306a36Sopenharmony_ci ieee754_csr.rm = oldrm; 200362306a36Sopenharmony_ci rfmt = w_fmt; 200462306a36Sopenharmony_ci goto copcsr; 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci case fsel_op: 200762306a36Sopenharmony_ci if (!cpu_has_mips_r6) 200862306a36Sopenharmony_ci return SIGILL; 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(sel_s); 201162306a36Sopenharmony_ci SPFROMREG(fd, MIPSInst_FD(ir)); 201262306a36Sopenharmony_ci if (fd.bits & 0x1) 201362306a36Sopenharmony_ci SPFROMREG(rv.s, MIPSInst_FT(ir)); 201462306a36Sopenharmony_ci else 201562306a36Sopenharmony_ci SPFROMREG(rv.s, MIPSInst_FS(ir)); 201662306a36Sopenharmony_ci break; 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci case fcvtl_op: 201962306a36Sopenharmony_ci if (!cpu_has_mips_3_4_5_64_r2_r6) 202062306a36Sopenharmony_ci return SIGILL; 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cvt_l_s); 202362306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 202462306a36Sopenharmony_ci rv.l = ieee754sp_tlong(fs); 202562306a36Sopenharmony_ci rfmt = l_fmt; 202662306a36Sopenharmony_ci goto copcsr; 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci case froundl_op: 202962306a36Sopenharmony_ci case ftruncl_op: 203062306a36Sopenharmony_ci case fceill_op: 203162306a36Sopenharmony_ci case ffloorl_op: 203262306a36Sopenharmony_ci if (!cpu_has_mips_3_4_5_64_r2_r6) 203362306a36Sopenharmony_ci return SIGILL; 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci if (MIPSInst_FUNC(ir) == fceill_op) 203662306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(ceil_l_s); 203762306a36Sopenharmony_ci if (MIPSInst_FUNC(ir) == ffloorl_op) 203862306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(floor_l_s); 203962306a36Sopenharmony_ci if (MIPSInst_FUNC(ir) == froundl_op) 204062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(round_l_s); 204162306a36Sopenharmony_ci if (MIPSInst_FUNC(ir) == ftruncl_op) 204262306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(trunc_l_s); 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci oldrm = ieee754_csr.rm; 204562306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 204662306a36Sopenharmony_ci ieee754_csr.rm = MIPSInst_FUNC(ir); 204762306a36Sopenharmony_ci rv.l = ieee754sp_tlong(fs); 204862306a36Sopenharmony_ci ieee754_csr.rm = oldrm; 204962306a36Sopenharmony_ci rfmt = l_fmt; 205062306a36Sopenharmony_ci goto copcsr; 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci default: 205362306a36Sopenharmony_ci if (!NO_R6EMU && MIPSInst_FUNC(ir) >= fcmp_op) { 205462306a36Sopenharmony_ci unsigned int cmpop; 205562306a36Sopenharmony_ci union ieee754sp fs, ft; 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci cmpop = MIPSInst_FUNC(ir) - fcmp_op; 205862306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 205962306a36Sopenharmony_ci SPFROMREG(ft, MIPSInst_FT(ir)); 206062306a36Sopenharmony_ci rv.w = ieee754sp_cmp(fs, ft, 206162306a36Sopenharmony_ci cmptab[cmpop & 0x7], cmpop & 0x8); 206262306a36Sopenharmony_ci rfmt = -1; 206362306a36Sopenharmony_ci if ((cmpop & 0x8) && ieee754_cxtest 206462306a36Sopenharmony_ci (IEEE754_INVALID_OPERATION)) 206562306a36Sopenharmony_ci rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 206662306a36Sopenharmony_ci else 206762306a36Sopenharmony_ci goto copcsr; 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci } else 207062306a36Sopenharmony_ci return SIGILL; 207162306a36Sopenharmony_ci break; 207262306a36Sopenharmony_ci } 207362306a36Sopenharmony_ci break; 207462306a36Sopenharmony_ci } 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci case d_fmt: { 207762306a36Sopenharmony_ci union ieee754dp fd, fs, ft; 207862306a36Sopenharmony_ci union { 207962306a36Sopenharmony_ci union ieee754dp(*b) (union ieee754dp, union ieee754dp); 208062306a36Sopenharmony_ci union ieee754dp(*u) (union ieee754dp); 208162306a36Sopenharmony_ci } handler; 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci switch (MIPSInst_FUNC(ir)) { 208462306a36Sopenharmony_ci /* binary ops */ 208562306a36Sopenharmony_ci case fadd_op: 208662306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(add_d); 208762306a36Sopenharmony_ci handler.b = ieee754dp_add; 208862306a36Sopenharmony_ci goto dcopbop; 208962306a36Sopenharmony_ci case fsub_op: 209062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(sub_d); 209162306a36Sopenharmony_ci handler.b = ieee754dp_sub; 209262306a36Sopenharmony_ci goto dcopbop; 209362306a36Sopenharmony_ci case fmul_op: 209462306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(mul_d); 209562306a36Sopenharmony_ci handler.b = ieee754dp_mul; 209662306a36Sopenharmony_ci goto dcopbop; 209762306a36Sopenharmony_ci case fdiv_op: 209862306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(div_d); 209962306a36Sopenharmony_ci handler.b = ieee754dp_div; 210062306a36Sopenharmony_ci goto dcopbop; 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci /* unary ops */ 210362306a36Sopenharmony_ci case fsqrt_op: 210462306a36Sopenharmony_ci if (!cpu_has_mips_2_3_4_5_r) 210562306a36Sopenharmony_ci return SIGILL; 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(sqrt_d); 210862306a36Sopenharmony_ci handler.u = ieee754dp_sqrt; 210962306a36Sopenharmony_ci goto dcopuop; 211062306a36Sopenharmony_ci /* 211162306a36Sopenharmony_ci * Note that on some MIPS IV implementations such as the 211262306a36Sopenharmony_ci * R5000 and R8000 the FSQRT and FRECIP instructions do not 211362306a36Sopenharmony_ci * achieve full IEEE-754 accuracy - however this emulator does. 211462306a36Sopenharmony_ci */ 211562306a36Sopenharmony_ci case frsqrt_op: 211662306a36Sopenharmony_ci if (!cpu_has_mips_4_5_64_r2_r6) 211762306a36Sopenharmony_ci return SIGILL; 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(rsqrt_d); 212062306a36Sopenharmony_ci handler.u = fpemu_dp_rsqrt; 212162306a36Sopenharmony_ci goto dcopuop; 212262306a36Sopenharmony_ci case frecip_op: 212362306a36Sopenharmony_ci if (!cpu_has_mips_4_5_64_r2_r6) 212462306a36Sopenharmony_ci return SIGILL; 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(recip_d); 212762306a36Sopenharmony_ci handler.u = fpemu_dp_recip; 212862306a36Sopenharmony_ci goto dcopuop; 212962306a36Sopenharmony_ci case fmovc_op: 213062306a36Sopenharmony_ci if (!cpu_has_mips_4_5_r) 213162306a36Sopenharmony_ci return SIGILL; 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci cond = fpucondbit[MIPSInst_FT(ir) >> 2]; 213462306a36Sopenharmony_ci if (((ctx->fcr31 & cond) != 0) != 213562306a36Sopenharmony_ci ((MIPSInst_FT(ir) & 1) != 0)) 213662306a36Sopenharmony_ci return 0; 213762306a36Sopenharmony_ci DPFROMREG(rv.d, MIPSInst_FS(ir)); 213862306a36Sopenharmony_ci break; 213962306a36Sopenharmony_ci case fmovz_op: 214062306a36Sopenharmony_ci if (!cpu_has_mips_4_5_r) 214162306a36Sopenharmony_ci return SIGILL; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci if (xcp->regs[MIPSInst_FT(ir)] != 0) 214462306a36Sopenharmony_ci return 0; 214562306a36Sopenharmony_ci DPFROMREG(rv.d, MIPSInst_FS(ir)); 214662306a36Sopenharmony_ci break; 214762306a36Sopenharmony_ci case fmovn_op: 214862306a36Sopenharmony_ci if (!cpu_has_mips_4_5_r) 214962306a36Sopenharmony_ci return SIGILL; 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ci if (xcp->regs[MIPSInst_FT(ir)] == 0) 215262306a36Sopenharmony_ci return 0; 215362306a36Sopenharmony_ci DPFROMREG(rv.d, MIPSInst_FS(ir)); 215462306a36Sopenharmony_ci break; 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci case fseleqz_op: 215762306a36Sopenharmony_ci if (!cpu_has_mips_r6) 215862306a36Sopenharmony_ci return SIGILL; 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(seleqz_d); 216162306a36Sopenharmony_ci DPFROMREG(rv.d, MIPSInst_FT(ir)); 216262306a36Sopenharmony_ci if (rv.l & 0x1) 216362306a36Sopenharmony_ci rv.l = 0; 216462306a36Sopenharmony_ci else 216562306a36Sopenharmony_ci DPFROMREG(rv.d, MIPSInst_FS(ir)); 216662306a36Sopenharmony_ci break; 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci case fselnez_op: 216962306a36Sopenharmony_ci if (!cpu_has_mips_r6) 217062306a36Sopenharmony_ci return SIGILL; 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(selnez_d); 217362306a36Sopenharmony_ci DPFROMREG(rv.d, MIPSInst_FT(ir)); 217462306a36Sopenharmony_ci if (rv.l & 0x1) 217562306a36Sopenharmony_ci DPFROMREG(rv.d, MIPSInst_FS(ir)); 217662306a36Sopenharmony_ci else 217762306a36Sopenharmony_ci rv.l = 0; 217862306a36Sopenharmony_ci break; 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci case fmaddf_op: { 218162306a36Sopenharmony_ci union ieee754dp ft, fs, fd; 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci if (!cpu_has_mips_r6) 218462306a36Sopenharmony_ci return SIGILL; 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(maddf_d); 218762306a36Sopenharmony_ci DPFROMREG(ft, MIPSInst_FT(ir)); 218862306a36Sopenharmony_ci DPFROMREG(fs, MIPSInst_FS(ir)); 218962306a36Sopenharmony_ci DPFROMREG(fd, MIPSInst_FD(ir)); 219062306a36Sopenharmony_ci rv.d = ieee754dp_maddf(fd, fs, ft); 219162306a36Sopenharmony_ci goto copcsr; 219262306a36Sopenharmony_ci } 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci case fmsubf_op: { 219562306a36Sopenharmony_ci union ieee754dp ft, fs, fd; 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci if (!cpu_has_mips_r6) 219862306a36Sopenharmony_ci return SIGILL; 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(msubf_d); 220162306a36Sopenharmony_ci DPFROMREG(ft, MIPSInst_FT(ir)); 220262306a36Sopenharmony_ci DPFROMREG(fs, MIPSInst_FS(ir)); 220362306a36Sopenharmony_ci DPFROMREG(fd, MIPSInst_FD(ir)); 220462306a36Sopenharmony_ci rv.d = ieee754dp_msubf(fd, fs, ft); 220562306a36Sopenharmony_ci goto copcsr; 220662306a36Sopenharmony_ci } 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci case frint_op: { 220962306a36Sopenharmony_ci union ieee754dp fs; 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci if (!cpu_has_mips_r6) 221262306a36Sopenharmony_ci return SIGILL; 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(rint_d); 221562306a36Sopenharmony_ci DPFROMREG(fs, MIPSInst_FS(ir)); 221662306a36Sopenharmony_ci rv.d = ieee754dp_rint(fs); 221762306a36Sopenharmony_ci goto copcsr; 221862306a36Sopenharmony_ci } 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci case fclass_op: { 222162306a36Sopenharmony_ci union ieee754dp fs; 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci if (!cpu_has_mips_r6) 222462306a36Sopenharmony_ci return SIGILL; 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(class_d); 222762306a36Sopenharmony_ci DPFROMREG(fs, MIPSInst_FS(ir)); 222862306a36Sopenharmony_ci rv.l = ieee754dp_2008class(fs); 222962306a36Sopenharmony_ci rfmt = l_fmt; 223062306a36Sopenharmony_ci goto copcsr; 223162306a36Sopenharmony_ci } 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_ci case fmin_op: { 223462306a36Sopenharmony_ci union ieee754dp fs, ft; 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci if (!cpu_has_mips_r6) 223762306a36Sopenharmony_ci return SIGILL; 223862306a36Sopenharmony_ci 223962306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(min_d); 224062306a36Sopenharmony_ci DPFROMREG(ft, MIPSInst_FT(ir)); 224162306a36Sopenharmony_ci DPFROMREG(fs, MIPSInst_FS(ir)); 224262306a36Sopenharmony_ci rv.d = ieee754dp_fmin(fs, ft); 224362306a36Sopenharmony_ci goto copcsr; 224462306a36Sopenharmony_ci } 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci case fmina_op: { 224762306a36Sopenharmony_ci union ieee754dp fs, ft; 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci if (!cpu_has_mips_r6) 225062306a36Sopenharmony_ci return SIGILL; 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(mina_d); 225362306a36Sopenharmony_ci DPFROMREG(ft, MIPSInst_FT(ir)); 225462306a36Sopenharmony_ci DPFROMREG(fs, MIPSInst_FS(ir)); 225562306a36Sopenharmony_ci rv.d = ieee754dp_fmina(fs, ft); 225662306a36Sopenharmony_ci goto copcsr; 225762306a36Sopenharmony_ci } 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci case fmax_op: { 226062306a36Sopenharmony_ci union ieee754dp fs, ft; 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci if (!cpu_has_mips_r6) 226362306a36Sopenharmony_ci return SIGILL; 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(max_d); 226662306a36Sopenharmony_ci DPFROMREG(ft, MIPSInst_FT(ir)); 226762306a36Sopenharmony_ci DPFROMREG(fs, MIPSInst_FS(ir)); 226862306a36Sopenharmony_ci rv.d = ieee754dp_fmax(fs, ft); 226962306a36Sopenharmony_ci goto copcsr; 227062306a36Sopenharmony_ci } 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci case fmaxa_op: { 227362306a36Sopenharmony_ci union ieee754dp fs, ft; 227462306a36Sopenharmony_ci 227562306a36Sopenharmony_ci if (!cpu_has_mips_r6) 227662306a36Sopenharmony_ci return SIGILL; 227762306a36Sopenharmony_ci 227862306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(maxa_d); 227962306a36Sopenharmony_ci DPFROMREG(ft, MIPSInst_FT(ir)); 228062306a36Sopenharmony_ci DPFROMREG(fs, MIPSInst_FS(ir)); 228162306a36Sopenharmony_ci rv.d = ieee754dp_fmaxa(fs, ft); 228262306a36Sopenharmony_ci goto copcsr; 228362306a36Sopenharmony_ci } 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_ci case fabs_op: 228662306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(abs_d); 228762306a36Sopenharmony_ci handler.u = ieee754dp_abs; 228862306a36Sopenharmony_ci goto dcopuop; 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci case fneg_op: 229162306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(neg_d); 229262306a36Sopenharmony_ci handler.u = ieee754dp_neg; 229362306a36Sopenharmony_ci goto dcopuop; 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci case fmov_op: 229662306a36Sopenharmony_ci /* an easy one */ 229762306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(mov_d); 229862306a36Sopenharmony_ci DPFROMREG(rv.d, MIPSInst_FS(ir)); 229962306a36Sopenharmony_ci goto copcsr; 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci /* binary op on handler */ 230262306a36Sopenharmony_cidcopbop: 230362306a36Sopenharmony_ci DPFROMREG(fs, MIPSInst_FS(ir)); 230462306a36Sopenharmony_ci DPFROMREG(ft, MIPSInst_FT(ir)); 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci rv.d = (*handler.b) (fs, ft); 230762306a36Sopenharmony_ci goto copcsr; 230862306a36Sopenharmony_cidcopuop: 230962306a36Sopenharmony_ci DPFROMREG(fs, MIPSInst_FS(ir)); 231062306a36Sopenharmony_ci rv.d = (*handler.u) (fs); 231162306a36Sopenharmony_ci goto copcsr; 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci /* 231462306a36Sopenharmony_ci * unary conv ops 231562306a36Sopenharmony_ci */ 231662306a36Sopenharmony_ci case fcvts_op: 231762306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cvt_s_d); 231862306a36Sopenharmony_ci DPFROMREG(fs, MIPSInst_FS(ir)); 231962306a36Sopenharmony_ci rv.s = ieee754sp_fdp(fs); 232062306a36Sopenharmony_ci rfmt = s_fmt; 232162306a36Sopenharmony_ci goto copcsr; 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci case fcvtd_op: 232462306a36Sopenharmony_ci return SIGILL; /* not defined */ 232562306a36Sopenharmony_ci 232662306a36Sopenharmony_ci case fcvtw_op: 232762306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cvt_w_d); 232862306a36Sopenharmony_ci DPFROMREG(fs, MIPSInst_FS(ir)); 232962306a36Sopenharmony_ci rv.w = ieee754dp_tint(fs); /* wrong */ 233062306a36Sopenharmony_ci rfmt = w_fmt; 233162306a36Sopenharmony_ci goto copcsr; 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_ci case fround_op: 233462306a36Sopenharmony_ci case ftrunc_op: 233562306a36Sopenharmony_ci case fceil_op: 233662306a36Sopenharmony_ci case ffloor_op: 233762306a36Sopenharmony_ci if (!cpu_has_mips_2_3_4_5_r) 233862306a36Sopenharmony_ci return SIGILL; 233962306a36Sopenharmony_ci 234062306a36Sopenharmony_ci if (MIPSInst_FUNC(ir) == fceil_op) 234162306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(ceil_w_d); 234262306a36Sopenharmony_ci if (MIPSInst_FUNC(ir) == ffloor_op) 234362306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(floor_w_d); 234462306a36Sopenharmony_ci if (MIPSInst_FUNC(ir) == fround_op) 234562306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(round_w_d); 234662306a36Sopenharmony_ci if (MIPSInst_FUNC(ir) == ftrunc_op) 234762306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(trunc_w_d); 234862306a36Sopenharmony_ci 234962306a36Sopenharmony_ci oldrm = ieee754_csr.rm; 235062306a36Sopenharmony_ci DPFROMREG(fs, MIPSInst_FS(ir)); 235162306a36Sopenharmony_ci ieee754_csr.rm = MIPSInst_FUNC(ir); 235262306a36Sopenharmony_ci rv.w = ieee754dp_tint(fs); 235362306a36Sopenharmony_ci ieee754_csr.rm = oldrm; 235462306a36Sopenharmony_ci rfmt = w_fmt; 235562306a36Sopenharmony_ci goto copcsr; 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_ci case fsel_op: 235862306a36Sopenharmony_ci if (!cpu_has_mips_r6) 235962306a36Sopenharmony_ci return SIGILL; 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(sel_d); 236262306a36Sopenharmony_ci DPFROMREG(fd, MIPSInst_FD(ir)); 236362306a36Sopenharmony_ci if (fd.bits & 0x1) 236462306a36Sopenharmony_ci DPFROMREG(rv.d, MIPSInst_FT(ir)); 236562306a36Sopenharmony_ci else 236662306a36Sopenharmony_ci DPFROMREG(rv.d, MIPSInst_FS(ir)); 236762306a36Sopenharmony_ci break; 236862306a36Sopenharmony_ci 236962306a36Sopenharmony_ci case fcvtl_op: 237062306a36Sopenharmony_ci if (!cpu_has_mips_3_4_5_64_r2_r6) 237162306a36Sopenharmony_ci return SIGILL; 237262306a36Sopenharmony_ci 237362306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cvt_l_d); 237462306a36Sopenharmony_ci DPFROMREG(fs, MIPSInst_FS(ir)); 237562306a36Sopenharmony_ci rv.l = ieee754dp_tlong(fs); 237662306a36Sopenharmony_ci rfmt = l_fmt; 237762306a36Sopenharmony_ci goto copcsr; 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_ci case froundl_op: 238062306a36Sopenharmony_ci case ftruncl_op: 238162306a36Sopenharmony_ci case fceill_op: 238262306a36Sopenharmony_ci case ffloorl_op: 238362306a36Sopenharmony_ci if (!cpu_has_mips_3_4_5_64_r2_r6) 238462306a36Sopenharmony_ci return SIGILL; 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci if (MIPSInst_FUNC(ir) == fceill_op) 238762306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(ceil_l_d); 238862306a36Sopenharmony_ci if (MIPSInst_FUNC(ir) == ffloorl_op) 238962306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(floor_l_d); 239062306a36Sopenharmony_ci if (MIPSInst_FUNC(ir) == froundl_op) 239162306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(round_l_d); 239262306a36Sopenharmony_ci if (MIPSInst_FUNC(ir) == ftruncl_op) 239362306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(trunc_l_d); 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci oldrm = ieee754_csr.rm; 239662306a36Sopenharmony_ci DPFROMREG(fs, MIPSInst_FS(ir)); 239762306a36Sopenharmony_ci ieee754_csr.rm = MIPSInst_FUNC(ir); 239862306a36Sopenharmony_ci rv.l = ieee754dp_tlong(fs); 239962306a36Sopenharmony_ci ieee754_csr.rm = oldrm; 240062306a36Sopenharmony_ci rfmt = l_fmt; 240162306a36Sopenharmony_ci goto copcsr; 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci default: 240462306a36Sopenharmony_ci if (!NO_R6EMU && MIPSInst_FUNC(ir) >= fcmp_op) { 240562306a36Sopenharmony_ci unsigned int cmpop; 240662306a36Sopenharmony_ci union ieee754dp fs, ft; 240762306a36Sopenharmony_ci 240862306a36Sopenharmony_ci cmpop = MIPSInst_FUNC(ir) - fcmp_op; 240962306a36Sopenharmony_ci DPFROMREG(fs, MIPSInst_FS(ir)); 241062306a36Sopenharmony_ci DPFROMREG(ft, MIPSInst_FT(ir)); 241162306a36Sopenharmony_ci rv.w = ieee754dp_cmp(fs, ft, 241262306a36Sopenharmony_ci cmptab[cmpop & 0x7], cmpop & 0x8); 241362306a36Sopenharmony_ci rfmt = -1; 241462306a36Sopenharmony_ci if ((cmpop & 0x8) 241562306a36Sopenharmony_ci && 241662306a36Sopenharmony_ci ieee754_cxtest 241762306a36Sopenharmony_ci (IEEE754_INVALID_OPERATION)) 241862306a36Sopenharmony_ci rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 241962306a36Sopenharmony_ci else 242062306a36Sopenharmony_ci goto copcsr; 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci } 242362306a36Sopenharmony_ci else { 242462306a36Sopenharmony_ci return SIGILL; 242562306a36Sopenharmony_ci } 242662306a36Sopenharmony_ci break; 242762306a36Sopenharmony_ci } 242862306a36Sopenharmony_ci break; 242962306a36Sopenharmony_ci } 243062306a36Sopenharmony_ci 243162306a36Sopenharmony_ci case w_fmt: { 243262306a36Sopenharmony_ci union ieee754dp fs; 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci switch (MIPSInst_FUNC(ir)) { 243562306a36Sopenharmony_ci case fcvts_op: 243662306a36Sopenharmony_ci /* convert word to single precision real */ 243762306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cvt_s_w); 243862306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 243962306a36Sopenharmony_ci rv.s = ieee754sp_fint(fs.bits); 244062306a36Sopenharmony_ci rfmt = s_fmt; 244162306a36Sopenharmony_ci goto copcsr; 244262306a36Sopenharmony_ci case fcvtd_op: 244362306a36Sopenharmony_ci /* convert word to double precision real */ 244462306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cvt_d_w); 244562306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 244662306a36Sopenharmony_ci rv.d = ieee754dp_fint(fs.bits); 244762306a36Sopenharmony_ci rfmt = d_fmt; 244862306a36Sopenharmony_ci goto copcsr; 244962306a36Sopenharmony_ci default: { 245062306a36Sopenharmony_ci /* Emulating the new CMP.condn.fmt R6 instruction */ 245162306a36Sopenharmony_ci#define CMPOP_MASK 0x7 245262306a36Sopenharmony_ci#define SIGN_BIT (0x1 << 3) 245362306a36Sopenharmony_ci#define PREDICATE_BIT (0x1 << 4) 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_ci int cmpop = MIPSInst_FUNC(ir) & CMPOP_MASK; 245662306a36Sopenharmony_ci int sig = MIPSInst_FUNC(ir) & SIGN_BIT; 245762306a36Sopenharmony_ci union ieee754sp fs, ft; 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci /* This is an R6 only instruction */ 246062306a36Sopenharmony_ci if (!cpu_has_mips_r6 || 246162306a36Sopenharmony_ci (MIPSInst_FUNC(ir) & 0x20)) 246262306a36Sopenharmony_ci return SIGILL; 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci if (!sig) { 246562306a36Sopenharmony_ci if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { 246662306a36Sopenharmony_ci switch (cmpop) { 246762306a36Sopenharmony_ci case 0: 246862306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_af_s); 246962306a36Sopenharmony_ci break; 247062306a36Sopenharmony_ci case 1: 247162306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_un_s); 247262306a36Sopenharmony_ci break; 247362306a36Sopenharmony_ci case 2: 247462306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_eq_s); 247562306a36Sopenharmony_ci break; 247662306a36Sopenharmony_ci case 3: 247762306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_ueq_s); 247862306a36Sopenharmony_ci break; 247962306a36Sopenharmony_ci case 4: 248062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_lt_s); 248162306a36Sopenharmony_ci break; 248262306a36Sopenharmony_ci case 5: 248362306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_ult_s); 248462306a36Sopenharmony_ci break; 248562306a36Sopenharmony_ci case 6: 248662306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_le_s); 248762306a36Sopenharmony_ci break; 248862306a36Sopenharmony_ci case 7: 248962306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_ule_s); 249062306a36Sopenharmony_ci break; 249162306a36Sopenharmony_ci } 249262306a36Sopenharmony_ci } else { 249362306a36Sopenharmony_ci switch (cmpop) { 249462306a36Sopenharmony_ci case 1: 249562306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_or_s); 249662306a36Sopenharmony_ci break; 249762306a36Sopenharmony_ci case 2: 249862306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_une_s); 249962306a36Sopenharmony_ci break; 250062306a36Sopenharmony_ci case 3: 250162306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_ne_s); 250262306a36Sopenharmony_ci break; 250362306a36Sopenharmony_ci } 250462306a36Sopenharmony_ci } 250562306a36Sopenharmony_ci } else { 250662306a36Sopenharmony_ci if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { 250762306a36Sopenharmony_ci switch (cmpop) { 250862306a36Sopenharmony_ci case 0: 250962306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_saf_s); 251062306a36Sopenharmony_ci break; 251162306a36Sopenharmony_ci case 1: 251262306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_sun_s); 251362306a36Sopenharmony_ci break; 251462306a36Sopenharmony_ci case 2: 251562306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_seq_s); 251662306a36Sopenharmony_ci break; 251762306a36Sopenharmony_ci case 3: 251862306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_sueq_s); 251962306a36Sopenharmony_ci break; 252062306a36Sopenharmony_ci case 4: 252162306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_slt_s); 252262306a36Sopenharmony_ci break; 252362306a36Sopenharmony_ci case 5: 252462306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_sult_s); 252562306a36Sopenharmony_ci break; 252662306a36Sopenharmony_ci case 6: 252762306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_sle_s); 252862306a36Sopenharmony_ci break; 252962306a36Sopenharmony_ci case 7: 253062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_sule_s); 253162306a36Sopenharmony_ci break; 253262306a36Sopenharmony_ci } 253362306a36Sopenharmony_ci } else { 253462306a36Sopenharmony_ci switch (cmpop) { 253562306a36Sopenharmony_ci case 1: 253662306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_sor_s); 253762306a36Sopenharmony_ci break; 253862306a36Sopenharmony_ci case 2: 253962306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_sune_s); 254062306a36Sopenharmony_ci break; 254162306a36Sopenharmony_ci case 3: 254262306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_sne_s); 254362306a36Sopenharmony_ci break; 254462306a36Sopenharmony_ci } 254562306a36Sopenharmony_ci } 254662306a36Sopenharmony_ci } 254762306a36Sopenharmony_ci 254862306a36Sopenharmony_ci /* fmt is w_fmt for single precision so fix it */ 254962306a36Sopenharmony_ci rfmt = s_fmt; 255062306a36Sopenharmony_ci /* default to false */ 255162306a36Sopenharmony_ci rv.w = 0; 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_ci /* CMP.condn.S */ 255462306a36Sopenharmony_ci SPFROMREG(fs, MIPSInst_FS(ir)); 255562306a36Sopenharmony_ci SPFROMREG(ft, MIPSInst_FT(ir)); 255662306a36Sopenharmony_ci 255762306a36Sopenharmony_ci /* positive predicates */ 255862306a36Sopenharmony_ci if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { 255962306a36Sopenharmony_ci if (ieee754sp_cmp(fs, ft, cmptab[cmpop], 256062306a36Sopenharmony_ci sig)) 256162306a36Sopenharmony_ci rv.w = -1; /* true, all 1s */ 256262306a36Sopenharmony_ci if ((sig) && 256362306a36Sopenharmony_ci ieee754_cxtest(IEEE754_INVALID_OPERATION)) 256462306a36Sopenharmony_ci rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 256562306a36Sopenharmony_ci else 256662306a36Sopenharmony_ci goto copcsr; 256762306a36Sopenharmony_ci } else { 256862306a36Sopenharmony_ci /* negative predicates */ 256962306a36Sopenharmony_ci switch (cmpop) { 257062306a36Sopenharmony_ci case 1: 257162306a36Sopenharmony_ci case 2: 257262306a36Sopenharmony_ci case 3: 257362306a36Sopenharmony_ci if (ieee754sp_cmp(fs, ft, 257462306a36Sopenharmony_ci negative_cmptab[cmpop], 257562306a36Sopenharmony_ci sig)) 257662306a36Sopenharmony_ci rv.w = -1; /* true, all 1s */ 257762306a36Sopenharmony_ci if (sig && 257862306a36Sopenharmony_ci ieee754_cxtest(IEEE754_INVALID_OPERATION)) 257962306a36Sopenharmony_ci rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 258062306a36Sopenharmony_ci else 258162306a36Sopenharmony_ci goto copcsr; 258262306a36Sopenharmony_ci break; 258362306a36Sopenharmony_ci default: 258462306a36Sopenharmony_ci /* Reserved R6 ops */ 258562306a36Sopenharmony_ci return SIGILL; 258662306a36Sopenharmony_ci } 258762306a36Sopenharmony_ci } 258862306a36Sopenharmony_ci break; 258962306a36Sopenharmony_ci } 259062306a36Sopenharmony_ci } 259162306a36Sopenharmony_ci break; 259262306a36Sopenharmony_ci } 259362306a36Sopenharmony_ci 259462306a36Sopenharmony_ci case l_fmt: 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_ci if (!cpu_has_mips_3_4_5_64_r2_r6) 259762306a36Sopenharmony_ci return SIGILL; 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_ci DIFROMREG(bits, MIPSInst_FS(ir)); 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ci switch (MIPSInst_FUNC(ir)) { 260262306a36Sopenharmony_ci case fcvts_op: 260362306a36Sopenharmony_ci /* convert long to single precision real */ 260462306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cvt_s_l); 260562306a36Sopenharmony_ci rv.s = ieee754sp_flong(bits); 260662306a36Sopenharmony_ci rfmt = s_fmt; 260762306a36Sopenharmony_ci goto copcsr; 260862306a36Sopenharmony_ci case fcvtd_op: 260962306a36Sopenharmony_ci /* convert long to double precision real */ 261062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cvt_d_l); 261162306a36Sopenharmony_ci rv.d = ieee754dp_flong(bits); 261262306a36Sopenharmony_ci rfmt = d_fmt; 261362306a36Sopenharmony_ci goto copcsr; 261462306a36Sopenharmony_ci default: { 261562306a36Sopenharmony_ci /* Emulating the new CMP.condn.fmt R6 instruction */ 261662306a36Sopenharmony_ci int cmpop = MIPSInst_FUNC(ir) & CMPOP_MASK; 261762306a36Sopenharmony_ci int sig = MIPSInst_FUNC(ir) & SIGN_BIT; 261862306a36Sopenharmony_ci union ieee754dp fs, ft; 261962306a36Sopenharmony_ci 262062306a36Sopenharmony_ci if (!cpu_has_mips_r6 || 262162306a36Sopenharmony_ci (MIPSInst_FUNC(ir) & 0x20)) 262262306a36Sopenharmony_ci return SIGILL; 262362306a36Sopenharmony_ci 262462306a36Sopenharmony_ci if (!sig) { 262562306a36Sopenharmony_ci if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { 262662306a36Sopenharmony_ci switch (cmpop) { 262762306a36Sopenharmony_ci case 0: 262862306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_af_d); 262962306a36Sopenharmony_ci break; 263062306a36Sopenharmony_ci case 1: 263162306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_un_d); 263262306a36Sopenharmony_ci break; 263362306a36Sopenharmony_ci case 2: 263462306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_eq_d); 263562306a36Sopenharmony_ci break; 263662306a36Sopenharmony_ci case 3: 263762306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_ueq_d); 263862306a36Sopenharmony_ci break; 263962306a36Sopenharmony_ci case 4: 264062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_lt_d); 264162306a36Sopenharmony_ci break; 264262306a36Sopenharmony_ci case 5: 264362306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_ult_d); 264462306a36Sopenharmony_ci break; 264562306a36Sopenharmony_ci case 6: 264662306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_le_d); 264762306a36Sopenharmony_ci break; 264862306a36Sopenharmony_ci case 7: 264962306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_ule_d); 265062306a36Sopenharmony_ci break; 265162306a36Sopenharmony_ci } 265262306a36Sopenharmony_ci } else { 265362306a36Sopenharmony_ci switch (cmpop) { 265462306a36Sopenharmony_ci case 1: 265562306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_or_d); 265662306a36Sopenharmony_ci break; 265762306a36Sopenharmony_ci case 2: 265862306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_une_d); 265962306a36Sopenharmony_ci break; 266062306a36Sopenharmony_ci case 3: 266162306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_ne_d); 266262306a36Sopenharmony_ci break; 266362306a36Sopenharmony_ci } 266462306a36Sopenharmony_ci } 266562306a36Sopenharmony_ci } else { 266662306a36Sopenharmony_ci if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { 266762306a36Sopenharmony_ci switch (cmpop) { 266862306a36Sopenharmony_ci case 0: 266962306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_saf_d); 267062306a36Sopenharmony_ci break; 267162306a36Sopenharmony_ci case 1: 267262306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_sun_d); 267362306a36Sopenharmony_ci break; 267462306a36Sopenharmony_ci case 2: 267562306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_seq_d); 267662306a36Sopenharmony_ci break; 267762306a36Sopenharmony_ci case 3: 267862306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_sueq_d); 267962306a36Sopenharmony_ci break; 268062306a36Sopenharmony_ci case 4: 268162306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_slt_d); 268262306a36Sopenharmony_ci break; 268362306a36Sopenharmony_ci case 5: 268462306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_sult_d); 268562306a36Sopenharmony_ci break; 268662306a36Sopenharmony_ci case 6: 268762306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_sle_d); 268862306a36Sopenharmony_ci break; 268962306a36Sopenharmony_ci case 7: 269062306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_sule_d); 269162306a36Sopenharmony_ci break; 269262306a36Sopenharmony_ci } 269362306a36Sopenharmony_ci } else { 269462306a36Sopenharmony_ci switch (cmpop) { 269562306a36Sopenharmony_ci case 1: 269662306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_sor_d); 269762306a36Sopenharmony_ci break; 269862306a36Sopenharmony_ci case 2: 269962306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_sune_d); 270062306a36Sopenharmony_ci break; 270162306a36Sopenharmony_ci case 3: 270262306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(cmp_sne_d); 270362306a36Sopenharmony_ci break; 270462306a36Sopenharmony_ci } 270562306a36Sopenharmony_ci } 270662306a36Sopenharmony_ci } 270762306a36Sopenharmony_ci 270862306a36Sopenharmony_ci /* fmt is l_fmt for double precision so fix it */ 270962306a36Sopenharmony_ci rfmt = d_fmt; 271062306a36Sopenharmony_ci /* default to false */ 271162306a36Sopenharmony_ci rv.l = 0; 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_ci /* CMP.condn.D */ 271462306a36Sopenharmony_ci DPFROMREG(fs, MIPSInst_FS(ir)); 271562306a36Sopenharmony_ci DPFROMREG(ft, MIPSInst_FT(ir)); 271662306a36Sopenharmony_ci 271762306a36Sopenharmony_ci /* positive predicates */ 271862306a36Sopenharmony_ci if (!(MIPSInst_FUNC(ir) & PREDICATE_BIT)) { 271962306a36Sopenharmony_ci if (ieee754dp_cmp(fs, ft, 272062306a36Sopenharmony_ci cmptab[cmpop], sig)) 272162306a36Sopenharmony_ci rv.l = -1LL; /* true, all 1s */ 272262306a36Sopenharmony_ci if (sig && 272362306a36Sopenharmony_ci ieee754_cxtest(IEEE754_INVALID_OPERATION)) 272462306a36Sopenharmony_ci rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 272562306a36Sopenharmony_ci else 272662306a36Sopenharmony_ci goto copcsr; 272762306a36Sopenharmony_ci } else { 272862306a36Sopenharmony_ci /* negative predicates */ 272962306a36Sopenharmony_ci switch (cmpop) { 273062306a36Sopenharmony_ci case 1: 273162306a36Sopenharmony_ci case 2: 273262306a36Sopenharmony_ci case 3: 273362306a36Sopenharmony_ci if (ieee754dp_cmp(fs, ft, 273462306a36Sopenharmony_ci negative_cmptab[cmpop], 273562306a36Sopenharmony_ci sig)) 273662306a36Sopenharmony_ci rv.l = -1LL; /* true, all 1s */ 273762306a36Sopenharmony_ci if (sig && 273862306a36Sopenharmony_ci ieee754_cxtest(IEEE754_INVALID_OPERATION)) 273962306a36Sopenharmony_ci rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S; 274062306a36Sopenharmony_ci else 274162306a36Sopenharmony_ci goto copcsr; 274262306a36Sopenharmony_ci break; 274362306a36Sopenharmony_ci default: 274462306a36Sopenharmony_ci /* Reserved R6 ops */ 274562306a36Sopenharmony_ci return SIGILL; 274662306a36Sopenharmony_ci } 274762306a36Sopenharmony_ci } 274862306a36Sopenharmony_ci break; 274962306a36Sopenharmony_ci } 275062306a36Sopenharmony_ci } 275162306a36Sopenharmony_ci break; 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_ci default: 275462306a36Sopenharmony_ci return SIGILL; 275562306a36Sopenharmony_ci } 275662306a36Sopenharmony_ci 275762306a36Sopenharmony_ci /* 275862306a36Sopenharmony_ci * Update the fpu CSR register for this operation. 275962306a36Sopenharmony_ci * If an exception is required, generate a tidy SIGFPE exception, 276062306a36Sopenharmony_ci * without updating the result register. 276162306a36Sopenharmony_ci * Note: cause exception bits do not accumulate, they are rewritten 276262306a36Sopenharmony_ci * for each op; only the flag/sticky bits accumulate. 276362306a36Sopenharmony_ci */ 276462306a36Sopenharmony_ci ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr; 276562306a36Sopenharmony_ci if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) { 276662306a36Sopenharmony_ci /*printk ("SIGFPE: FPU csr = %08x\n",ctx->fcr31); */ 276762306a36Sopenharmony_ci return SIGFPE; 276862306a36Sopenharmony_ci } 276962306a36Sopenharmony_ci 277062306a36Sopenharmony_ci /* 277162306a36Sopenharmony_ci * Now we can safely write the result back to the register file. 277262306a36Sopenharmony_ci */ 277362306a36Sopenharmony_ci switch (rfmt) { 277462306a36Sopenharmony_ci case -1: 277562306a36Sopenharmony_ci 277662306a36Sopenharmony_ci if (cpu_has_mips_4_5_r) 277762306a36Sopenharmony_ci cbit = fpucondbit[MIPSInst_FD(ir) >> 2]; 277862306a36Sopenharmony_ci else 277962306a36Sopenharmony_ci cbit = FPU_CSR_COND; 278062306a36Sopenharmony_ci if (rv.w) 278162306a36Sopenharmony_ci ctx->fcr31 |= cbit; 278262306a36Sopenharmony_ci else 278362306a36Sopenharmony_ci ctx->fcr31 &= ~cbit; 278462306a36Sopenharmony_ci break; 278562306a36Sopenharmony_ci 278662306a36Sopenharmony_ci case d_fmt: 278762306a36Sopenharmony_ci DPTOREG(rv.d, MIPSInst_FD(ir)); 278862306a36Sopenharmony_ci break; 278962306a36Sopenharmony_ci case s_fmt: 279062306a36Sopenharmony_ci SPTOREG(rv.s, MIPSInst_FD(ir)); 279162306a36Sopenharmony_ci break; 279262306a36Sopenharmony_ci case w_fmt: 279362306a36Sopenharmony_ci SITOREG(rv.w, MIPSInst_FD(ir)); 279462306a36Sopenharmony_ci break; 279562306a36Sopenharmony_ci case l_fmt: 279662306a36Sopenharmony_ci if (!cpu_has_mips_3_4_5_64_r2_r6) 279762306a36Sopenharmony_ci return SIGILL; 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci DITOREG(rv.l, MIPSInst_FD(ir)); 280062306a36Sopenharmony_ci break; 280162306a36Sopenharmony_ci default: 280262306a36Sopenharmony_ci return SIGILL; 280362306a36Sopenharmony_ci } 280462306a36Sopenharmony_ci 280562306a36Sopenharmony_ci return 0; 280662306a36Sopenharmony_ci} 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci/* 280962306a36Sopenharmony_ci * Emulate FPU instructions. 281062306a36Sopenharmony_ci * 281162306a36Sopenharmony_ci * If we use FPU hardware, then we have been typically called to handle 281262306a36Sopenharmony_ci * an unimplemented operation, such as where an operand is a NaN or 281362306a36Sopenharmony_ci * denormalized. In that case exit the emulation loop after a single 281462306a36Sopenharmony_ci * iteration so as to let hardware execute any subsequent instructions. 281562306a36Sopenharmony_ci * 281662306a36Sopenharmony_ci * If we have no FPU hardware or it has been disabled, then continue 281762306a36Sopenharmony_ci * emulating floating-point instructions until one of these conditions 281862306a36Sopenharmony_ci * has occurred: 281962306a36Sopenharmony_ci * 282062306a36Sopenharmony_ci * - a non-FPU instruction has been encountered, 282162306a36Sopenharmony_ci * 282262306a36Sopenharmony_ci * - an attempt to emulate has ended with a signal, 282362306a36Sopenharmony_ci * 282462306a36Sopenharmony_ci * - the ISA mode has been switched. 282562306a36Sopenharmony_ci * 282662306a36Sopenharmony_ci * We need to terminate the emulation loop if we got switched to the 282762306a36Sopenharmony_ci * MIPS16 mode, whether supported or not, so that we do not attempt 282862306a36Sopenharmony_ci * to emulate a MIPS16 instruction as a regular MIPS FPU instruction. 282962306a36Sopenharmony_ci * Similarly if we got switched to the microMIPS mode and only the 283062306a36Sopenharmony_ci * regular MIPS mode is supported, so that we do not attempt to emulate 283162306a36Sopenharmony_ci * a microMIPS instruction as a regular MIPS FPU instruction. Or if 283262306a36Sopenharmony_ci * we got switched to the regular MIPS mode and only the microMIPS mode 283362306a36Sopenharmony_ci * is supported, so that we do not attempt to emulate a regular MIPS 283462306a36Sopenharmony_ci * instruction that should cause an Address Error exception instead. 283562306a36Sopenharmony_ci * For simplicity we always terminate upon an ISA mode switch. 283662306a36Sopenharmony_ci */ 283762306a36Sopenharmony_ciint fpu_emulator_cop1Handler(struct pt_regs *xcp, struct mips_fpu_struct *ctx, 283862306a36Sopenharmony_ci int has_fpu, void __user **fault_addr) 283962306a36Sopenharmony_ci{ 284062306a36Sopenharmony_ci unsigned long oldepc, prevepc; 284162306a36Sopenharmony_ci struct mm_decoded_insn dec_insn; 284262306a36Sopenharmony_ci u16 instr[4]; 284362306a36Sopenharmony_ci u16 *instr_ptr; 284462306a36Sopenharmony_ci int sig = 0; 284562306a36Sopenharmony_ci 284662306a36Sopenharmony_ci /* 284762306a36Sopenharmony_ci * Initialize context if it hasn't been used already, otherwise ensure 284862306a36Sopenharmony_ci * it has been saved to struct thread_struct. 284962306a36Sopenharmony_ci */ 285062306a36Sopenharmony_ci if (!init_fp_ctx(current)) 285162306a36Sopenharmony_ci lose_fpu(1); 285262306a36Sopenharmony_ci 285362306a36Sopenharmony_ci oldepc = xcp->cp0_epc; 285462306a36Sopenharmony_ci do { 285562306a36Sopenharmony_ci prevepc = xcp->cp0_epc; 285662306a36Sopenharmony_ci 285762306a36Sopenharmony_ci if (get_isa16_mode(prevepc) && cpu_has_mmips) { 285862306a36Sopenharmony_ci /* 285962306a36Sopenharmony_ci * Get next 2 microMIPS instructions and convert them 286062306a36Sopenharmony_ci * into 32-bit instructions. 286162306a36Sopenharmony_ci */ 286262306a36Sopenharmony_ci if ((get_user(instr[0], (u16 __user *)msk_isa16_mode(xcp->cp0_epc))) || 286362306a36Sopenharmony_ci (get_user(instr[1], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 2))) || 286462306a36Sopenharmony_ci (get_user(instr[2], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 4))) || 286562306a36Sopenharmony_ci (get_user(instr[3], (u16 __user *)msk_isa16_mode(xcp->cp0_epc + 6)))) { 286662306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(errors); 286762306a36Sopenharmony_ci return SIGBUS; 286862306a36Sopenharmony_ci } 286962306a36Sopenharmony_ci instr_ptr = instr; 287062306a36Sopenharmony_ci 287162306a36Sopenharmony_ci /* Get first instruction. */ 287262306a36Sopenharmony_ci if (mm_insn_16bit(*instr_ptr)) { 287362306a36Sopenharmony_ci /* Duplicate the half-word. */ 287462306a36Sopenharmony_ci dec_insn.insn = (*instr_ptr << 16) | 287562306a36Sopenharmony_ci (*instr_ptr); 287662306a36Sopenharmony_ci /* 16-bit instruction. */ 287762306a36Sopenharmony_ci dec_insn.pc_inc = 2; 287862306a36Sopenharmony_ci instr_ptr += 1; 287962306a36Sopenharmony_ci } else { 288062306a36Sopenharmony_ci dec_insn.insn = (*instr_ptr << 16) | 288162306a36Sopenharmony_ci *(instr_ptr+1); 288262306a36Sopenharmony_ci /* 32-bit instruction. */ 288362306a36Sopenharmony_ci dec_insn.pc_inc = 4; 288462306a36Sopenharmony_ci instr_ptr += 2; 288562306a36Sopenharmony_ci } 288662306a36Sopenharmony_ci /* Get second instruction. */ 288762306a36Sopenharmony_ci if (mm_insn_16bit(*instr_ptr)) { 288862306a36Sopenharmony_ci /* Duplicate the half-word. */ 288962306a36Sopenharmony_ci dec_insn.next_insn = (*instr_ptr << 16) | 289062306a36Sopenharmony_ci (*instr_ptr); 289162306a36Sopenharmony_ci /* 16-bit instruction. */ 289262306a36Sopenharmony_ci dec_insn.next_pc_inc = 2; 289362306a36Sopenharmony_ci } else { 289462306a36Sopenharmony_ci dec_insn.next_insn = (*instr_ptr << 16) | 289562306a36Sopenharmony_ci *(instr_ptr+1); 289662306a36Sopenharmony_ci /* 32-bit instruction. */ 289762306a36Sopenharmony_ci dec_insn.next_pc_inc = 4; 289862306a36Sopenharmony_ci } 289962306a36Sopenharmony_ci dec_insn.micro_mips_mode = 1; 290062306a36Sopenharmony_ci } else { 290162306a36Sopenharmony_ci if ((get_user(dec_insn.insn, 290262306a36Sopenharmony_ci (mips_instruction __user *) xcp->cp0_epc)) || 290362306a36Sopenharmony_ci (get_user(dec_insn.next_insn, 290462306a36Sopenharmony_ci (mips_instruction __user *)(xcp->cp0_epc+4)))) { 290562306a36Sopenharmony_ci MIPS_FPU_EMU_INC_STATS(errors); 290662306a36Sopenharmony_ci return SIGBUS; 290762306a36Sopenharmony_ci } 290862306a36Sopenharmony_ci dec_insn.pc_inc = 4; 290962306a36Sopenharmony_ci dec_insn.next_pc_inc = 4; 291062306a36Sopenharmony_ci dec_insn.micro_mips_mode = 0; 291162306a36Sopenharmony_ci } 291262306a36Sopenharmony_ci 291362306a36Sopenharmony_ci if ((dec_insn.insn == 0) || 291462306a36Sopenharmony_ci ((dec_insn.pc_inc == 2) && 291562306a36Sopenharmony_ci ((dec_insn.insn & 0xffff) == MM_NOP16))) 291662306a36Sopenharmony_ci xcp->cp0_epc += dec_insn.pc_inc; /* Skip NOPs */ 291762306a36Sopenharmony_ci else { 291862306a36Sopenharmony_ci /* 291962306a36Sopenharmony_ci * The 'ieee754_csr' is an alias of ctx->fcr31. 292062306a36Sopenharmony_ci * No need to copy ctx->fcr31 to ieee754_csr. 292162306a36Sopenharmony_ci */ 292262306a36Sopenharmony_ci sig = cop1Emulate(xcp, ctx, dec_insn, fault_addr); 292362306a36Sopenharmony_ci } 292462306a36Sopenharmony_ci 292562306a36Sopenharmony_ci if (has_fpu) 292662306a36Sopenharmony_ci break; 292762306a36Sopenharmony_ci if (sig) 292862306a36Sopenharmony_ci break; 292962306a36Sopenharmony_ci /* 293062306a36Sopenharmony_ci * We have to check for the ISA bit explicitly here, 293162306a36Sopenharmony_ci * because `get_isa16_mode' may return 0 if support 293262306a36Sopenharmony_ci * for code compression has been globally disabled, 293362306a36Sopenharmony_ci * or otherwise we may produce the wrong signal or 293462306a36Sopenharmony_ci * even proceed successfully where we must not. 293562306a36Sopenharmony_ci */ 293662306a36Sopenharmony_ci if ((xcp->cp0_epc ^ prevepc) & 0x1) 293762306a36Sopenharmony_ci break; 293862306a36Sopenharmony_ci 293962306a36Sopenharmony_ci cond_resched(); 294062306a36Sopenharmony_ci } while (xcp->cp0_epc > prevepc); 294162306a36Sopenharmony_ci 294262306a36Sopenharmony_ci /* SIGILL indicates a non-fpu instruction */ 294362306a36Sopenharmony_ci if (sig == SIGILL && xcp->cp0_epc != oldepc) 294462306a36Sopenharmony_ci /* but if EPC has advanced, then ignore it */ 294562306a36Sopenharmony_ci sig = 0; 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_ci return sig; 294862306a36Sopenharmony_ci} 2949