2010-12-28 13:26:23 -08: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 ) 2010 Cavium Networks , Inc .
*/
# include <linux/jump_label.h>
# include <linux/kernel.h>
# include <linux/memory.h>
# include <linux/mutex.h>
# include <linux/types.h>
# include <linux/cpu.h>
# include <asm/cacheflush.h>
# include <asm/inst.h>
2014-11-17 16:10:32 +00:00
/*
* Define parameters for the standard MIPS and the microMIPS jump
* instruction encoding respectively :
*
* - the ISA bit of the target , either 0 or 1 respectively ,
*
* - the amount the jump target address is shifted right to fit in the
* immediate field of the machine instruction , either 2 or 1 ,
*
* - the mask determining the size of the jump region relative to the
* delay - slot instruction , either 256 MB or 128 MB ,
*
* - the jump target alignment , either 4 or 2 bytes .
*/
# define J_ISA_BIT IS_ENABLED(CONFIG_CPU_MICROMIPS)
# define J_RANGE_SHIFT (2 - J_ISA_BIT)
# define J_RANGE_MASK ((1ul << (26 + J_RANGE_SHIFT)) - 1)
# define J_ALIGN_MASK ((1ul << J_RANGE_SHIFT) - 1)
2010-12-28 13:26:23 -08:00
void arch_jump_label_transform ( struct jump_entry * e ,
enum jump_label_type type )
{
2014-11-17 16:10:32 +00:00
union mips_instruction * insn_p ;
2010-12-28 13:26:23 -08:00
union mips_instruction insn ;
2019-04-05 22:50:36 +00:00
long offset ;
2010-12-28 13:26:23 -08:00
2014-11-17 16:10:32 +00:00
insn_p = ( union mips_instruction * ) msk_isa16_mode ( e - > code ) ;
/* Target must have the right alignment and ISA must be preserved. */
BUG_ON ( ( e - > target & J_ALIGN_MASK ) ! = J_ISA_BIT ) ;
2010-12-28 13:26:23 -08:00
2015-07-24 14:45:44 +02:00
if ( type = = JUMP_LABEL_JMP ) {
2019-04-05 22:50:36 +00:00
if ( ! IS_ENABLED ( CONFIG_CPU_MICROMIPS ) & & MIPS_ISA_REV > = 6 ) {
offset = e - > target - ( ( unsigned long ) insn_p + 4 ) ;
offset > > = 2 ;
/*
* The branch offset must fit in the instruction ' s 26
* bit field .
*/
WARN_ON ( ( offset > = BIT ( 25 ) ) | |
( offset < - ( long ) BIT ( 25 ) ) ) ;
insn . j_format . opcode = bc6_op ;
insn . j_format . target = offset ;
} else {
/*
* Jump only works within an aligned region its delay
* slot is in .
*/
WARN_ON ( ( e - > target & ~ J_RANGE_MASK ) ! =
( ( e - > code + 4 ) & ~ J_RANGE_MASK ) ) ;
insn . j_format . opcode = J_ISA_BIT ? mm_j32_op : j_op ;
insn . j_format . target = e - > target > > J_RANGE_SHIFT ;
}
2010-12-28 13:26:23 -08:00
} else {
insn . word = 0 ; /* nop */
}
mutex_lock ( & text_mutex ) ;
2014-11-17 16:10:32 +00:00
if ( IS_ENABLED ( CONFIG_CPU_MICROMIPS ) ) {
insn_p - > halfword [ 0 ] = insn . word > > 16 ;
insn_p - > halfword [ 1 ] = insn . word ;
} else
* insn_p = insn ;
2010-12-28 13:26:23 -08:00
flush_icache_range ( ( unsigned long ) insn_p ,
( unsigned long ) insn_p + sizeof ( * insn_p ) ) ;
mutex_unlock ( & text_mutex ) ;
}