2005-04-16 15:20:36 -07:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* Copyright ( C ) 1996 , 97 , 2000 , 2001 by Ralf Baechle
* Copyright ( C ) 2001 MIPS Technologies , Inc .
*/
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/signal.h>
2011-11-08 17:07:11 +05:30
# include <linux/module.h>
2005-04-16 15:20:36 -07:00
# include <asm/branch.h>
# include <asm/cpu.h>
# include <asm/cpu-features.h>
2005-05-09 13:16:07 +00:00
# include <asm/fpu.h>
2013-03-25 13:08:40 -05:00
# include <asm/fpu_emulator.h>
2005-04-16 15:20:36 -07:00
# include <asm/inst.h>
# include <asm/ptrace.h>
# include <asm/uaccess.h>
2013-03-25 13:08:40 -05:00
/*
2013-03-25 13:45:19 -05:00
* Calculate and return exception PC in case of branch delay slot
* for microMIPS and MIPS16e . It does not clear the ISA mode bit .
2013-03-25 13:08:40 -05:00
*/
int __isa_exception_epc ( struct pt_regs * regs )
{
unsigned short inst ;
2013-03-25 13:45:19 -05:00
long epc = regs - > cp0_epc ;
2013-03-25 13:08:40 -05:00
/* Calculate exception PC in branch delay slot. */
if ( __get_user ( inst , ( u16 __user * ) msk_isa16_mode ( epc ) ) ) {
/* This should never happen because delay slot was checked. */
force_sig ( SIGSEGV , current ) ;
return epc ;
}
2013-03-25 13:45:19 -05:00
if ( cpu_has_mips16 ) {
if ( ( ( union mips16e_instruction ) inst ) . ri . opcode
= = MIPS16e_jal_op )
epc + = 4 ;
else
epc + = 2 ;
} else if ( mm_insn_16bit ( inst ) )
2013-03-25 13:08:40 -05:00
epc + = 2 ;
else
epc + = 4 ;
return epc ;
}
/*
* Compute return address and emulate branch in microMIPS mode after an
* exception only . It does not handle compact branches / jumps and cannot
* be used in interrupt context . ( Compact branches / jumps do not cause
* exceptions . )
*/
int __microMIPS_compute_return_epc ( struct pt_regs * regs )
{
u16 __user * pc16 ;
u16 halfword ;
unsigned int word ;
unsigned long contpc ;
struct mm_decoded_insn mminsn = { 0 } ;
mminsn . micro_mips_mode = 1 ;
/* This load never faults. */
pc16 = ( unsigned short __user * ) msk_isa16_mode ( regs - > cp0_epc ) ;
__get_user ( halfword , pc16 ) ;
pc16 + + ;
contpc = regs - > cp0_epc + 2 ;
word = ( ( unsigned int ) halfword < < 16 ) ;
mminsn . pc_inc = 2 ;
if ( ! mm_insn_16bit ( halfword ) ) {
__get_user ( halfword , pc16 ) ;
pc16 + + ;
contpc = regs - > cp0_epc + 4 ;
mminsn . pc_inc = 4 ;
word | = halfword ;
}
mminsn . insn = word ;
if ( get_user ( halfword , pc16 ) )
goto sigsegv ;
mminsn . next_pc_inc = 2 ;
word = ( ( unsigned int ) halfword < < 16 ) ;
if ( ! mm_insn_16bit ( halfword ) ) {
pc16 + + ;
if ( get_user ( halfword , pc16 ) )
goto sigsegv ;
mminsn . next_pc_inc = 4 ;
word | = halfword ;
}
mminsn . next_insn = word ;
mm_isBranchInstr ( regs , mminsn , & contpc ) ;
regs - > cp0_epc = contpc ;
return 0 ;
sigsegv :
force_sig ( SIGSEGV , current ) ;
return - EFAULT ;
}
2013-03-25 13:45:19 -05:00
/*
* Compute return address and emulate branch in MIPS16e mode after an
* exception only . It does not handle compact branches / jumps and cannot
* be used in interrupt context . ( Compact branches / jumps do not cause
* exceptions . )
*/
int __MIPS16e_compute_return_epc ( struct pt_regs * regs )
{
u16 __user * addr ;
union mips16e_instruction inst ;
u16 inst2 ;
u32 fullinst ;
long epc ;
epc = regs - > cp0_epc ;
/* Read the instruction. */
addr = ( u16 __user * ) msk_isa16_mode ( epc ) ;
if ( __get_user ( inst . full , addr ) ) {
force_sig ( SIGSEGV , current ) ;
return - EFAULT ;
}
switch ( inst . ri . opcode ) {
case MIPS16e_extend_op :
regs - > cp0_epc + = 4 ;
return 0 ;
/*
* JAL and JALX in MIPS16e mode
*/
case MIPS16e_jal_op :
addr + = 1 ;
if ( __get_user ( inst2 , addr ) ) {
force_sig ( SIGSEGV , current ) ;
return - EFAULT ;
}
fullinst = ( ( unsigned ) inst . full < < 16 ) | inst2 ;
regs - > regs [ 31 ] = epc + 6 ;
epc + = 4 ;
epc > > = 28 ;
epc < < = 28 ;
/*
* JAL : 5 X : 1 TARGET [ 20 - 16 ] : 5 TARGET [ 25 : 21 ] : 5 TARGET [ 15 : 0 ] : 16
*
* . . . . . . TARGET [ 15 : 0 ] . . . . . . . . . . . . . . . . . TARGET [ 20 : 16 ] . . . . . . . . . . .
* . . . . . . TARGET [ 25 : 21 ]
*/
epc | =
( ( fullinst & 0xffff ) < < 2 ) | ( ( fullinst & 0x3e00000 ) > > 3 ) |
( ( fullinst & 0x1f0000 ) < < 7 ) ;
if ( ! inst . jal . x )
set_isa16_mode ( epc ) ; /* Set ISA mode bit. */
regs - > cp0_epc = epc ;
return 0 ;
/*
* J ( AL ) R ( C )
*/
case MIPS16e_rr_op :
if ( inst . rr . func = = MIPS16e_jr_func ) {
if ( inst . rr . ra )
regs - > cp0_epc = regs - > regs [ 31 ] ;
else
regs - > cp0_epc =
regs - > regs [ reg16to32 [ inst . rr . rx ] ] ;
if ( inst . rr . l ) {
if ( inst . rr . nd )
regs - > regs [ 31 ] = epc + 2 ;
else
regs - > regs [ 31 ] = epc + 4 ;
}
return 0 ;
}
break ;
}
/*
* All other cases have no branch delay slot and are 16 - bits .
* Branches do not cause an exception .
*/
regs - > cp0_epc + = 2 ;
return 0 ;
}
2011-11-08 17:07:11 +05:30
/**
* __compute_return_epc_for_insn - Computes the return address and do emulate
* branch simulation , if required .
*
* @ regs : Pointer to pt_regs
* @ insn : branch instruction to decode
* @ returns : - EFAULT on error and forces SIGBUS , and on success
* returns 0 or BRANCH_LIKELY_TAKEN as appropriate after
* evaluating the branch .
2005-04-16 15:20:36 -07:00
*/
2011-11-08 17:07:11 +05:30
int __compute_return_epc_for_insn ( struct pt_regs * regs ,
union mips_instruction insn )
2005-04-16 15:20:36 -07:00
{
2007-07-13 23:02:42 +09:00
unsigned int bit , fcr31 , dspcontrol ;
2011-11-08 17:07:11 +05:30
long epc = regs - > cp0_epc ;
int ret = 0 ;
2005-04-16 15:20:36 -07:00
switch ( insn . i_format . opcode ) {
/*
* jr and jalr are in r_format format .
*/
case spec_op :
switch ( insn . r_format . func ) {
case jalr_op :
regs - > regs [ insn . r_format . rd ] = epc + 8 ;
/* Fall through */
case jr_op :
regs - > cp0_epc = regs - > regs [ insn . r_format . rs ] ;
break ;
}
break ;
/*
* This group contains :
* bltz_op , bgez_op , bltzl_op , bgezl_op ,
* bltzal_op , bgezal_op , bltzall_op , bgezall_op .
*/
case bcond_op :
switch ( insn . i_format . rt ) {
2013-01-22 12:59:30 +01:00
case bltz_op :
2005-04-16 15:20:36 -07:00
case bltzl_op :
2011-11-08 17:07:11 +05:30
if ( ( long ) regs - > regs [ insn . i_format . rs ] < 0 ) {
2005-04-16 15:20:36 -07:00
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
2011-11-08 17:07:11 +05:30
if ( insn . i_format . rt = = bltzl_op )
ret = BRANCH_LIKELY_TAKEN ;
} else
2005-04-16 15:20:36 -07:00
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
case bgez_op :
case bgezl_op :
2011-11-08 17:07:11 +05:30
if ( ( long ) regs - > regs [ insn . i_format . rs ] > = 0 ) {
2005-04-16 15:20:36 -07:00
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
2011-11-08 17:07:11 +05:30
if ( insn . i_format . rt = = bgezl_op )
ret = BRANCH_LIKELY_TAKEN ;
} else
2005-04-16 15:20:36 -07:00
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
case bltzal_op :
case bltzall_op :
regs - > regs [ 31 ] = epc + 8 ;
2011-11-08 17:07:11 +05:30
if ( ( long ) regs - > regs [ insn . i_format . rs ] < 0 ) {
2005-04-16 15:20:36 -07:00
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
2011-11-08 17:07:11 +05:30
if ( insn . i_format . rt = = bltzall_op )
ret = BRANCH_LIKELY_TAKEN ;
} else
2005-04-16 15:20:36 -07:00
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
case bgezal_op :
case bgezall_op :
regs - > regs [ 31 ] = epc + 8 ;
2011-11-08 17:07:11 +05:30
if ( ( long ) regs - > regs [ insn . i_format . rs ] > = 0 ) {
2005-04-16 15:20:36 -07:00
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
2011-11-08 17:07:11 +05:30
if ( insn . i_format . rt = = bgezall_op )
ret = BRANCH_LIKELY_TAKEN ;
} else
2005-04-16 15:20:36 -07:00
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
2011-11-08 17:07:11 +05:30
2005-05-31 11:49:19 +00:00
case bposge32_op :
if ( ! cpu_has_dsp )
goto sigill ;
dspcontrol = rddsp ( 0x01 ) ;
if ( dspcontrol > = 32 ) {
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
} else
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
2005-04-16 15:20:36 -07:00
}
break ;
/*
* These are unconditional and in j_format .
*/
case jal_op :
regs - > regs [ 31 ] = regs - > cp0_epc + 8 ;
case j_op :
epc + = 4 ;
epc > > = 28 ;
epc < < = 28 ;
epc | = ( insn . j_format . target < < 2 ) ;
regs - > cp0_epc = epc ;
2013-03-25 13:08:40 -05:00
if ( insn . i_format . opcode = = jalx_op )
set_isa16_mode ( regs - > cp0_epc ) ;
2005-04-16 15:20:36 -07:00
break ;
/*
* These are conditional and in i_format .
*/
case beq_op :
case beql_op :
if ( regs - > regs [ insn . i_format . rs ] = =
2011-11-08 17:07:11 +05:30
regs - > regs [ insn . i_format . rt ] ) {
2005-04-16 15:20:36 -07:00
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
2011-11-08 17:07:11 +05:30
if ( insn . i_format . rt = = beql_op )
ret = BRANCH_LIKELY_TAKEN ;
} else
2005-04-16 15:20:36 -07:00
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
case bne_op :
case bnel_op :
if ( regs - > regs [ insn . i_format . rs ] ! =
2011-11-08 17:07:11 +05:30
regs - > regs [ insn . i_format . rt ] ) {
2005-04-16 15:20:36 -07:00
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
2011-11-08 17:07:11 +05:30
if ( insn . i_format . rt = = bnel_op )
ret = BRANCH_LIKELY_TAKEN ;
} else
2005-04-16 15:20:36 -07:00
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
case blez_op : /* not really i_format */
case blezl_op :
/* rt field assumed to be zero */
2011-11-08 17:07:11 +05:30
if ( ( long ) regs - > regs [ insn . i_format . rs ] < = 0 ) {
2005-04-16 15:20:36 -07:00
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
2011-11-08 17:07:11 +05:30
if ( insn . i_format . rt = = bnel_op )
ret = BRANCH_LIKELY_TAKEN ;
} else
2005-04-16 15:20:36 -07:00
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
case bgtz_op :
case bgtzl_op :
/* rt field assumed to be zero */
2011-11-08 17:07:11 +05:30
if ( ( long ) regs - > regs [ insn . i_format . rs ] > 0 ) {
2005-04-16 15:20:36 -07:00
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
2011-11-08 17:07:11 +05:30
if ( insn . i_format . rt = = bnel_op )
ret = BRANCH_LIKELY_TAKEN ;
} else
2005-04-16 15:20:36 -07:00
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
/*
* And now the FPA / cp1 branch instructions .
*/
case cop1_op :
2005-05-09 13:16:07 +00:00
preempt_disable ( ) ;
if ( is_fpu_owner ( ) )
2005-04-16 15:20:36 -07:00
asm volatile ( " cfc1 \t %0,$31 " : " =r " ( fcr31 ) ) ;
2005-05-09 13:16:07 +00:00
else
2006-05-16 01:26:03 +09:00
fcr31 = current - > thread . fpu . fcr31 ;
2005-05-09 13:16:07 +00:00
preempt_enable ( ) ;
2005-04-16 15:20:36 -07:00
bit = ( insn . i_format . rt > > 2 ) ;
bit + = ( bit ! = 0 ) ;
bit + = 23 ;
2006-04-26 21:33:03 +01:00
switch ( insn . i_format . rt & 3 ) {
2013-01-22 12:59:30 +01:00
case 0 : /* bc1f */
case 2 : /* bc1fl */
2011-11-08 17:07:11 +05:30
if ( ~ fcr31 & ( 1 < < bit ) ) {
2005-04-16 15:20:36 -07:00
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
2011-11-08 17:07:11 +05:30
if ( insn . i_format . rt = = 2 )
ret = BRANCH_LIKELY_TAKEN ;
} else
2005-04-16 15:20:36 -07:00
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
2013-01-22 12:59:30 +01:00
case 1 : /* bc1t */
case 3 : /* bc1tl */
2011-11-08 17:07:11 +05:30
if ( fcr31 & ( 1 < < bit ) ) {
2005-04-16 15:20:36 -07:00
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
2011-11-08 17:07:11 +05:30
if ( insn . i_format . rt = = 3 )
ret = BRANCH_LIKELY_TAKEN ;
} else
2005-04-16 15:20:36 -07:00
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
}
break ;
2008-12-11 15:33:34 -08: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 )
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
else
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
case ldc2_op : /* This is bbit032 on Octeon */
if ( ( regs - > regs [ insn . i_format . rs ] &
( 1ull < < ( insn . i_format . rt + 32 ) ) ) = = 0 )
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
else
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
case swc2_op : /* This is bbit1 on Octeon */
if ( regs - > regs [ insn . i_format . rs ] & ( 1ull < < insn . i_format . rt ) )
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
else
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
case sdc2_op : /* This is bbit132 on Octeon */
if ( regs - > regs [ insn . i_format . rs ] &
( 1ull < < ( insn . i_format . rt + 32 ) ) )
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
else
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
# endif
2005-04-16 15:20:36 -07:00
}
2011-11-08 17:07:11 +05:30
return ret ;
2005-04-16 15:20:36 -07:00
2011-11-08 17:07:11 +05:30
sigill :
printk ( " %s: DSP branch but not DSP ASE - sending SIGBUS. \n " , current - > comm ) ;
2005-04-16 15:20:36 -07:00
force_sig ( SIGBUS , current ) ;
return - EFAULT ;
2011-11-08 17:07:11 +05:30
}
EXPORT_SYMBOL_GPL ( __compute_return_epc_for_insn ) ;
2005-05-31 11:49:19 +00:00
2011-11-08 17:07:11 +05:30
int __compute_return_epc ( struct pt_regs * regs )
{
unsigned int __user * addr ;
long epc ;
union mips_instruction insn ;
epc = regs - > cp0_epc ;
if ( epc & 3 )
goto unaligned ;
/*
* Read the instruction
*/
addr = ( unsigned int __user * ) epc ;
if ( __get_user ( insn . word , addr ) ) {
force_sig ( SIGSEGV , current ) ;
return - EFAULT ;
}
return __compute_return_epc_for_insn ( regs , insn ) ;
unaligned :
printk ( " %s: unaligned epc - sending SIGBUS. \n " , current - > comm ) ;
2005-05-31 11:49:19 +00:00
force_sig ( SIGBUS , current ) ;
return - EFAULT ;
2005-04-16 15:20:36 -07:00
}