2005-04-17 02:20:36 +04: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>
# include <asm/branch.h>
# include <asm/cpu.h>
# include <asm/cpu-features.h>
2005-05-09 17:16:07 +04:00
# include <asm/fpu.h>
2005-04-17 02:20:36 +04:00
# include <asm/inst.h>
# include <asm/ptrace.h>
# include <asm/uaccess.h>
/*
* Compute the return address and do emulate branch simulation , if required .
*/
int __compute_return_epc ( struct pt_regs * regs )
{
2005-05-31 15:49:19 +04:00
unsigned int * addr , bit , fcr31 , dspcontrol ;
2005-04-17 02:20:36 +04:00
long epc ;
union mips_instruction insn ;
epc = regs - > cp0_epc ;
if ( epc & 3 )
goto unaligned ;
/*
* Read the instruction
*/
addr = ( unsigned int * ) epc ;
if ( __get_user ( insn . word , addr ) ) {
force_sig ( SIGSEGV , current ) ;
return - EFAULT ;
}
regs - > regs [ 0 ] = 0 ;
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 ) {
case bltz_op :
case bltzl_op :
if ( ( long ) regs - > regs [ insn . i_format . rs ] < 0 )
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
else
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
case bgez_op :
case bgezl_op :
if ( ( long ) regs - > regs [ insn . i_format . rs ] > = 0 )
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
else
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
case bltzal_op :
case bltzall_op :
regs - > regs [ 31 ] = epc + 8 ;
if ( ( long ) regs - > regs [ insn . i_format . rs ] < 0 )
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
else
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
case bgezal_op :
case bgezall_op :
regs - > regs [ 31 ] = epc + 8 ;
if ( ( long ) regs - > regs [ insn . i_format . rs ] > = 0 )
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
else
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
2005-05-31 15:49:19 +04: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-17 02:20:36 +04: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 ;
break ;
/*
* These are conditional and in i_format .
*/
case beq_op :
case beql_op :
if ( regs - > regs [ insn . i_format . rs ] = =
regs - > regs [ insn . i_format . rt ] )
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
else
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
case bne_op :
case bnel_op :
if ( regs - > regs [ insn . i_format . rs ] ! =
regs - > regs [ insn . i_format . rt ] )
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
else
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
case blez_op : /* not really i_format */
case blezl_op :
/* rt field assumed to be zero */
if ( ( long ) regs - > regs [ insn . i_format . rs ] < = 0 )
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
else
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
case bgtz_op :
case bgtzl_op :
/* rt field assumed to be zero */
if ( ( long ) regs - > regs [ insn . i_format . rs ] > 0 )
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
else
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
/*
* And now the FPA / cp1 branch instructions .
*/
case cop1_op :
2005-05-09 17:16:07 +04:00
preempt_disable ( ) ;
if ( is_fpu_owner ( ) )
2005-04-17 02:20:36 +04:00
asm volatile ( " cfc1 \t %0,$31 " : " =r " ( fcr31 ) ) ;
2005-05-09 17:16:07 +04:00
else
fcr31 = current - > thread . fpu . hard . fcr31 ;
preempt_enable ( ) ;
2005-04-17 02:20:36 +04:00
bit = ( insn . i_format . rt > > 2 ) ;
bit + = ( bit ! = 0 ) ;
bit + = 23 ;
2006-04-27 00:33:03 +04:00
switch ( insn . i_format . rt & 3 ) {
2005-04-17 02:20:36 +04:00
case 0 : /* bc1f */
case 2 : /* bc1fl */
if ( ~ fcr31 & ( 1 < < bit ) )
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
else
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
case 1 : /* bc1t */
case 3 : /* bc1tl */
if ( fcr31 & ( 1 < < bit ) )
epc = epc + 4 + ( insn . i_format . simmediate < < 2 ) ;
else
epc + = 8 ;
regs - > cp0_epc = epc ;
break ;
}
break ;
}
return 0 ;
unaligned :
printk ( " %s: unaligned epc - sending SIGBUS. \n " , current - > comm ) ;
force_sig ( SIGBUS , current ) ;
return - EFAULT ;
2005-05-31 15:49:19 +04:00
sigill :
printk ( " %s: DSP branch but not DSP ASE - sending SIGBUS. \n " , current - > comm ) ;
force_sig ( SIGBUS , current ) ;
return - EFAULT ;
2005-04-17 02:20:36 +04:00
}