2005-04-17 02:20:36 +04:00
/*
2014-04-26 03:49:14 +04:00
* cp1emu . c : a MIPS coprocessor 1 ( FPU ) instruction emulator
2005-04-17 02:20:36 +04:00
*
* MIPS floating point support
* Copyright ( C ) 1994 - 2000 Algorithmics Ltd .
*
* Kevin D . Kissell , kevink @ mips . com and Carsten Langgaard , carstenl @ mips . com
* Copyright ( C ) 2000 MIPS Technologies , Inc .
*
* This program is free software ; you can distribute it and / or modify it
* under the terms of the GNU General Public License ( Version 2 ) as
* published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License
* for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
2014-04-26 03:49:14 +04:00
* 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA .
2005-04-17 02:20:36 +04:00
*
* A complete emulator for MIPS coprocessor 1 instructions . This is
* required for # float ( switch ) or # float ( trap ) , where it catches all
* COP1 instructions via the " CoProcessor Unusable " exception .
*
* More surprisingly it is also required for # float ( ieee ) , to help out
2014-04-26 03:49:14 +04:00
* the hardware FPU at the boundaries of the IEEE - 754 representation
2005-04-17 02:20:36 +04:00
* ( denormalised values , infinities , underflow , etc ) . It is made
* quite nasty because emulation of some non - COP1 instructions is
* required , e . g . in branch delay slots .
*
2014-04-26 03:49:14 +04:00
* Note if you know that you won ' t have an FPU , then you ' ll get much
2005-04-17 02:20:36 +04:00
* better performance by compiling with - msoft - float !
*/
# include <linux/sched.h>
2007-07-07 18:21:49 +04:00
# include <linux/debugfs.h>
2014-04-19 15:11:37 +04:00
# include <linux/kconfig.h>
2014-04-16 04:46:11 +04:00
# include <linux/percpu-defs.h>
2010-10-12 15:37:21 +04:00
# include <linux/perf_event.h>
2005-04-17 02:20:36 +04:00
2014-04-16 04:09:53 +04:00
# include <asm/branch.h>
2005-04-17 02:20:36 +04:00
# include <asm/inst.h>
# include <asm/ptrace.h>
# include <asm/signal.h>
2014-04-16 04:09:53 +04:00
# include <asm/uaccess.h>
# include <asm/processor.h>
2005-04-17 02:20:36 +04:00
# include <asm/fpu_emulator.h>
2013-03-25 21:09:02 +04:00
# include <asm/fpu.h>
2014-12-03 18:47:03 +03:00
# include <asm/mips-r2-to-r6-emul.h>
2005-04-17 02:20:36 +04:00
# include "ieee754.h"
/* Function which emulates a floating point instruction. */
2006-05-15 20:26:03 +04:00
static int fpu_emu ( struct pt_regs * , struct mips_fpu_struct * ,
2005-04-17 02:20:36 +04:00
mips_instruction ) ;
static int fpux_emu ( struct pt_regs * ,
2010-10-22 03:32:26 +04:00
struct mips_fpu_struct * , mips_instruction , void * __user * ) ;
2005-04-17 02:20:36 +04:00
/* Control registers */
# define FPCREG_RID 0 /* $0 = revision id */
# define FPCREG_CSR 31 /* $31 = csr */
/* convert condition code register number to csr bit */
2014-12-03 18:47:03 +03:00
const unsigned int fpucondbit [ 8 ] = {
2005-04-17 02:20:36 +04:00
FPU_CSR_COND0 ,
FPU_CSR_COND1 ,
FPU_CSR_COND2 ,
FPU_CSR_COND3 ,
FPU_CSR_COND4 ,
FPU_CSR_COND5 ,
FPU_CSR_COND6 ,
FPU_CSR_COND7
} ;
2013-03-25 21:09:02 +04:00
/* (microMIPS) Convert certain microMIPS instructions to MIPS32 format. */
static const int sd_format [ ] = { 16 , 17 , 0 , 0 , 0 , 0 , 0 , 0 } ;
static const int sdps_format [ ] = { 16 , 17 , 22 , 0 , 0 , 0 , 0 , 0 } ;
static const int dwl_format [ ] = { 17 , 20 , 21 , 0 , 0 , 0 , 0 , 0 } ;
static const int swl_format [ ] = { 16 , 20 , 21 , 0 , 0 , 0 , 0 , 0 } ;
/*
* This functions translates a 32 - bit microMIPS instruction
* into a 32 - bit MIPS32 instruction . Returns 0 on success
* and SIGILL otherwise .
*/
static int microMIPS32_to_MIPS32 ( union mips_instruction * insn_ptr )
{
union mips_instruction insn = * insn_ptr ;
union mips_instruction mips32_insn = insn ;
int func , fmt , op ;
switch ( insn . mm_i_format . opcode ) {
case mm_ldc132_op :
mips32_insn . mm_i_format . opcode = ldc1_op ;
mips32_insn . mm_i_format . rt = insn . mm_i_format . rs ;
mips32_insn . mm_i_format . rs = insn . mm_i_format . rt ;
break ;
case mm_lwc132_op :
mips32_insn . mm_i_format . opcode = lwc1_op ;
mips32_insn . mm_i_format . rt = insn . mm_i_format . rs ;
mips32_insn . mm_i_format . rs = insn . mm_i_format . rt ;
break ;
case mm_sdc132_op :
mips32_insn . mm_i_format . opcode = sdc1_op ;
mips32_insn . mm_i_format . rt = insn . mm_i_format . rs ;
mips32_insn . mm_i_format . rs = insn . mm_i_format . rt ;
break ;
case mm_swc132_op :
mips32_insn . mm_i_format . opcode = swc1_op ;
mips32_insn . mm_i_format . rt = insn . mm_i_format . rs ;
mips32_insn . mm_i_format . rs = insn . mm_i_format . rt ;
break ;
case mm_pool32i_op :
/* NOTE: offset is << by 1 if in microMIPS mode. */
if ( ( insn . mm_i_format . rt = = mm_bc1f_op ) | |
( insn . mm_i_format . rt = = mm_bc1t_op ) ) {
mips32_insn . fb_format . opcode = cop1_op ;
mips32_insn . fb_format . bc = bc_op ;
mips32_insn . fb_format . flag =
( insn . mm_i_format . rt = = mm_bc1t_op ) ? 1 : 0 ;
} else
return SIGILL ;
break ;
case mm_pool32f_op :
switch ( insn . mm_fp0_format . func ) {
case mm_32f_01_op :
case mm_32f_11_op :
case mm_32f_02_op :
case mm_32f_12_op :
case mm_32f_41_op :
case mm_32f_51_op :
case mm_32f_42_op :
case mm_32f_52_op :
op = insn . mm_fp0_format . func ;
if ( op = = mm_32f_01_op )
func = madd_s_op ;
else if ( op = = mm_32f_11_op )
func = madd_d_op ;
else if ( op = = mm_32f_02_op )
func = nmadd_s_op ;
else if ( op = = mm_32f_12_op )
func = nmadd_d_op ;
else if ( op = = mm_32f_41_op )
func = msub_s_op ;
else if ( op = = mm_32f_51_op )
func = msub_d_op ;
else if ( op = = mm_32f_42_op )
func = nmsub_s_op ;
else
func = nmsub_d_op ;
mips32_insn . fp6_format . opcode = cop1x_op ;
mips32_insn . fp6_format . fr = insn . mm_fp6_format . fr ;
mips32_insn . fp6_format . ft = insn . mm_fp6_format . ft ;
mips32_insn . fp6_format . fs = insn . mm_fp6_format . fs ;
mips32_insn . fp6_format . fd = insn . mm_fp6_format . fd ;
mips32_insn . fp6_format . func = func ;
break ;
case mm_32f_10_op :
func = - 1 ; /* Invalid */
op = insn . mm_fp5_format . op & 0x7 ;
if ( op = = mm_ldxc1_op )
func = ldxc1_op ;
else if ( op = = mm_sdxc1_op )
func = sdxc1_op ;
else if ( op = = mm_lwxc1_op )
func = lwxc1_op ;
else if ( op = = mm_swxc1_op )
func = swxc1_op ;
if ( func ! = - 1 ) {
mips32_insn . r_format . opcode = cop1x_op ;
mips32_insn . r_format . rs =
insn . mm_fp5_format . base ;
mips32_insn . r_format . rt =
insn . mm_fp5_format . index ;
mips32_insn . r_format . rd = 0 ;
mips32_insn . r_format . re = insn . mm_fp5_format . fd ;
mips32_insn . r_format . func = func ;
} else
return SIGILL ;
break ;
case mm_32f_40_op :
op = - 1 ; /* Invalid */
if ( insn . mm_fp2_format . op = = mm_fmovt_op )
op = 1 ;
else if ( insn . mm_fp2_format . op = = mm_fmovf_op )
op = 0 ;
if ( op ! = - 1 ) {
mips32_insn . fp0_format . opcode = cop1_op ;
mips32_insn . fp0_format . fmt =
sdps_format [ insn . mm_fp2_format . fmt ] ;
mips32_insn . fp0_format . ft =
( insn . mm_fp2_format . cc < < 2 ) + op ;
mips32_insn . fp0_format . fs =
insn . mm_fp2_format . fs ;
mips32_insn . fp0_format . fd =
insn . mm_fp2_format . fd ;
mips32_insn . fp0_format . func = fmovc_op ;
} else
return SIGILL ;
break ;
case mm_32f_60_op :
func = - 1 ; /* Invalid */
if ( insn . mm_fp0_format . op = = mm_fadd_op )
func = fadd_op ;
else if ( insn . mm_fp0_format . op = = mm_fsub_op )
func = fsub_op ;
else if ( insn . mm_fp0_format . op = = mm_fmul_op )
func = fmul_op ;
else if ( insn . mm_fp0_format . op = = mm_fdiv_op )
func = fdiv_op ;
if ( func ! = - 1 ) {
mips32_insn . fp0_format . opcode = cop1_op ;
mips32_insn . fp0_format . fmt =
sdps_format [ insn . mm_fp0_format . fmt ] ;
mips32_insn . fp0_format . ft =
insn . mm_fp0_format . ft ;
mips32_insn . fp0_format . fs =
insn . mm_fp0_format . fs ;
mips32_insn . fp0_format . fd =
insn . mm_fp0_format . fd ;
mips32_insn . fp0_format . func = func ;
} else
return SIGILL ;
break ;
case mm_32f_70_op :
func = - 1 ; /* Invalid */
if ( insn . mm_fp0_format . op = = mm_fmovn_op )
func = fmovn_op ;
else if ( insn . mm_fp0_format . op = = mm_fmovz_op )
func = fmovz_op ;
if ( func ! = - 1 ) {
mips32_insn . fp0_format . opcode = cop1_op ;
mips32_insn . fp0_format . fmt =
sdps_format [ insn . mm_fp0_format . fmt ] ;
mips32_insn . fp0_format . ft =
insn . mm_fp0_format . ft ;
mips32_insn . fp0_format . fs =
insn . mm_fp0_format . fs ;
mips32_insn . fp0_format . fd =
insn . mm_fp0_format . fd ;
mips32_insn . fp0_format . func = func ;
} else
return SIGILL ;
break ;
case mm_32f_73_op : /* POOL32FXF */
switch ( insn . mm_fp1_format . op ) {
case mm_movf0_op :
case mm_movf1_op :
case mm_movt0_op :
case mm_movt1_op :
if ( ( insn . mm_fp1_format . op & 0x7f ) = =
mm_movf0_op )
op = 0 ;
else
op = 1 ;
mips32_insn . r_format . opcode = spec_op ;
mips32_insn . r_format . rs = insn . mm_fp4_format . fs ;
mips32_insn . r_format . rt =
( insn . mm_fp4_format . cc < < 2 ) + op ;
mips32_insn . r_format . rd = insn . mm_fp4_format . rt ;
mips32_insn . r_format . re = 0 ;
mips32_insn . r_format . func = movc_op ;
break ;
case mm_fcvtd0_op :
case mm_fcvtd1_op :
case mm_fcvts0_op :
case mm_fcvts1_op :
if ( ( insn . mm_fp1_format . op & 0x7f ) = =
mm_fcvtd0_op ) {
func = fcvtd_op ;
fmt = swl_format [ insn . mm_fp3_format . fmt ] ;
} else {
func = fcvts_op ;
fmt = dwl_format [ insn . mm_fp3_format . fmt ] ;
}
mips32_insn . fp0_format . opcode = cop1_op ;
mips32_insn . fp0_format . fmt = fmt ;
mips32_insn . fp0_format . ft = 0 ;
mips32_insn . fp0_format . fs =
insn . mm_fp3_format . fs ;
mips32_insn . fp0_format . fd =
insn . mm_fp3_format . rt ;
mips32_insn . fp0_format . func = func ;
break ;
case mm_fmov0_op :
case mm_fmov1_op :
case mm_fabs0_op :
case mm_fabs1_op :
case mm_fneg0_op :
case mm_fneg1_op :
if ( ( insn . mm_fp1_format . op & 0x7f ) = =
mm_fmov0_op )
func = fmov_op ;
else if ( ( insn . mm_fp1_format . op & 0x7f ) = =
mm_fabs0_op )
func = fabs_op ;
else
func = fneg_op ;
mips32_insn . fp0_format . opcode = cop1_op ;
mips32_insn . fp0_format . fmt =
sdps_format [ insn . mm_fp3_format . fmt ] ;
mips32_insn . fp0_format . ft = 0 ;
mips32_insn . fp0_format . fs =
insn . mm_fp3_format . fs ;
mips32_insn . fp0_format . fd =
insn . mm_fp3_format . rt ;
mips32_insn . fp0_format . func = func ;
break ;
case mm_ffloorl_op :
case mm_ffloorw_op :
case mm_fceill_op :
case mm_fceilw_op :
case mm_ftruncl_op :
case mm_ftruncw_op :
case mm_froundl_op :
case mm_froundw_op :
case mm_fcvtl_op :
case mm_fcvtw_op :
if ( insn . mm_fp1_format . op = = mm_ffloorl_op )
func = ffloorl_op ;
else if ( insn . mm_fp1_format . op = = mm_ffloorw_op )
func = ffloor_op ;
else if ( insn . mm_fp1_format . op = = mm_fceill_op )
func = fceill_op ;
else if ( insn . mm_fp1_format . op = = mm_fceilw_op )
func = fceil_op ;
else if ( insn . mm_fp1_format . op = = mm_ftruncl_op )
func = ftruncl_op ;
else if ( insn . mm_fp1_format . op = = mm_ftruncw_op )
func = ftrunc_op ;
else if ( insn . mm_fp1_format . op = = mm_froundl_op )
func = froundl_op ;
else if ( insn . mm_fp1_format . op = = mm_froundw_op )
func = fround_op ;
else if ( insn . mm_fp1_format . op = = mm_fcvtl_op )
func = fcvtl_op ;
else
func = fcvtw_op ;
mips32_insn . fp0_format . opcode = cop1_op ;
mips32_insn . fp0_format . fmt =
sd_format [ insn . mm_fp1_format . fmt ] ;
mips32_insn . fp0_format . ft = 0 ;
mips32_insn . fp0_format . fs =
insn . mm_fp1_format . fs ;
mips32_insn . fp0_format . fd =
insn . mm_fp1_format . rt ;
mips32_insn . fp0_format . func = func ;
break ;
case mm_frsqrt_op :
case mm_fsqrt_op :
case mm_frecip_op :
if ( insn . mm_fp1_format . op = = mm_frsqrt_op )
func = frsqrt_op ;
else if ( insn . mm_fp1_format . op = = mm_fsqrt_op )
func = fsqrt_op ;
else
func = frecip_op ;
mips32_insn . fp0_format . opcode = cop1_op ;
mips32_insn . fp0_format . fmt =
sdps_format [ insn . mm_fp1_format . fmt ] ;
mips32_insn . fp0_format . ft = 0 ;
mips32_insn . fp0_format . fs =
insn . mm_fp1_format . fs ;
mips32_insn . fp0_format . fd =
insn . mm_fp1_format . rt ;
mips32_insn . fp0_format . func = func ;
break ;
case mm_mfc1_op :
case mm_mtc1_op :
case mm_cfc1_op :
case mm_ctc1_op :
2013-11-07 16:48:29 +04:00
case mm_mfhc1_op :
case mm_mthc1_op :
2013-03-25 21:09:02 +04:00
if ( insn . mm_fp1_format . op = = mm_mfc1_op )
op = mfc_op ;
else if ( insn . mm_fp1_format . op = = mm_mtc1_op )
op = mtc_op ;
else if ( insn . mm_fp1_format . op = = mm_cfc1_op )
op = cfc_op ;
2013-11-07 16:48:29 +04:00
else if ( insn . mm_fp1_format . op = = mm_ctc1_op )
2013-03-25 21:09:02 +04:00
op = ctc_op ;
2013-11-07 16:48:29 +04:00
else if ( insn . mm_fp1_format . op = = mm_mfhc1_op )
op = mfhc_op ;
else
op = mthc_op ;
2013-03-25 21:09:02 +04:00
mips32_insn . fp1_format . opcode = cop1_op ;
mips32_insn . fp1_format . op = op ;
mips32_insn . fp1_format . rt =
insn . mm_fp1_format . rt ;
mips32_insn . fp1_format . fs =
insn . mm_fp1_format . fs ;
mips32_insn . fp1_format . fd = 0 ;
mips32_insn . fp1_format . func = 0 ;
break ;
default :
return SIGILL ;
}
break ;
case mm_32f_74_op : /* c.cond.fmt */
mips32_insn . fp0_format . opcode = cop1_op ;
mips32_insn . fp0_format . fmt =
sdps_format [ insn . mm_fp4_format . fmt ] ;
mips32_insn . fp0_format . ft = insn . mm_fp4_format . rt ;
mips32_insn . fp0_format . fs = insn . mm_fp4_format . fs ;
mips32_insn . fp0_format . fd = insn . mm_fp4_format . cc < < 2 ;
mips32_insn . fp0_format . func =
insn . mm_fp4_format . cond | MM_MIPS32_COND_FC ;
break ;
default :
return SIGILL ;
}
break ;
default :
return SIGILL ;
}
* insn_ptr = mips32_insn ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
/*
* Redundant with logic already in kernel / branch . c ,
* embedded in compute_return_epc . At some point ,
* a single subroutine should be used across both
* modules .
*/
2013-03-25 21:09:02 +04:00
static int isBranchInstr ( struct pt_regs * regs , struct mm_decoded_insn dec_insn ,
unsigned long * contpc )
2005-04-17 02:20:36 +04:00
{
2013-03-25 21:09:02 +04:00
union mips_instruction insn = ( union mips_instruction ) dec_insn . insn ;
unsigned int fcr31 ;
unsigned int bit = 0 ;
switch ( insn . i_format . opcode ) {
2005-04-17 02:20:36 +04:00
case spec_op :
2013-03-25 21:09:02 +04:00
switch ( insn . r_format . func ) {
2005-04-17 02:20:36 +04:00
case jalr_op :
2013-03-25 21:09:02 +04:00
regs - > regs [ insn . r_format . rd ] =
regs - > cp0_epc + dec_insn . pc_inc +
dec_insn . next_pc_inc ;
/* Fall through */
2005-04-17 02:20:36 +04:00
case jr_op :
2014-11-25 18:54:14 +03:00
/* For R6, JR already emulated in jalr_op */
if ( NO_R6EMU & & insn . r_format . opcode = = jr_op )
break ;
2013-03-25 21:09:02 +04:00
* contpc = regs - > regs [ insn . r_format . rs ] ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
break ;
case bcond_op :
2013-03-25 21:09:02 +04:00
switch ( insn . i_format . rt ) {
case bltzal_op :
case bltzall_op :
2014-11-25 19:02:23 +03:00
if ( NO_R6EMU & & ( insn . i_format . rs | |
insn . i_format . rt = = bltzall_op ) )
break ;
2013-03-25 21:09:02 +04:00
regs - > regs [ 31 ] = regs - > cp0_epc +
dec_insn . pc_inc +
dec_insn . next_pc_inc ;
/* Fall through */
2005-04-17 02:20:36 +04:00
case bltzl_op :
2014-11-25 19:02:23 +03:00
if ( NO_R6EMU )
break ;
case bltz_op :
2013-03-25 21:09:02 +04:00
if ( ( long ) regs - > regs [ insn . i_format . rs ] < 0 )
* contpc = regs - > cp0_epc +
dec_insn . pc_inc +
( insn . i_format . simmediate < < 2 ) ;
else
* contpc = regs - > cp0_epc +
dec_insn . pc_inc +
dec_insn . next_pc_inc ;
return 1 ;
2005-04-17 02:20:36 +04:00
case bgezal_op :
case bgezall_op :
2014-11-25 19:02:23 +03:00
if ( NO_R6EMU & & ( insn . i_format . rs | |
insn . i_format . rt = = bgezall_op ) )
break ;
2013-03-25 21:09:02 +04:00
regs - > regs [ 31 ] = regs - > cp0_epc +
dec_insn . pc_inc +
dec_insn . next_pc_inc ;
/* Fall through */
case bgezl_op :
2014-11-25 19:02:23 +03:00
if ( NO_R6EMU )
break ;
case bgez_op :
2013-03-25 21:09:02 +04:00
if ( ( long ) regs - > regs [ insn . i_format . rs ] > = 0 )
* contpc = regs - > cp0_epc +
dec_insn . pc_inc +
( insn . i_format . simmediate < < 2 ) ;
else
* contpc = regs - > cp0_epc +
dec_insn . pc_inc +
dec_insn . next_pc_inc ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
break ;
case jalx_op :
2013-03-25 21:09:02 +04:00
set_isa16_mode ( bit ) ;
case jal_op :
regs - > regs [ 31 ] = regs - > cp0_epc +
dec_insn . pc_inc +
dec_insn . next_pc_inc ;
/* Fall through */
case j_op :
* contpc = regs - > cp0_epc + dec_insn . pc_inc ;
* contpc > > = 28 ;
* contpc < < = 28 ;
* contpc | = ( insn . j_format . target < < 2 ) ;
/* Set microMIPS mode bit: XOR for jalx. */
* contpc ^ = bit ;
return 1 ;
2005-04-17 02:20:36 +04:00
case beql_op :
2014-11-25 19:02:23 +03:00
if ( NO_R6EMU )
break ;
case beq_op :
2013-03-25 21:09:02 +04:00
if ( regs - > regs [ insn . i_format . rs ] = =
regs - > regs [ insn . i_format . rt ] )
* contpc = regs - > cp0_epc +
dec_insn . pc_inc +
( insn . i_format . simmediate < < 2 ) ;
else
* contpc = regs - > cp0_epc +
dec_insn . pc_inc +
dec_insn . next_pc_inc ;
return 1 ;
2005-04-17 02:20:36 +04:00
case bnel_op :
2014-11-25 19:02:23 +03:00
if ( NO_R6EMU )
break ;
case bne_op :
2013-03-25 21:09:02 +04:00
if ( regs - > regs [ insn . i_format . rs ] ! =
regs - > regs [ insn . i_format . rt ] )
* contpc = regs - > cp0_epc +
dec_insn . pc_inc +
( insn . i_format . simmediate < < 2 ) ;
else
* contpc = regs - > cp0_epc +
dec_insn . pc_inc +
dec_insn . next_pc_inc ;
return 1 ;
2005-04-17 02:20:36 +04:00
case blezl_op :
2014-11-25 19:02:23 +03:00
if ( NO_R6EMU )
break ;
case blez_op :
2014-11-26 15:57:54 +03:00
/*
* Compact branches for R6 for the
* blez and blezl opcodes .
* BLEZ | rs = 0 | rt ! = 0 = = BLEZALC
* BLEZ | rs = rt ! = 0 = = BGEZALC
* BLEZ | rs ! = 0 | rt ! = 0 = = BGEUC
* BLEZL | rs = 0 | rt ! = 0 = = BLEZC
* BLEZL | rs = rt ! = 0 = = BGEZC
* BLEZL | rs ! = 0 | rt ! = 0 = = BGEC
*
* For real BLEZ { , L } , rt is always 0.
*/
if ( cpu_has_mips_r6 & & insn . i_format . rt ) {
if ( ( insn . i_format . opcode = = blez_op ) & &
( ( ! insn . i_format . rs & & insn . i_format . rt ) | |
( insn . i_format . rs = = insn . i_format . rt ) ) )
regs - > regs [ 31 ] = regs - > cp0_epc +
dec_insn . pc_inc ;
* contpc = regs - > cp0_epc + dec_insn . pc_inc +
dec_insn . next_pc_inc ;
return 1 ;
}
2013-03-25 21:09:02 +04:00
if ( ( long ) regs - > regs [ insn . i_format . rs ] < = 0 )
* contpc = regs - > cp0_epc +
dec_insn . pc_inc +
( insn . i_format . simmediate < < 2 ) ;
else
* contpc = regs - > cp0_epc +
dec_insn . pc_inc +
dec_insn . next_pc_inc ;
return 1 ;
2005-04-17 02:20:36 +04:00
case bgtzl_op :
2014-11-25 19:02:23 +03:00
if ( NO_R6EMU )
break ;
case bgtz_op :
2014-11-26 16:05:09 +03:00
/*
* Compact branches for R6 for the
* bgtz and bgtzl opcodes .
* BGTZ | rs = 0 | rt ! = 0 = = BGTZALC
* BGTZ | rs = rt ! = 0 = = BLTZALC
* BGTZ | rs ! = 0 | rt ! = 0 = = BLTUC
* BGTZL | rs = 0 | rt ! = 0 = = BGTZC
* BGTZL | rs = rt ! = 0 = = BLTZC
* BGTZL | rs ! = 0 | rt ! = 0 = = BLTC
*
* * ZALC varint for BGTZ & & & rt ! = 0
* For real GTZ { , L } , rt is always 0.
*/
if ( cpu_has_mips_r6 & & insn . i_format . rt ) {
if ( ( insn . i_format . opcode = = blez_op ) & &
( ( ! insn . i_format . rs & & insn . i_format . rt ) | |
( insn . i_format . rs = = insn . i_format . rt ) ) )
regs - > regs [ 31 ] = regs - > cp0_epc +
dec_insn . pc_inc ;
* contpc = regs - > cp0_epc + dec_insn . pc_inc +
dec_insn . next_pc_inc ;
return 1 ;
}
2013-03-25 21:09:02 +04:00
if ( ( long ) regs - > regs [ insn . i_format . rs ] > 0 )
* contpc = regs - > cp0_epc +
dec_insn . pc_inc +
( insn . i_format . simmediate < < 2 ) ;
else
* contpc = regs - > cp0_epc +
dec_insn . pc_inc +
dec_insn . next_pc_inc ;
2005-04-17 02:20:36 +04:00
return 1 ;
2014-11-26 17:08:52 +03:00
case cbcond0_op :
2014-11-26 18:03:54 +03:00
case cbcond1_op :
2014-11-26 17:08:52 +03:00
if ( ! cpu_has_mips_r6 )
break ;
if ( insn . i_format . rt & & ! insn . i_format . rs )
regs - > regs [ 31 ] = regs - > cp0_epc + 4 ;
* contpc = regs - > cp0_epc + dec_insn . pc_inc +
dec_insn . next_pc_inc ;
return 1 ;
2013-08-19 23:10:34 +04:00
# ifdef CONFIG_CPU_CAVIUM_OCTEON
case lwc2_op : /* This is bbit0 on Octeon */
if ( ( regs - > regs [ insn . i_format . rs ] & ( 1ull < < insn . i_format . rt ) ) = = 0 )
* contpc = regs - > cp0_epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
else
* contpc = regs - > cp0_epc + 8 ;
return 1 ;
case ldc2_op : /* This is bbit032 on Octeon */
if ( ( regs - > regs [ insn . i_format . rs ] & ( 1ull < < ( insn . i_format . rt + 32 ) ) ) = = 0 )
* contpc = regs - > cp0_epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
else
* contpc = regs - > cp0_epc + 8 ;
return 1 ;
case swc2_op : /* This is bbit1 on Octeon */
if ( regs - > regs [ insn . i_format . rs ] & ( 1ull < < insn . i_format . rt ) )
* contpc = regs - > cp0_epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
else
* contpc = regs - > cp0_epc + 8 ;
return 1 ;
case sdc2_op : /* This is bbit132 on Octeon */
if ( regs - > regs [ insn . i_format . rs ] & ( 1ull < < ( insn . i_format . rt + 32 ) ) )
* contpc = regs - > cp0_epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
else
* contpc = regs - > cp0_epc + 8 ;
return 1 ;
2014-11-26 16:56:51 +03:00
# else
case bc6_op :
/*
* Only valid for MIPS R6 but we can still end up
* here from a broken userland so just tell emulator
* this is not a branch and let it break later on .
*/
if ( ! cpu_has_mips_r6 )
break ;
* contpc = regs - > cp0_epc + dec_insn . pc_inc +
dec_insn . next_pc_inc ;
2014-11-26 18:43:11 +03:00
return 1 ;
case balc6_op :
if ( ! cpu_has_mips_r6 )
break ;
regs - > regs [ 31 ] = regs - > cp0_epc + 4 ;
* contpc = regs - > cp0_epc + dec_insn . pc_inc +
dec_insn . next_pc_inc ;
2014-11-27 12:32:25 +03:00
return 1 ;
case beqzcjic_op :
if ( ! cpu_has_mips_r6 )
break ;
* contpc = regs - > cp0_epc + dec_insn . pc_inc +
dec_insn . next_pc_inc ;
2015-01-08 14:55:20 +03:00
return 1 ;
case bnezcjialc_op :
if ( ! cpu_has_mips_r6 )
break ;
if ( ! insn . i_format . rs )
regs - > regs [ 31 ] = regs - > cp0_epc + 4 ;
* contpc = regs - > cp0_epc + dec_insn . pc_inc +
dec_insn . next_pc_inc ;
2014-11-26 16:56:51 +03:00
return 1 ;
2013-08-19 23:10:34 +04:00
# endif
2005-04-17 02:20:36 +04:00
case cop0_op :
case cop1_op :
2014-11-26 13:10:18 +03:00
/* Need to check for R6 bc1nez and bc1eqz branches */
if ( cpu_has_mips_r6 & &
( ( insn . i_format . rs = = bc1eqz_op ) | |
( insn . i_format . rs = = bc1nez_op ) ) ) {
bit = 0 ;
switch ( insn . i_format . rs ) {
case bc1eqz_op :
if ( get_fpr32 ( & current - > thread . fpu . fpr [ insn . i_format . rt ] , 0 ) & 0x1 )
bit = 1 ;
break ;
case bc1nez_op :
if ( ! ( get_fpr32 ( & current - > thread . fpu . fpr [ insn . i_format . rt ] , 0 ) & 0x1 ) )
bit = 1 ;
break ;
}
if ( bit )
* contpc = regs - > cp0_epc +
dec_insn . pc_inc +
( insn . i_format . simmediate < < 2 ) ;
else
* contpc = regs - > cp0_epc +
dec_insn . pc_inc +
dec_insn . next_pc_inc ;
return 1 ;
}
/* R2/R6 compatible cop1 instruction. Fall through */
2005-04-17 02:20:36 +04:00
case cop2_op :
case cop1x_op :
2013-03-25 21:09:02 +04:00
if ( insn . i_format . rs = = bc_op ) {
preempt_disable ( ) ;
if ( is_fpu_owner ( ) )
2014-11-07 16:13:54 +03:00
fcr31 = read_32bit_cp1_register ( CP1_STATUS ) ;
2013-03-25 21:09:02 +04:00
else
fcr31 = current - > thread . fpu . fcr31 ;
preempt_enable ( ) ;
bit = ( insn . i_format . rt > > 2 ) ;
bit + = ( bit ! = 0 ) ;
bit + = 23 ;
switch ( insn . i_format . rt & 3 ) {
case 0 : /* bc1f */
case 2 : /* bc1fl */
if ( ~ fcr31 & ( 1 < < bit ) )
* contpc = regs - > cp0_epc +
dec_insn . pc_inc +
( insn . i_format . simmediate < < 2 ) ;
else
* contpc = regs - > cp0_epc +
dec_insn . pc_inc +
dec_insn . next_pc_inc ;
return 1 ;
case 1 : /* bc1t */
case 3 : /* bc1tl */
if ( fcr31 & ( 1 < < bit ) )
* contpc = regs - > cp0_epc +
dec_insn . pc_inc +
( insn . i_format . simmediate < < 2 ) ;
else
* contpc = regs - > cp0_epc +
dec_insn . pc_inc +
dec_insn . next_pc_inc ;
return 1 ;
}
}
2005-04-17 02:20:36 +04:00
break ;
}
return 0 ;
}
/*
* In the Linux kernel , we support selection of FPR format on the
2013-01-22 15:59:30 +04:00
* basis of the Status . FR bit . If an FPU is not present , the FR bit
2009-11-02 22:33:46 +03:00
* is hardwired to zero , which would imply a 32 - bit FPU even for
2013-11-22 17:12:07 +04:00
* 64 - bit CPUs so we rather look at TIF_32BIT_FPREGS .
2012-08-15 21:42:19 +04:00
* FPU emu is slow and bulky and optimizing this function offers fairly
* sizeable benefits so we try to be clever and make this function return
* a constant whenever possible , that is on 64 - bit kernels without O32
2013-11-22 17:12:07 +04:00
* compatibility enabled and on 32 - bit without 64 - bit FPU support .
2005-04-17 02:20:36 +04:00
*/
2009-11-02 22:33:46 +03:00
static inline int cop1_64bit ( struct pt_regs * xcp )
{
2014-04-19 15:11:37 +04:00
if ( config_enabled ( CONFIG_64BIT ) & & ! config_enabled ( CONFIG_MIPS32_O32 ) )
return 1 ;
else if ( config_enabled ( CONFIG_32BIT ) & &
! config_enabled ( CONFIG_MIPS_O32_FP64_SUPPORT ) )
return 0 ;
2013-11-22 17:12:07 +04:00
return ! test_thread_flag ( TIF_32BIT_FPREGS ) ;
2009-11-02 22:33:46 +03:00
}
2014-09-11 11:30:20 +04:00
static inline bool hybrid_fprs ( void )
{
return test_thread_flag ( TIF_HYBRID_FPREGS ) ;
}
2014-04-16 13:00:12 +04:00
# define SIFROMREG(si, x) \
do { \
2014-09-11 11:30:20 +04:00
if ( cop1_64bit ( xcp ) & & ! hybrid_fprs ( ) ) \
2014-09-24 13:45:37 +04:00
( si ) = ( int ) get_fpr32 ( & ctx - > fpr [ x ] , 0 ) ; \
2014-02-13 15:26:41 +04:00
else \
2014-09-24 13:45:37 +04:00
( si ) = ( int ) get_fpr32 ( & ctx - > fpr [ ( x ) & ~ 1 ] , ( x ) & 1 ) ; \
2014-02-13 15:26:41 +04:00
} while ( 0 )
2005-04-17 02:20:36 +04:00
2014-04-16 13:00:12 +04:00
# define SITOREG(si, x) \
do { \
2014-09-11 11:30:20 +04:00
if ( cop1_64bit ( xcp ) & & ! hybrid_fprs ( ) ) { \
2014-01-27 21:14:47 +04:00
unsigned i ; \
2014-02-13 15:26:41 +04:00
set_fpr32 ( & ctx - > fpr [ x ] , 0 , si ) ; \
2014-01-27 21:14:47 +04:00
for ( i = 1 ; i < ARRAY_SIZE ( ctx - > fpr [ x ] . val32 ) ; i + + ) \
set_fpr32 ( & ctx - > fpr [ x ] , i , 0 ) ; \
} else { \
2014-02-13 15:26:41 +04:00
set_fpr32 ( & ctx - > fpr [ ( x ) & ~ 1 ] , ( x ) & 1 , si ) ; \
2014-01-27 21:14:47 +04:00
} \
2014-02-13 15:26:41 +04:00
} while ( 0 )
2005-04-17 02:20:36 +04:00
2014-09-24 13:45:37 +04:00
# define SIFROMHREG(si, x) ((si) = (int)get_fpr32(&ctx->fpr[x], 1))
2014-01-27 21:14:47 +04:00
2014-04-16 13:00:12 +04:00
# define SITOHREG(si, x) \
do { \
2014-01-27 21:14:47 +04:00
unsigned i ; \
set_fpr32 ( & ctx - > fpr [ x ] , 1 , si ) ; \
for ( i = 2 ; i < ARRAY_SIZE ( ctx - > fpr [ x ] . val32 ) ; i + + ) \
set_fpr32 ( & ctx - > fpr [ x ] , i , 0 ) ; \
} while ( 0 )
2013-11-07 16:48:28 +04:00
2014-04-16 13:00:12 +04:00
# define DIFROMREG(di, x) \
2014-02-13 15:26:41 +04:00
( ( di ) = get_fpr64 ( & ctx - > fpr [ ( x ) & ~ ( cop1_64bit ( xcp ) = = 0 ) ] , 0 ) )
2014-04-16 13:00:12 +04:00
# define DITOREG(di, x) \
do { \
2014-01-27 21:14:47 +04:00
unsigned fpr , i ; \
fpr = ( x ) & ~ ( cop1_64bit ( xcp ) = = 0 ) ; \
set_fpr64 ( & ctx - > fpr [ fpr ] , 0 , di ) ; \
for ( i = 1 ; i < ARRAY_SIZE ( ctx - > fpr [ x ] . val64 ) ; i + + ) \
set_fpr64 ( & ctx - > fpr [ fpr ] , i , 0 ) ; \
} while ( 0 )
2005-04-17 02:20:36 +04:00
2007-10-12 02:46:15 +04:00
# define SPFROMREG(sp, x) SIFROMREG((sp).bits, x)
# define SPTOREG(sp, x) SITOREG((sp).bits, x)
# define DPFROMREG(dp, x) DIFROMREG((dp).bits, x)
# define DPTOREG(dp, x) DITOREG((dp).bits, x)
2005-04-17 02:20:36 +04:00
2015-04-04 01:25:04 +03:00
/*
* Emulate a CFC1 instruction .
*/
static inline void cop1_cfc ( struct pt_regs * xcp , struct mips_fpu_struct * ctx ,
mips_instruction ir )
{
u32 value ;
if ( MIPSInst_RD ( ir ) = = FPCREG_CSR ) {
value = ctx - > fcr31 ;
pr_debug ( " %p gpr[%d]<-csr=%08x \n " ,
( void * ) xcp - > cp0_epc ,
MIPSInst_RT ( ir ) , value ) ;
} else if ( MIPSInst_RD ( ir ) = = FPCREG_RID )
value = 0 ;
else
value = 0 ;
if ( MIPSInst_RT ( ir ) )
xcp - > regs [ MIPSInst_RT ( ir ) ] = value ;
}
/*
* Emulate a CTC1 instruction .
*/
static inline void cop1_ctc ( struct pt_regs * xcp , struct mips_fpu_struct * ctx ,
mips_instruction ir )
{
u32 value ;
if ( MIPSInst_RT ( ir ) = = 0 )
value = 0 ;
else
value = xcp - > regs [ MIPSInst_RT ( ir ) ] ;
/* we only have one writable control reg
*/
if ( MIPSInst_RD ( ir ) = = FPCREG_CSR ) {
pr_debug ( " %p gpr[%d]->csr=%08x \n " ,
( void * ) xcp - > cp0_epc ,
MIPSInst_RT ( ir ) , value ) ;
/* Don't write reserved bits. */
ctx - > fcr31 = value & ~ FPU_CSR_RSVD ;
}
}
2005-04-17 02:20:36 +04:00
/*
* Emulate the single floating point instruction pointed at by EPC .
* Two instructions if the instruction is in a branch delay slot .
*/
2010-10-22 03:32:26 +04:00
static int cop1Emulate ( struct pt_regs * xcp , struct mips_fpu_struct * ctx ,
2013-03-25 21:09:02 +04:00
struct mm_decoded_insn dec_insn , void * __user * fault_addr )
2005-04-17 02:20:36 +04:00
{
2013-03-25 21:09:02 +04:00
unsigned long contpc = xcp - > cp0_epc + dec_insn . pc_inc ;
2014-04-26 03:49:14 +04:00
unsigned int cond , cbit ;
mips_instruction ir ;
int likely , pc_inc ;
u32 __user * wva ;
u64 __user * dva ;
u32 wval ;
u64 dval ;
int sig ;
2005-04-17 02:20:36 +04:00
2014-04-30 13:09:44 +04:00
/*
* These are giving gcc a gentle hint about what to expect in
* dec_inst in order to do better optimization .
*/
if ( ! cpu_has_mmips & & dec_insn . micro_mips_mode )
unreachable ( ) ;
2005-04-17 02:20:36 +04:00
/* XXX NEC Vr54xx bug workaround */
2014-04-16 03:59:03 +04:00
if ( delay_slot ( xcp ) ) {
2013-03-25 21:09:02 +04:00
if ( dec_insn . micro_mips_mode ) {
if ( ! mm_isBranchInstr ( xcp , dec_insn , & contpc ) )
2014-04-16 03:59:03 +04:00
clear_delay_slot ( xcp ) ;
2013-03-25 21:09:02 +04:00
} else {
if ( ! isBranchInstr ( xcp , dec_insn , & contpc ) )
2014-04-16 03:59:03 +04:00
clear_delay_slot ( xcp ) ;
2013-03-25 21:09:02 +04:00
}
}
2005-04-17 02:20:36 +04:00
2014-04-16 03:59:03 +04:00
if ( delay_slot ( xcp ) ) {
2005-04-17 02:20:36 +04:00
/*
* The instruction to be emulated is in a branch delay slot
2013-01-22 15:59:30 +04:00
* which means that we have to emulate the branch instruction
2005-04-17 02:20:36 +04:00
* BEFORE we do the cop1 instruction .
*
* This branch could be a COP1 branch , but in that case we
* would have had a trap for that instruction , and would not
* come through this route .
*
* Linux MIPS branch emulator operates on context , updating the
* cp0_epc .
*/
2013-03-25 21:09:02 +04:00
ir = dec_insn . next_insn ; /* process delay slot instr */
pc_inc = dec_insn . next_pc_inc ;
} else {
ir = dec_insn . insn ; /* process current instr */
pc_inc = dec_insn . pc_inc ;
}
2005-04-17 02:20:36 +04:00
2013-03-25 21:09:02 +04:00
/*
* Since microMIPS FPU instructios are a subset of MIPS32 FPU
* instructions , we want to convert microMIPS FPU instructions
* into MIPS32 instructions so that we could reuse all of the
* FPU emulation code .
*
* NOTE : We cannot do this for branch instructions since they
* are not a subset . Example : Cannot emulate a 16 - bit
* aligned target address with a MIPS32 instruction .
*/
if ( dec_insn . micro_mips_mode ) {
/*
* If next instruction is a 16 - bit instruction , then it
* it cannot be a FPU instruction . This could happen
* since we can be called for non - FPU instructions .
*/
if ( ( pc_inc = = 2 ) | |
( microMIPS32_to_MIPS32 ( ( union mips_instruction * ) & ir )
= = SIGILL ) )
2005-04-17 02:20:36 +04:00
return SIGILL ;
}
2014-04-26 03:49:14 +04:00
emul :
2011-06-27 16:41:57 +04:00
perf_sw_event ( PERF_COUNT_SW_EMULATION_FAULTS , 1 , xcp , 0 ) ;
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( emulated ) ;
2005-04-17 02:20:36 +04:00
switch ( MIPSInst_OPCODE ( ir ) ) {
2014-04-26 03:49:14 +04:00
case ldc1_op :
dva = ( u64 __user * ) ( xcp - > regs [ MIPSInst_RS ( ir ) ] +
MIPSInst_SIMM ( ir ) ) ;
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( loads ) ;
2010-10-22 03:32:26 +04:00
2014-04-26 03:49:14 +04:00
if ( ! access_ok ( VERIFY_READ , dva , sizeof ( u64 ) ) ) {
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( errors ) ;
2014-04-26 03:49:14 +04:00
* fault_addr = dva ;
2005-04-17 02:20:36 +04:00
return SIGBUS ;
}
2014-04-26 03:49:14 +04:00
if ( __get_user ( dval , dva ) ) {
2010-10-22 03:32:26 +04:00
MIPS_FPU_EMU_INC_STATS ( errors ) ;
2014-04-26 03:49:14 +04:00
* fault_addr = dva ;
2010-10-22 03:32:26 +04:00
return SIGSEGV ;
}
2014-04-26 03:49:14 +04:00
DITOREG ( dval , MIPSInst_RT ( ir ) ) ;
2005-04-17 02:20:36 +04:00
break ;
2014-04-26 03:49:14 +04:00
case sdc1_op :
dva = ( u64 __user * ) ( xcp - > regs [ MIPSInst_RS ( ir ) ] +
MIPSInst_SIMM ( ir ) ) ;
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( stores ) ;
2014-04-26 03:49:14 +04:00
DIFROMREG ( dval , MIPSInst_RT ( ir ) ) ;
if ( ! access_ok ( VERIFY_WRITE , dva , sizeof ( u64 ) ) ) {
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( errors ) ;
2014-04-26 03:49:14 +04:00
* fault_addr = dva ;
2005-04-17 02:20:36 +04:00
return SIGBUS ;
}
2014-04-26 03:49:14 +04:00
if ( __put_user ( dval , dva ) ) {
2010-10-22 03:32:26 +04:00
MIPS_FPU_EMU_INC_STATS ( errors ) ;
2014-04-26 03:49:14 +04:00
* fault_addr = dva ;
2010-10-22 03:32:26 +04:00
return SIGSEGV ;
}
2005-04-17 02:20:36 +04:00
break ;
2014-04-26 03:49:14 +04:00
case lwc1_op :
wva = ( u32 __user * ) ( xcp - > regs [ MIPSInst_RS ( ir ) ] +
MIPSInst_SIMM ( ir ) ) ;
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( loads ) ;
2014-04-26 03:49:14 +04:00
if ( ! access_ok ( VERIFY_READ , wva , sizeof ( u32 ) ) ) {
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( errors ) ;
2014-04-26 03:49:14 +04:00
* fault_addr = wva ;
2005-04-17 02:20:36 +04:00
return SIGBUS ;
}
2014-04-26 03:49:14 +04:00
if ( __get_user ( wval , wva ) ) {
2010-10-22 03:32:26 +04:00
MIPS_FPU_EMU_INC_STATS ( errors ) ;
2014-04-26 03:49:14 +04:00
* fault_addr = wva ;
2010-10-22 03:32:26 +04:00
return SIGSEGV ;
}
2014-04-26 03:49:14 +04:00
SITOREG ( wval , MIPSInst_RT ( ir ) ) ;
2005-04-17 02:20:36 +04:00
break ;
2014-04-26 03:49:14 +04:00
case swc1_op :
wva = ( u32 __user * ) ( xcp - > regs [ MIPSInst_RS ( ir ) ] +
MIPSInst_SIMM ( ir ) ) ;
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( stores ) ;
2014-04-26 03:49:14 +04:00
SIFROMREG ( wval , MIPSInst_RT ( ir ) ) ;
if ( ! access_ok ( VERIFY_WRITE , wva , sizeof ( u32 ) ) ) {
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( errors ) ;
2014-04-26 03:49:14 +04:00
* fault_addr = wva ;
2005-04-17 02:20:36 +04:00
return SIGBUS ;
}
2014-04-26 03:49:14 +04:00
if ( __put_user ( wval , wva ) ) {
2010-10-22 03:32:26 +04:00
MIPS_FPU_EMU_INC_STATS ( errors ) ;
2014-04-26 03:49:14 +04:00
* fault_addr = wva ;
2010-10-22 03:32:26 +04:00
return SIGSEGV ;
}
2005-04-17 02:20:36 +04:00
break ;
case cop1_op :
switch ( MIPSInst_RS ( ir ) ) {
case dmfc_op :
2014-04-19 15:11:37 +04:00
if ( ! cpu_has_mips_3_4_5 & & ! cpu_has_mips64 )
return SIGILL ;
2005-04-17 02:20:36 +04:00
/* copregister fs -> gpr[rt] */
if ( MIPSInst_RT ( ir ) ! = 0 ) {
DIFROMREG ( xcp - > regs [ MIPSInst_RT ( ir ) ] ,
MIPSInst_RD ( ir ) ) ;
}
break ;
case dmtc_op :
2014-04-19 15:11:37 +04:00
if ( ! cpu_has_mips_3_4_5 & & ! cpu_has_mips64 )
return SIGILL ;
2005-04-17 02:20:36 +04:00
/* copregister fs <- rt */
DITOREG ( xcp - > regs [ MIPSInst_RT ( ir ) ] , MIPSInst_RD ( ir ) ) ;
break ;
2013-11-07 16:48:28 +04:00
case mfhc_op :
if ( ! cpu_has_mips_r2 )
goto sigill ;
/* copregister rd -> gpr[rt] */
if ( MIPSInst_RT ( ir ) ! = 0 ) {
SIFROMHREG ( xcp - > regs [ MIPSInst_RT ( ir ) ] ,
MIPSInst_RD ( ir ) ) ;
}
break ;
case mthc_op :
if ( ! cpu_has_mips_r2 )
goto sigill ;
/* copregister rd <- gpr[rt] */
SITOHREG ( xcp - > regs [ MIPSInst_RT ( ir ) ] , MIPSInst_RD ( ir ) ) ;
break ;
2005-04-17 02:20:36 +04:00
case mfc_op :
/* copregister rd -> gpr[rt] */
if ( MIPSInst_RT ( ir ) ! = 0 ) {
SIFROMREG ( xcp - > regs [ MIPSInst_RT ( ir ) ] ,
MIPSInst_RD ( ir ) ) ;
}
break ;
case mtc_op :
/* copregister rd <- rt */
SITOREG ( xcp - > regs [ MIPSInst_RT ( ir ) ] , MIPSInst_RD ( ir ) ) ;
break ;
2014-04-26 03:49:14 +04:00
case cfc_op :
2005-04-17 02:20:36 +04:00
/* cop control register rd -> gpr[rt] */
2015-04-04 01:25:04 +03:00
cop1_cfc ( xcp , ctx , ir ) ;
2005-04-17 02:20:36 +04:00
break ;
2014-04-26 03:49:14 +04:00
case ctc_op :
2005-04-17 02:20:36 +04:00
/* copregister rd <- rt */
2015-04-04 01:25:04 +03:00
cop1_ctc ( xcp , ctx , ir ) ;
2005-04-17 02:20:36 +04:00
if ( ( ctx - > fcr31 > > 5 ) & ctx - > fcr31 & FPU_CSR_ALL_E ) {
return SIGFPE ;
}
break ;
2014-04-26 03:49:14 +04:00
case bc_op :
2014-04-16 03:59:03 +04:00
if ( delay_slot ( xcp ) )
2005-04-17 02:20:36 +04:00
return SIGILL ;
2014-04-19 15:11:37 +04:00
if ( cpu_has_mips_4_5_r )
cbit = fpucondbit [ MIPSInst_RT ( ir ) > > 2 ] ;
else
cbit = FPU_CSR_COND ;
cond = ctx - > fcr31 & cbit ;
2014-04-26 03:49:14 +04:00
likely = 0 ;
2005-04-17 02:20:36 +04:00
switch ( MIPSInst_RT ( ir ) & 3 ) {
case bcfl_op :
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
if ( cpu_has_mips_2_3_4_5_r )
likely = 1 ;
/* Fall through */
2005-04-17 02:20:36 +04:00
case bcf_op :
cond = ! cond ;
break ;
case bctl_op :
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
if ( cpu_has_mips_2_3_4_5_r )
likely = 1 ;
/* Fall through */
2005-04-17 02:20:36 +04:00
case bct_op :
break ;
}
2014-04-16 03:59:03 +04:00
set_delay_slot ( xcp ) ;
2005-04-17 02:20:36 +04:00
if ( cond ) {
2014-04-26 03:49:14 +04:00
/*
* Branch taken : emulate dslot instruction
2005-04-17 02:20:36 +04:00
*/
2015-04-04 01:26:56 +03:00
unsigned long bcpc ;
/*
* Remember EPC at the branch to point back
* at so that any delay - slot instruction
* signal is not silently ignored .
*/
bcpc = xcp - > cp0_epc ;
2013-03-25 21:09:02 +04:00
xcp - > cp0_epc + = dec_insn . pc_inc ;
contpc = MIPSInst_SIMM ( ir ) ;
ir = dec_insn . next_insn ;
if ( dec_insn . micro_mips_mode ) {
contpc = ( xcp - > cp0_epc + ( contpc < < 1 ) ) ;
/* If 16-bit instruction, not FPU. */
if ( ( dec_insn . next_pc_inc = = 2 ) | |
( microMIPS32_to_MIPS32 ( ( union mips_instruction * ) & ir ) = = SIGILL ) ) {
/*
* Since this instruction will
* be put on the stack with
* 32 - bit words , get around
* this problem by putting a
* NOP16 as the second one .
*/
if ( dec_insn . next_pc_inc = = 2 )
ir = ( ir & ( ~ 0xffff ) ) | MM_NOP16 ;
/*
* Single step the non - CP1
* instruction in the dslot .
*/
2015-04-04 01:26:56 +03:00
sig = mips_dsemul ( xcp , ir ,
contpc ) ;
if ( sig )
xcp - > cp0_epc = bcpc ;
/*
* SIGILL forces out of
* the emulation loop .
*/
return sig ? sig : SIGILL ;
2013-03-25 21:09:02 +04:00
}
} else
contpc = ( xcp - > cp0_epc + ( contpc < < 2 ) ) ;
2005-04-17 02:20:36 +04:00
switch ( MIPSInst_OPCODE ( ir ) ) {
case lwc1_op :
case swc1_op :
2014-04-19 15:11:37 +04:00
goto emul ;
2014-04-26 03:49:14 +04:00
2005-04-17 02:20:36 +04:00
case ldc1_op :
case sdc1_op :
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
if ( cpu_has_mips_2_3_4_5_r )
2014-04-19 15:11:37 +04:00
goto emul ;
2015-04-04 01:26:56 +03:00
goto bc_sigill ;
2014-04-26 03:49:14 +04:00
2005-04-17 02:20:36 +04:00
case cop1_op :
goto emul ;
2014-04-26 03:49:14 +04:00
2014-04-19 15:11:37 +04:00
case cop1x_op :
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
if ( cpu_has_mips_4_5_64_r2_r6 )
2014-04-19 15:11:37 +04:00
/* its one of ours */
goto emul ;
2015-04-04 01:26:56 +03:00
goto bc_sigill ;
2014-04-26 03:49:14 +04:00
2005-04-17 02:20:36 +04:00
case spec_op :
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
switch ( MIPSInst_FUNC ( ir ) ) {
case movc_op :
if ( cpu_has_mips_4_5_r )
goto emul ;
2014-04-19 15:11:37 +04:00
2015-04-04 01:26:56 +03:00
goto bc_sigill ;
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
}
2005-04-17 02:20:36 +04:00
break ;
2015-04-04 01:26:56 +03:00
bc_sigill :
xcp - > cp0_epc = bcpc ;
return SIGILL ;
2005-04-17 02:20:36 +04:00
}
/*
* Single step the non - cp1
* instruction in the dslot
*/
2015-04-04 01:26:56 +03:00
sig = mips_dsemul ( xcp , ir , contpc ) ;
if ( sig )
xcp - > cp0_epc = bcpc ;
/* SIGILL forces out of the emulation loop. */
return sig ? sig : SIGILL ;
2014-04-26 03:49:14 +04:00
} else if ( likely ) { /* branch not taken */
2015-04-04 01:24:24 +03:00
/*
* branch likely nullifies
* dslot if not taken
*/
xcp - > cp0_epc + = dec_insn . pc_inc ;
contpc + = dec_insn . pc_inc ;
/*
* else continue & execute
* dslot as normal insn
*/
}
2005-04-17 02:20:36 +04:00
break ;
default :
if ( ! ( MIPSInst_RS ( ir ) & 0x10 ) )
return SIGILL ;
2014-04-26 03:49:14 +04:00
/* a real fpu computation instruction */
if ( ( sig = fpu_emu ( xcp , ctx , ir ) ) )
return sig ;
2005-04-17 02:20:36 +04:00
}
break ;
2014-04-26 03:49:14 +04:00
case cop1x_op :
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
if ( ! cpu_has_mips_4_5_64_r2_r6 )
2014-04-19 15:11:37 +04:00
return SIGILL ;
sig = fpux_emu ( xcp , ctx , ir , fault_addr ) ;
2010-10-22 03:32:26 +04:00
if ( sig )
2005-04-17 02:20:36 +04:00
return sig ;
break ;
case spec_op :
2014-04-19 15:11:37 +04:00
if ( ! cpu_has_mips_4_5_r )
return SIGILL ;
2005-04-17 02:20:36 +04:00
if ( MIPSInst_FUNC ( ir ) ! = movc_op )
return SIGILL ;
cond = fpucondbit [ MIPSInst_RT ( ir ) > > 2 ] ;
if ( ( ( ctx - > fcr31 & cond ) ! = 0 ) = = ( ( MIPSInst_RT ( ir ) & 1 ) ! = 0 ) )
xcp - > regs [ MIPSInst_RD ( ir ) ] =
xcp - > regs [ MIPSInst_RS ( ir ) ] ;
break ;
default :
2013-11-07 16:48:28 +04:00
sigill :
2005-04-17 02:20:36 +04:00
return SIGILL ;
}
/* we did it !! */
2007-07-13 18:02:29 +04:00
xcp - > cp0_epc = contpc ;
2014-04-16 03:59:03 +04:00
clear_delay_slot ( xcp ) ;
2005-02-28 20:55:57 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Conversion table from MIPS compare ops 48 - 63
* cond = ieee754dp_cmp ( x , y , IEEE754_UN , sig ) ;
*/
static const unsigned char cmptab [ 8 ] = {
0 , /* cmp_0 (sig) cmp_sf */
IEEE754_CUN , /* cmp_un (sig) cmp_ngle */
IEEE754_CEQ , /* cmp_eq (sig) cmp_seq */
IEEE754_CEQ | IEEE754_CUN , /* cmp_ueq (sig) cmp_ngl */
IEEE754_CLT , /* cmp_olt (sig) cmp_lt */
IEEE754_CLT | IEEE754_CUN , /* cmp_ult (sig) cmp_nge */
IEEE754_CLT | IEEE754_CEQ , /* cmp_ole (sig) cmp_le */
IEEE754_CLT | IEEE754_CEQ | IEEE754_CUN , /* cmp_ule (sig) cmp_ngt */
} ;
/*
* Additional MIPS4 instructions
*/
2014-04-16 13:00:12 +04:00
# define DEF3OP(name, p, f1, f2, f3) \
static union ieee754 # # p fpemu_ # # p # # _ # # name ( union ieee754 # # p r , \
union ieee754 # # p s , union ieee754 # # p t ) \
{ \
struct _ieee754_csr ieee754_csr_save ; \
s = f1 ( s , t ) ; \
ieee754_csr_save = ieee754_csr ; \
s = f2 ( s , r ) ; \
ieee754_csr_save . cx | = ieee754_csr . cx ; \
ieee754_csr_save . sx | = ieee754_csr . sx ; \
s = f3 ( s ) ; \
ieee754_csr . cx | = ieee754_csr_save . cx ; \
ieee754_csr . sx | = ieee754_csr_save . sx ; \
return s ; \
2005-04-17 02:20:36 +04:00
}
2014-04-16 03:31:11 +04:00
static union ieee754dp fpemu_dp_recip ( union ieee754dp d )
2005-04-17 02:20:36 +04:00
{
return ieee754dp_div ( ieee754dp_one ( 0 ) , d ) ;
}
2014-04-16 03:31:11 +04:00
static union ieee754dp fpemu_dp_rsqrt ( union ieee754dp d )
2005-04-17 02:20:36 +04:00
{
return ieee754dp_div ( ieee754dp_one ( 0 ) , ieee754dp_sqrt ( d ) ) ;
}
2014-04-16 03:31:11 +04:00
static union ieee754sp fpemu_sp_recip ( union ieee754sp s )
2005-04-17 02:20:36 +04:00
{
return ieee754sp_div ( ieee754sp_one ( 0 ) , s ) ;
}
2014-04-16 03:31:11 +04:00
static union ieee754sp fpemu_sp_rsqrt ( union ieee754sp s )
2005-04-17 02:20:36 +04:00
{
return ieee754sp_div ( ieee754sp_one ( 0 ) , ieee754sp_sqrt ( s ) ) ;
}
2007-10-12 02:46:15 +04:00
DEF3OP ( madd , sp , ieee754sp_mul , ieee754sp_add , ) ;
DEF3OP ( msub , sp , ieee754sp_mul , ieee754sp_sub , ) ;
2005-04-17 02:20:36 +04:00
DEF3OP ( nmadd , sp , ieee754sp_mul , ieee754sp_add , ieee754sp_neg ) ;
DEF3OP ( nmsub , sp , ieee754sp_mul , ieee754sp_sub , ieee754sp_neg ) ;
2007-10-12 02:46:15 +04:00
DEF3OP ( madd , dp , ieee754dp_mul , ieee754dp_add , ) ;
DEF3OP ( msub , dp , ieee754dp_mul , ieee754dp_sub , ) ;
2005-04-17 02:20:36 +04:00
DEF3OP ( nmadd , dp , ieee754dp_mul , ieee754dp_add , ieee754dp_neg ) ;
DEF3OP ( nmsub , dp , ieee754dp_mul , ieee754dp_sub , ieee754dp_neg ) ;
2006-05-15 20:26:03 +04:00
static int fpux_emu ( struct pt_regs * xcp , struct mips_fpu_struct * ctx ,
2010-10-22 03:32:26 +04:00
mips_instruction ir , void * __user * fault_addr )
2005-04-17 02:20:36 +04:00
{
unsigned rcsr = 0 ; /* resulting csr */
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( cp1xops ) ;
2005-04-17 02:20:36 +04:00
switch ( MIPSInst_FMA_FFMT ( ir ) ) {
case s_fmt : { /* 0 */
2014-04-16 03:31:11 +04:00
union ieee754sp ( * handler ) ( union ieee754sp , union ieee754sp , union ieee754sp ) ;
union ieee754sp fd , fr , fs , ft ;
2005-10-23 16:58:21 +04:00
u32 __user * va ;
2005-04-17 02:20:36 +04:00
u32 val ;
switch ( MIPSInst_FUNC ( ir ) ) {
case lwxc1_op :
2005-10-23 16:58:21 +04:00
va = ( void __user * ) ( xcp - > regs [ MIPSInst_FR ( ir ) ] +
2005-04-17 02:20:36 +04:00
xcp - > regs [ MIPSInst_FT ( ir ) ] ) ;
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( loads ) ;
2010-10-22 03:32:26 +04:00
if ( ! access_ok ( VERIFY_READ , va , sizeof ( u32 ) ) ) {
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( errors ) ;
2010-10-22 03:32:26 +04:00
* fault_addr = va ;
2005-04-17 02:20:36 +04:00
return SIGBUS ;
}
2010-10-22 03:32:26 +04:00
if ( __get_user ( val , va ) ) {
MIPS_FPU_EMU_INC_STATS ( errors ) ;
* fault_addr = va ;
return SIGSEGV ;
}
2005-04-17 02:20:36 +04:00
SITOREG ( val , MIPSInst_FD ( ir ) ) ;
break ;
case swxc1_op :
2005-10-23 16:58:21 +04:00
va = ( void __user * ) ( xcp - > regs [ MIPSInst_FR ( ir ) ] +
2005-04-17 02:20:36 +04:00
xcp - > regs [ MIPSInst_FT ( ir ) ] ) ;
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( stores ) ;
2005-04-17 02:20:36 +04:00
SIFROMREG ( val , MIPSInst_FS ( ir ) ) ;
2010-10-22 03:32:26 +04:00
if ( ! access_ok ( VERIFY_WRITE , va , sizeof ( u32 ) ) ) {
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( errors ) ;
2010-10-22 03:32:26 +04:00
* fault_addr = va ;
2005-04-17 02:20:36 +04:00
return SIGBUS ;
}
2010-10-22 03:32:26 +04:00
if ( put_user ( val , va ) ) {
MIPS_FPU_EMU_INC_STATS ( errors ) ;
* fault_addr = va ;
return SIGSEGV ;
}
2005-04-17 02:20:36 +04:00
break ;
case madd_s_op :
handler = fpemu_sp_madd ;
goto scoptop ;
case msub_s_op :
handler = fpemu_sp_msub ;
goto scoptop ;
case nmadd_s_op :
handler = fpemu_sp_nmadd ;
goto scoptop ;
case nmsub_s_op :
handler = fpemu_sp_nmsub ;
goto scoptop ;
scoptop :
SPFROMREG ( fr , MIPSInst_FR ( ir ) ) ;
SPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
SPFROMREG ( ft , MIPSInst_FT ( ir ) ) ;
fd = ( * handler ) ( fr , fs , ft ) ;
SPTOREG ( fd , MIPSInst_FD ( ir ) ) ;
copcsr :
2014-05-29 23:26:45 +04:00
if ( ieee754_cxtest ( IEEE754_INEXACT ) ) {
MIPS_FPU_EMU_INC_STATS ( ieee754_inexact ) ;
2005-04-17 02:20:36 +04:00
rcsr | = FPU_CSR_INE_X | FPU_CSR_INE_S ;
2014-05-29 23:26:45 +04:00
}
if ( ieee754_cxtest ( IEEE754_UNDERFLOW ) ) {
MIPS_FPU_EMU_INC_STATS ( ieee754_underflow ) ;
2005-04-17 02:20:36 +04:00
rcsr | = FPU_CSR_UDF_X | FPU_CSR_UDF_S ;
2014-05-29 23:26:45 +04:00
}
if ( ieee754_cxtest ( IEEE754_OVERFLOW ) ) {
MIPS_FPU_EMU_INC_STATS ( ieee754_overflow ) ;
2005-04-17 02:20:36 +04:00
rcsr | = FPU_CSR_OVF_X | FPU_CSR_OVF_S ;
2014-05-29 23:26:45 +04:00
}
if ( ieee754_cxtest ( IEEE754_INVALID_OPERATION ) ) {
MIPS_FPU_EMU_INC_STATS ( ieee754_invalidop ) ;
2005-04-17 02:20:36 +04:00
rcsr | = FPU_CSR_INV_X | FPU_CSR_INV_S ;
2014-05-29 23:26:45 +04:00
}
2005-04-17 02:20:36 +04:00
ctx - > fcr31 = ( ctx - > fcr31 & ~ FPU_CSR_ALL_X ) | rcsr ;
if ( ( ctx - > fcr31 > > 5 ) & ctx - > fcr31 & FPU_CSR_ALL_E ) {
2014-04-26 03:49:14 +04:00
/*printk ("SIGFPE: FPU csr = %08x\n",
2005-04-17 02:20:36 +04:00
ctx - > fcr31 ) ; */
return SIGFPE ;
}
break ;
default :
return SIGILL ;
}
break ;
}
case d_fmt : { /* 1 */
2014-04-16 03:31:11 +04:00
union ieee754dp ( * handler ) ( union ieee754dp , union ieee754dp , union ieee754dp ) ;
union ieee754dp fd , fr , fs , ft ;
2005-10-23 16:58:21 +04:00
u64 __user * va ;
2005-04-17 02:20:36 +04:00
u64 val ;
switch ( MIPSInst_FUNC ( ir ) ) {
case ldxc1_op :
2005-10-23 16:58:21 +04:00
va = ( void __user * ) ( xcp - > regs [ MIPSInst_FR ( ir ) ] +
2005-04-17 02:20:36 +04:00
xcp - > regs [ MIPSInst_FT ( ir ) ] ) ;
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( loads ) ;
2010-10-22 03:32:26 +04:00
if ( ! access_ok ( VERIFY_READ , va , sizeof ( u64 ) ) ) {
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( errors ) ;
2010-10-22 03:32:26 +04:00
* fault_addr = va ;
2005-04-17 02:20:36 +04:00
return SIGBUS ;
}
2010-10-22 03:32:26 +04:00
if ( __get_user ( val , va ) ) {
MIPS_FPU_EMU_INC_STATS ( errors ) ;
* fault_addr = va ;
return SIGSEGV ;
}
2005-04-17 02:20:36 +04:00
DITOREG ( val , MIPSInst_FD ( ir ) ) ;
break ;
case sdxc1_op :
2005-10-23 16:58:21 +04:00
va = ( void __user * ) ( xcp - > regs [ MIPSInst_FR ( ir ) ] +
2005-04-17 02:20:36 +04:00
xcp - > regs [ MIPSInst_FT ( ir ) ] ) ;
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( stores ) ;
2005-04-17 02:20:36 +04:00
DIFROMREG ( val , MIPSInst_FS ( ir ) ) ;
2010-10-22 03:32:26 +04:00
if ( ! access_ok ( VERIFY_WRITE , va , sizeof ( u64 ) ) ) {
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( errors ) ;
2010-10-22 03:32:26 +04:00
* fault_addr = va ;
2005-04-17 02:20:36 +04:00
return SIGBUS ;
}
2010-10-22 03:32:26 +04:00
if ( __put_user ( val , va ) ) {
MIPS_FPU_EMU_INC_STATS ( errors ) ;
* fault_addr = va ;
return SIGSEGV ;
}
2005-04-17 02:20:36 +04:00
break ;
case madd_d_op :
handler = fpemu_dp_madd ;
goto dcoptop ;
case msub_d_op :
handler = fpemu_dp_msub ;
goto dcoptop ;
case nmadd_d_op :
handler = fpemu_dp_nmadd ;
goto dcoptop ;
case nmsub_d_op :
handler = fpemu_dp_nmsub ;
goto dcoptop ;
dcoptop :
DPFROMREG ( fr , MIPSInst_FR ( ir ) ) ;
DPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
DPFROMREG ( ft , MIPSInst_FT ( ir ) ) ;
fd = ( * handler ) ( fr , fs , ft ) ;
DPTOREG ( fd , MIPSInst_FD ( ir ) ) ;
goto copcsr ;
default :
return SIGILL ;
}
break ;
}
2014-03-07 05:05:27 +04:00
case 0x3 :
if ( MIPSInst_FUNC ( ir ) ! = pfetch_op )
2005-04-17 02:20:36 +04:00
return SIGILL ;
2014-03-07 05:05:27 +04:00
2005-04-17 02:20:36 +04:00
/* ignore prefx operation */
break ;
default :
return SIGILL ;
}
return 0 ;
}
/*
* Emulate a single COP1 arithmetic instruction .
*/
2006-05-15 20:26:03 +04:00
static int fpu_emu ( struct pt_regs * xcp , struct mips_fpu_struct * ctx ,
2005-04-17 02:20:36 +04:00
mips_instruction ir )
{
int rfmt ; /* resulting format */
unsigned rcsr = 0 ; /* resulting csr */
2014-04-26 03:49:14 +04:00
unsigned int oldrm ;
unsigned int cbit ;
2005-04-17 02:20:36 +04:00
unsigned cond ;
union {
2014-04-16 03:31:11 +04:00
union ieee754dp d ;
union ieee754sp s ;
2005-04-17 02:20:36 +04:00
int w ;
s64 l ;
} rv ; /* resulting value */
2014-04-26 03:49:14 +04:00
u64 bits ;
2005-04-17 02:20:36 +04:00
2009-11-05 22:34:26 +03:00
MIPS_FPU_EMU_INC_STATS ( cp1ops ) ;
2005-04-17 02:20:36 +04:00
switch ( rfmt = ( MIPSInst_FFMT ( ir ) & 0xf ) ) {
2014-04-26 03:49:14 +04:00
case s_fmt : { /* 0 */
2005-04-17 02:20:36 +04:00
union {
2014-04-16 03:31:11 +04:00
union ieee754sp ( * b ) ( union ieee754sp , union ieee754sp ) ;
union ieee754sp ( * u ) ( union ieee754sp ) ;
2005-04-17 02:20:36 +04:00
} handler ;
2014-04-26 03:49:14 +04:00
union ieee754sp fs , ft ;
2005-04-17 02:20:36 +04:00
switch ( MIPSInst_FUNC ( ir ) ) {
/* binary ops */
case fadd_op :
handler . b = ieee754sp_add ;
goto scopbop ;
case fsub_op :
handler . b = ieee754sp_sub ;
goto scopbop ;
case fmul_op :
handler . b = ieee754sp_mul ;
goto scopbop ;
case fdiv_op :
handler . b = ieee754sp_div ;
goto scopbop ;
/* unary ops */
case fsqrt_op :
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
if ( ! cpu_has_mips_2_3_4_5_r )
2014-04-19 15:11:37 +04:00
return SIGILL ;
2005-04-17 02:20:36 +04:00
handler . u = ieee754sp_sqrt ;
goto scopuop ;
2014-04-26 03:49:14 +04:00
2014-04-19 15:11:37 +04:00
/*
* Note that on some MIPS IV implementations such as the
* R5000 and R8000 the FSQRT and FRECIP instructions do not
* achieve full IEEE - 754 accuracy - however this emulator does .
*/
2005-04-17 02:20:36 +04:00
case frsqrt_op :
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
if ( ! cpu_has_mips_4_5_64_r2_r6 )
2014-04-19 15:11:37 +04:00
return SIGILL ;
2005-04-17 02:20:36 +04:00
handler . u = fpemu_sp_rsqrt ;
goto scopuop ;
2014-04-26 03:49:14 +04:00
2005-04-17 02:20:36 +04:00
case frecip_op :
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
if ( ! cpu_has_mips_4_5_64_r2_r6 )
2014-04-19 15:11:37 +04:00
return SIGILL ;
2005-04-17 02:20:36 +04:00
handler . u = fpemu_sp_recip ;
goto scopuop ;
2014-04-19 15:11:37 +04:00
2005-04-17 02:20:36 +04:00
case fmovc_op :
2014-04-19 15:11:37 +04:00
if ( ! cpu_has_mips_4_5_r )
return SIGILL ;
2005-04-17 02:20:36 +04:00
cond = fpucondbit [ MIPSInst_FT ( ir ) > > 2 ] ;
if ( ( ( ctx - > fcr31 & cond ) ! = 0 ) ! =
( ( MIPSInst_FT ( ir ) & 1 ) ! = 0 ) )
return 0 ;
SPFROMREG ( rv . s , MIPSInst_FS ( ir ) ) ;
break ;
2014-04-26 03:49:14 +04:00
2005-04-17 02:20:36 +04:00
case fmovz_op :
2014-04-19 15:11:37 +04:00
if ( ! cpu_has_mips_4_5_r )
return SIGILL ;
2005-04-17 02:20:36 +04:00
if ( xcp - > regs [ MIPSInst_FT ( ir ) ] ! = 0 )
return 0 ;
SPFROMREG ( rv . s , MIPSInst_FS ( ir ) ) ;
break ;
2014-04-26 03:49:14 +04:00
2005-04-17 02:20:36 +04:00
case fmovn_op :
2014-04-19 15:11:37 +04:00
if ( ! cpu_has_mips_4_5_r )
return SIGILL ;
2005-04-17 02:20:36 +04:00
if ( xcp - > regs [ MIPSInst_FT ( ir ) ] = = 0 )
return 0 ;
SPFROMREG ( rv . s , MIPSInst_FS ( ir ) ) ;
break ;
2014-04-26 03:49:14 +04:00
2005-04-17 02:20:36 +04:00
case fabs_op :
handler . u = ieee754sp_abs ;
goto scopuop ;
2014-04-26 03:49:14 +04:00
2005-04-17 02:20:36 +04:00
case fneg_op :
handler . u = ieee754sp_neg ;
goto scopuop ;
2014-04-26 03:49:14 +04:00
2005-04-17 02:20:36 +04:00
case fmov_op :
/* an easy one */
SPFROMREG ( rv . s , MIPSInst_FS ( ir ) ) ;
goto copcsr ;
/* binary op on handler */
2014-04-26 03:49:14 +04:00
scopbop :
SPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
SPFROMREG ( ft , MIPSInst_FT ( ir ) ) ;
2005-04-17 02:20:36 +04:00
2014-04-26 03:49:14 +04:00
rv . s = ( * handler . b ) ( fs , ft ) ;
goto copcsr ;
scopuop :
SPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
rv . s = ( * handler . u ) ( fs ) ;
goto copcsr ;
copcsr :
2014-05-29 23:26:45 +04:00
if ( ieee754_cxtest ( IEEE754_INEXACT ) ) {
MIPS_FPU_EMU_INC_STATS ( ieee754_inexact ) ;
2005-04-17 02:20:36 +04:00
rcsr | = FPU_CSR_INE_X | FPU_CSR_INE_S ;
2014-05-29 23:26:45 +04:00
}
if ( ieee754_cxtest ( IEEE754_UNDERFLOW ) ) {
MIPS_FPU_EMU_INC_STATS ( ieee754_underflow ) ;
2005-04-17 02:20:36 +04:00
rcsr | = FPU_CSR_UDF_X | FPU_CSR_UDF_S ;
2014-05-29 23:26:45 +04:00
}
if ( ieee754_cxtest ( IEEE754_OVERFLOW ) ) {
MIPS_FPU_EMU_INC_STATS ( ieee754_overflow ) ;
2005-04-17 02:20:36 +04:00
rcsr | = FPU_CSR_OVF_X | FPU_CSR_OVF_S ;
2014-05-29 23:26:45 +04:00
}
if ( ieee754_cxtest ( IEEE754_ZERO_DIVIDE ) ) {
MIPS_FPU_EMU_INC_STATS ( ieee754_zerodiv ) ;
2005-04-17 02:20:36 +04:00
rcsr | = FPU_CSR_DIV_X | FPU_CSR_DIV_S ;
2014-05-29 23:26:45 +04:00
}
if ( ieee754_cxtest ( IEEE754_INVALID_OPERATION ) ) {
MIPS_FPU_EMU_INC_STATS ( ieee754_invalidop ) ;
2005-04-17 02:20:36 +04:00
rcsr | = FPU_CSR_INV_X | FPU_CSR_INV_S ;
2014-05-29 23:26:45 +04:00
}
2005-04-17 02:20:36 +04:00
break ;
/* unary conv ops */
case fcvts_op :
return SIGILL ; /* not defined */
2014-04-26 03:49:14 +04:00
case fcvtd_op :
2005-04-17 02:20:36 +04:00
SPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
rv . d = ieee754dp_fsp ( fs ) ;
rfmt = d_fmt ;
goto copcsr ;
2014-04-26 03:49:14 +04:00
case fcvtw_op :
2005-04-17 02:20:36 +04:00
SPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
rv . w = ieee754sp_tint ( fs ) ;
rfmt = w_fmt ;
goto copcsr ;
case fround_op :
case ftrunc_op :
case fceil_op :
2014-04-26 03:49:14 +04:00
case ffloor_op :
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
if ( ! cpu_has_mips_2_3_4_5_r )
2014-04-19 15:11:37 +04:00
return SIGILL ;
2014-04-26 03:49:14 +04:00
oldrm = ieee754_csr . rm ;
2005-04-17 02:20:36 +04:00
SPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
2015-04-04 01:24:56 +03:00
ieee754_csr . rm = MIPSInst_FUNC ( ir ) ;
2005-04-17 02:20:36 +04:00
rv . w = ieee754sp_tint ( fs ) ;
ieee754_csr . rm = oldrm ;
rfmt = w_fmt ;
goto copcsr ;
2014-04-26 03:49:14 +04:00
case fcvtl_op :
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
if ( ! cpu_has_mips_3_4_5_64_r2_r6 )
2014-04-19 15:11:37 +04:00
return SIGILL ;
2005-04-17 02:20:36 +04:00
SPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
rv . l = ieee754sp_tlong ( fs ) ;
rfmt = l_fmt ;
goto copcsr ;
case froundl_op :
case ftruncl_op :
case fceill_op :
2014-04-26 03:49:14 +04:00
case ffloorl_op :
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
if ( ! cpu_has_mips_3_4_5_64_r2_r6 )
2014-04-19 15:11:37 +04:00
return SIGILL ;
2014-04-26 03:49:14 +04:00
oldrm = ieee754_csr . rm ;
2005-04-17 02:20:36 +04:00
SPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
2015-04-04 01:24:56 +03:00
ieee754_csr . rm = MIPSInst_FUNC ( ir ) ;
2005-04-17 02:20:36 +04:00
rv . l = ieee754sp_tlong ( fs ) ;
ieee754_csr . rm = oldrm ;
rfmt = l_fmt ;
goto copcsr ;
default :
if ( MIPSInst_FUNC ( ir ) > = fcmp_op ) {
unsigned cmpop = MIPSInst_FUNC ( ir ) - fcmp_op ;
2014-04-16 03:31:11 +04:00
union ieee754sp fs , ft ;
2005-04-17 02:20:36 +04:00
SPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
SPFROMREG ( ft , MIPSInst_FT ( ir ) ) ;
rv . w = ieee754sp_cmp ( fs , ft ,
cmptab [ cmpop & 0x7 ] , cmpop & 0x8 ) ;
rfmt = - 1 ;
if ( ( cmpop & 0x8 ) & & ieee754_cxtest
( IEEE754_INVALID_OPERATION ) )
rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S ;
else
goto copcsr ;
2014-04-26 03:49:14 +04:00
} else
2005-04-17 02:20:36 +04:00
return SIGILL ;
break ;
}
break ;
}
2014-04-26 03:49:14 +04:00
case d_fmt : {
union ieee754dp fs , ft ;
2005-04-17 02:20:36 +04:00
union {
2014-04-16 03:31:11 +04:00
union ieee754dp ( * b ) ( union ieee754dp , union ieee754dp ) ;
union ieee754dp ( * u ) ( union ieee754dp ) ;
2005-04-17 02:20:36 +04:00
} handler ;
switch ( MIPSInst_FUNC ( ir ) ) {
/* binary ops */
case fadd_op :
handler . b = ieee754dp_add ;
goto dcopbop ;
case fsub_op :
handler . b = ieee754dp_sub ;
goto dcopbop ;
case fmul_op :
handler . b = ieee754dp_mul ;
goto dcopbop ;
case fdiv_op :
handler . b = ieee754dp_div ;
goto dcopbop ;
/* unary ops */
case fsqrt_op :
2014-04-19 15:11:37 +04:00
if ( ! cpu_has_mips_2_3_4_5_r )
return SIGILL ;
2005-04-17 02:20:36 +04:00
handler . u = ieee754dp_sqrt ;
goto dcopuop ;
2014-04-19 15:11:37 +04:00
/*
* Note that on some MIPS IV implementations such as the
* R5000 and R8000 the FSQRT and FRECIP instructions do not
* achieve full IEEE - 754 accuracy - however this emulator does .
*/
2005-04-17 02:20:36 +04:00
case frsqrt_op :
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
if ( ! cpu_has_mips_4_5_64_r2_r6 )
2014-04-19 15:11:37 +04:00
return SIGILL ;
2005-04-17 02:20:36 +04:00
handler . u = fpemu_dp_rsqrt ;
goto dcopuop ;
case frecip_op :
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
if ( ! cpu_has_mips_4_5_64_r2_r6 )
2014-04-19 15:11:37 +04:00
return SIGILL ;
2005-04-17 02:20:36 +04:00
handler . u = fpemu_dp_recip ;
goto dcopuop ;
case fmovc_op :
2014-04-19 15:11:37 +04:00
if ( ! cpu_has_mips_4_5_r )
return SIGILL ;
2005-04-17 02:20:36 +04:00
cond = fpucondbit [ MIPSInst_FT ( ir ) > > 2 ] ;
if ( ( ( ctx - > fcr31 & cond ) ! = 0 ) ! =
( ( MIPSInst_FT ( ir ) & 1 ) ! = 0 ) )
return 0 ;
DPFROMREG ( rv . d , MIPSInst_FS ( ir ) ) ;
break ;
case fmovz_op :
2014-04-19 15:11:37 +04:00
if ( ! cpu_has_mips_4_5_r )
return SIGILL ;
2005-04-17 02:20:36 +04:00
if ( xcp - > regs [ MIPSInst_FT ( ir ) ] ! = 0 )
return 0 ;
DPFROMREG ( rv . d , MIPSInst_FS ( ir ) ) ;
break ;
case fmovn_op :
2014-04-19 15:11:37 +04:00
if ( ! cpu_has_mips_4_5_r )
return SIGILL ;
2005-04-17 02:20:36 +04:00
if ( xcp - > regs [ MIPSInst_FT ( ir ) ] = = 0 )
return 0 ;
DPFROMREG ( rv . d , MIPSInst_FS ( ir ) ) ;
break ;
case fabs_op :
handler . u = ieee754dp_abs ;
goto dcopuop ;
case fneg_op :
handler . u = ieee754dp_neg ;
goto dcopuop ;
case fmov_op :
/* an easy one */
DPFROMREG ( rv . d , MIPSInst_FS ( ir ) ) ;
goto copcsr ;
/* binary op on handler */
2014-04-26 03:49:14 +04:00
dcopbop :
DPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
DPFROMREG ( ft , MIPSInst_FT ( ir ) ) ;
2005-04-17 02:20:36 +04:00
2014-04-26 03:49:14 +04:00
rv . d = ( * handler . b ) ( fs , ft ) ;
goto copcsr ;
dcopuop :
DPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
rv . d = ( * handler . u ) ( fs ) ;
goto copcsr ;
2005-04-17 02:20:36 +04:00
2014-04-26 03:49:14 +04:00
/*
* unary conv ops
*/
case fcvts_op :
2005-04-17 02:20:36 +04:00
DPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
rv . s = ieee754sp_fdp ( fs ) ;
rfmt = s_fmt ;
goto copcsr ;
2014-04-26 03:49:14 +04:00
2005-04-17 02:20:36 +04:00
case fcvtd_op :
return SIGILL ; /* not defined */
2014-04-26 03:49:14 +04:00
case fcvtw_op :
2005-04-17 02:20:36 +04:00
DPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
rv . w = ieee754dp_tint ( fs ) ; /* wrong */
rfmt = w_fmt ;
goto copcsr ;
case fround_op :
case ftrunc_op :
case fceil_op :
2014-04-26 03:49:14 +04:00
case ffloor_op :
2014-04-19 15:11:37 +04:00
if ( ! cpu_has_mips_2_3_4_5_r )
return SIGILL ;
2014-04-26 03:49:14 +04:00
oldrm = ieee754_csr . rm ;
2005-04-17 02:20:36 +04:00
DPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
2015-04-04 01:24:56 +03:00
ieee754_csr . rm = MIPSInst_FUNC ( ir ) ;
2005-04-17 02:20:36 +04:00
rv . w = ieee754dp_tint ( fs ) ;
ieee754_csr . rm = oldrm ;
rfmt = w_fmt ;
goto copcsr ;
2014-04-26 03:49:14 +04:00
case fcvtl_op :
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
if ( ! cpu_has_mips_3_4_5_64_r2_r6 )
2014-04-19 15:11:37 +04:00
return SIGILL ;
2005-04-17 02:20:36 +04:00
DPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
rv . l = ieee754dp_tlong ( fs ) ;
rfmt = l_fmt ;
goto copcsr ;
case froundl_op :
case ftruncl_op :
case fceill_op :
2014-04-26 03:49:14 +04:00
case ffloorl_op :
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
if ( ! cpu_has_mips_3_4_5_64_r2_r6 )
2014-04-19 15:11:37 +04:00
return SIGILL ;
2014-04-26 03:49:14 +04:00
oldrm = ieee754_csr . rm ;
2005-04-17 02:20:36 +04:00
DPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
2015-04-04 01:24:56 +03:00
ieee754_csr . rm = MIPSInst_FUNC ( ir ) ;
2005-04-17 02:20:36 +04:00
rv . l = ieee754dp_tlong ( fs ) ;
ieee754_csr . rm = oldrm ;
rfmt = l_fmt ;
goto copcsr ;
default :
if ( MIPSInst_FUNC ( ir ) > = fcmp_op ) {
unsigned cmpop = MIPSInst_FUNC ( ir ) - fcmp_op ;
2014-04-16 03:31:11 +04:00
union ieee754dp fs , ft ;
2005-04-17 02:20:36 +04:00
DPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
DPFROMREG ( ft , MIPSInst_FT ( ir ) ) ;
rv . w = ieee754dp_cmp ( fs , ft ,
cmptab [ cmpop & 0x7 ] , cmpop & 0x8 ) ;
rfmt = - 1 ;
if ( ( cmpop & 0x8 )
& &
ieee754_cxtest
( IEEE754_INVALID_OPERATION ) )
rcsr = FPU_CSR_INV_X | FPU_CSR_INV_S ;
else
goto copcsr ;
}
else {
return SIGILL ;
}
break ;
}
break ;
2014-04-26 03:49:14 +04:00
case w_fmt :
2005-04-17 02:20:36 +04:00
switch ( MIPSInst_FUNC ( ir ) ) {
case fcvts_op :
/* convert word to single precision real */
SPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
rv . s = ieee754sp_fint ( fs . bits ) ;
rfmt = s_fmt ;
goto copcsr ;
case fcvtd_op :
/* convert word to double precision real */
SPFROMREG ( fs , MIPSInst_FS ( ir ) ) ;
rv . d = ieee754dp_fint ( fs . bits ) ;
rfmt = d_fmt ;
goto copcsr ;
default :
return SIGILL ;
}
break ;
}
2014-04-26 03:49:14 +04:00
case l_fmt :
2014-04-19 15:11:37 +04:00
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
if ( ! cpu_has_mips_3_4_5_64_r2_r6 )
2014-04-19 15:11:37 +04:00
return SIGILL ;
2014-02-13 15:26:41 +04:00
DIFROMREG ( bits , MIPSInst_FS ( ir ) ) ;
2005-04-17 02:20:36 +04:00
switch ( MIPSInst_FUNC ( ir ) ) {
case fcvts_op :
/* convert long to single precision real */
2014-02-13 15:26:41 +04:00
rv . s = ieee754sp_flong ( bits ) ;
2005-04-17 02:20:36 +04:00
rfmt = s_fmt ;
goto copcsr ;
case fcvtd_op :
/* convert long to double precision real */
2014-02-13 15:26:41 +04:00
rv . d = ieee754dp_flong ( bits ) ;
2005-04-17 02:20:36 +04:00
rfmt = d_fmt ;
goto copcsr ;
default :
return SIGILL ;
}
break ;
default :
return SIGILL ;
}
/*
* Update the fpu CSR register for this operation .
* If an exception is required , generate a tidy SIGFPE exception ,
* without updating the result register .
* Note : cause exception bits do not accumulate , they are rewritten
* for each op ; only the flag / sticky bits accumulate .
*/
ctx - > fcr31 = ( ctx - > fcr31 & ~ FPU_CSR_ALL_X ) | rcsr ;
if ( ( ctx - > fcr31 > > 5 ) & ctx - > fcr31 & FPU_CSR_ALL_E ) {
2014-04-26 03:49:14 +04:00
/*printk ("SIGFPE: FPU csr = %08x\n",ctx->fcr31); */
2005-04-17 02:20:36 +04:00
return SIGFPE ;
}
/*
* Now we can safely write the result back to the register file .
*/
switch ( rfmt ) {
2014-04-19 15:11:37 +04:00
case - 1 :
if ( cpu_has_mips_4_5_r )
2014-07-23 13:03:58 +04:00
cbit = fpucondbit [ MIPSInst_FD ( ir ) > > 2 ] ;
2014-04-19 15:11:37 +04:00
else
cbit = FPU_CSR_COND ;
2005-04-17 02:20:36 +04:00
if ( rv . w )
2014-04-19 15:11:37 +04:00
ctx - > fcr31 | = cbit ;
2005-04-17 02:20:36 +04:00
else
2014-04-19 15:11:37 +04:00
ctx - > fcr31 & = ~ cbit ;
2005-04-17 02:20:36 +04:00
break ;
2014-04-19 15:11:37 +04:00
2005-04-17 02:20:36 +04:00
case d_fmt :
DPTOREG ( rv . d , MIPSInst_FD ( ir ) ) ;
break ;
case s_fmt :
SPTOREG ( rv . s , MIPSInst_FD ( ir ) ) ;
break ;
case w_fmt :
SITOREG ( rv . w , MIPSInst_FD ( ir ) ) ;
break ;
case l_fmt :
MIPS: Correct FP ISA requirements
Correct ISA requirements for floating-point instructions:
* the CU3 exception signifies a real COP3 instruction in MIPS I & II,
* the BC1FL and BC1TL instructions are not supported in MIPS I,
* the SQRT.fmt instructions are indeed supported in MIPS II,
* the LDC1 and SDC1 instructions are indeed supported in MIPS32r1,
* the CEIL.W.fmt, FLOOR.W.fmt, ROUND.W.fmt and TRUNC.W.fmt instructions
are indeed supported in MIPS32,
* the CVT.L.fmt and CVT.fmt.L instructions are indeed supported in
MIPS32r2 and MIPS32r6,
* the CEIL.L.fmt, FLOOR.L.fmt, ROUND.L.fmt and TRUNC.L.fmt instructions
are indeed supported in MIPS32r2 and MIPS32r6,
* the RSQRT.fmt and RECIP.fmt instructions are indeed supported in
MIPS64r1,
Also simplify conditionals for MIPS III and MIPS IV FPU instructions and
the handling of the MOVCI minor opcode.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9700/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2015-04-04 01:26:49 +03:00
if ( ! cpu_has_mips_3_4_5_64_r2_r6 )
2014-04-19 15:11:37 +04:00
return SIGILL ;
2005-04-17 02:20:36 +04:00
DITOREG ( rv . l , MIPSInst_FD ( ir ) ) ;
break ;
default :
return SIGILL ;
}
return 0 ;
}
2006-10-08 19:10:01 +04:00
int fpu_emulator_cop1Handler ( struct pt_regs * xcp , struct mips_fpu_struct * ctx ,
2010-10-22 03:32:26 +04:00
int has_fpu , void * __user * fault_addr )
2005-04-17 02:20:36 +04:00
{
2005-02-28 20:55:57 +03:00
unsigned long oldepc , prevepc ;
2013-03-25 21:09:02 +04:00
struct mm_decoded_insn dec_insn ;
u16 instr [ 4 ] ;
u16 * instr_ptr ;
2005-04-17 02:20:36 +04:00
int sig = 0 ;
oldepc = xcp - > cp0_epc ;
do {
prevepc = xcp - > cp0_epc ;
2013-03-25 21:09:02 +04:00
if ( get_isa16_mode ( prevepc ) & & cpu_has_mmips ) {
/*
* Get next 2 microMIPS instructions and convert them
* into 32 - bit instructions .
*/
if ( ( get_user ( instr [ 0 ] , ( u16 __user * ) msk_isa16_mode ( xcp - > cp0_epc ) ) ) | |
( get_user ( instr [ 1 ] , ( u16 __user * ) msk_isa16_mode ( xcp - > cp0_epc + 2 ) ) ) | |
( get_user ( instr [ 2 ] , ( u16 __user * ) msk_isa16_mode ( xcp - > cp0_epc + 4 ) ) ) | |
( get_user ( instr [ 3 ] , ( u16 __user * ) msk_isa16_mode ( xcp - > cp0_epc + 6 ) ) ) ) {
MIPS_FPU_EMU_INC_STATS ( errors ) ;
return SIGBUS ;
}
instr_ptr = instr ;
/* Get first instruction. */
if ( mm_insn_16bit ( * instr_ptr ) ) {
/* Duplicate the half-word. */
dec_insn . insn = ( * instr_ptr < < 16 ) |
( * instr_ptr ) ;
/* 16-bit instruction. */
dec_insn . pc_inc = 2 ;
instr_ptr + = 1 ;
} else {
dec_insn . insn = ( * instr_ptr < < 16 ) |
* ( instr_ptr + 1 ) ;
/* 32-bit instruction. */
dec_insn . pc_inc = 4 ;
instr_ptr + = 2 ;
}
/* Get second instruction. */
if ( mm_insn_16bit ( * instr_ptr ) ) {
/* Duplicate the half-word. */
dec_insn . next_insn = ( * instr_ptr < < 16 ) |
( * instr_ptr ) ;
/* 16-bit instruction. */
dec_insn . next_pc_inc = 2 ;
} else {
dec_insn . next_insn = ( * instr_ptr < < 16 ) |
* ( instr_ptr + 1 ) ;
/* 32-bit instruction. */
dec_insn . next_pc_inc = 4 ;
}
dec_insn . micro_mips_mode = 1 ;
} else {
if ( ( get_user ( dec_insn . insn ,
( mips_instruction __user * ) xcp - > cp0_epc ) ) | |
( get_user ( dec_insn . next_insn ,
( mips_instruction __user * ) ( xcp - > cp0_epc + 4 ) ) ) ) {
MIPS_FPU_EMU_INC_STATS ( errors ) ;
return SIGBUS ;
}
dec_insn . pc_inc = 4 ;
dec_insn . next_pc_inc = 4 ;
dec_insn . micro_mips_mode = 0 ;
2010-10-22 03:32:26 +04:00
}
2013-03-25 21:09:02 +04:00
if ( ( dec_insn . insn = = 0 ) | |
( ( dec_insn . pc_inc = = 2 ) & &
( ( dec_insn . insn & 0xffff ) = = MM_NOP16 ) ) )
xcp - > cp0_epc + = dec_insn . pc_inc ; /* Skip NOPs */
2005-04-17 02:20:36 +04:00
else {
2005-04-28 17:39:10 +04:00
/*
2015-04-04 01:24:56 +03:00
* The ' ieee754_csr ' is an alias of ctx - > fcr31 .
* No need to copy ctx - > fcr31 to ieee754_csr .
2005-04-28 17:39:10 +04:00
*/
2013-03-25 21:09:02 +04:00
sig = cop1Emulate ( xcp , ctx , dec_insn , fault_addr ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-08 19:10:01 +04:00
if ( has_fpu )
2005-04-17 02:20:36 +04:00
break ;
if ( sig )
break ;
cond_resched ( ) ;
} while ( xcp - > cp0_epc > prevepc ) ;
/* SIGILL indicates a non-fpu instruction */
if ( sig = = SIGILL & & xcp - > cp0_epc ! = oldepc )
2014-04-26 03:49:14 +04:00
/* but if EPC has advanced, then ignore it */
2005-04-17 02:20:36 +04:00
sig = 0 ;
return sig ;
}