2008-05-31 14:23:50 +05:30
/*
* Dynamic function tracing support .
*
* Copyright ( C ) 2008 Abhishek Sagar < sagar . abhishek @ gmail . com >
*
* For licencing details , see COPYING .
*
* Defines low - level handling of mcount calls when the kernel
* is compiled with the - pg flag . When using dynamic ftrace , the
* mcount call - sites get patched lazily with NOP till they are
* enabled . All code mutation routines here take effect atomically .
*/
# include <linux/ftrace.h>
2008-06-21 23:47:27 +05:30
2008-05-31 14:23:50 +05:30
# include <asm/cacheflush.h>
2008-06-21 23:47:27 +05:30
# include <asm/ftrace.h>
2008-05-31 14:23:50 +05:30
# define PC_OFFSET 8
# define BL_OPCODE 0xeb000000
# define BL_OFFSET_MASK 0x00ffffff
static unsigned long bl_insn ;
static const unsigned long NOP = 0xe1a00000 ; /* mov r0, r0 */
unsigned char * ftrace_nop_replace ( void )
{
return ( char * ) & NOP ;
}
/* construct a branch (BL) instruction to addr */
unsigned char * ftrace_call_replace ( unsigned long pc , unsigned long addr )
{
long offset ;
2008-06-21 23:47:27 +05:30
offset = ( long ) addr - ( long ) ( pc + PC_OFFSET ) ;
2008-05-31 14:23:50 +05:30
if ( unlikely ( offset < - 33554432 | | offset > 33554428 ) ) {
/* Can't generate branches that far (from ARM ARM). Ftrace
2008-06-21 23:47:27 +05:30
* doesn ' t generate branches outside of kernel text .
2008-05-31 14:23:50 +05:30
*/
WARN_ON_ONCE ( 1 ) ;
return NULL ;
}
offset = ( offset > > 2 ) & BL_OFFSET_MASK ;
bl_insn = BL_OPCODE | offset ;
return ( unsigned char * ) & bl_insn ;
}
int ftrace_modify_code ( unsigned long pc , unsigned char * old_code ,
unsigned char * new_code )
{
unsigned long err = 0 , replaced = 0 , old , new ;
old = * ( unsigned long * ) old_code ;
new = * ( unsigned long * ) new_code ;
__asm__ __volatile__ (
" 1: ldr %1, [%2] \n "
" cmp %1, %4 \n "
" 2: streq %3, [%2] \n "
" cmpne %1, %3 \n "
" movne %0, #2 \n "
" 3: \n "
2010-04-19 10:15:03 +01:00
" .pushsection .fixup, \" ax \" \n "
2008-05-31 14:23:50 +05:30
" 4: mov %0, #1 \n "
" b 3b \n "
2010-04-19 10:15:03 +01:00
" .popsection \n "
2008-05-31 14:23:50 +05:30
2010-04-19 10:15:03 +01:00
" .pushsection __ex_table, \" a \" \n "
2008-05-31 14:23:50 +05:30
" .long 1b, 4b \n "
" .long 2b, 4b \n "
2010-04-19 10:15:03 +01:00
" .popsection \n "
2008-05-31 14:23:50 +05:30
: " =r " ( err ) , " =r " ( replaced )
: " r " ( pc ) , " r " ( new ) , " r " ( old ) , " 0 " ( err ) , " 1 " ( replaced )
: " memory " ) ;
if ( ! err & & ( replaced = = old ) )
2008-06-21 23:47:27 +05:30
flush_icache_range ( pc , pc + MCOUNT_INSN_SIZE ) ;
2008-05-31 14:23:50 +05:30
return err ;
}
int ftrace_update_ftrace_func ( ftrace_func_t func )
{
int ret ;
unsigned long pc , old ;
unsigned char * new ;
pc = ( unsigned long ) & ftrace_call ;
2008-06-21 23:47:27 +05:30
memcpy ( & old , & ftrace_call , MCOUNT_INSN_SIZE ) ;
2008-05-31 14:23:50 +05:30
new = ftrace_call_replace ( pc , ( unsigned long ) func ) ;
ret = ftrace_modify_code ( pc , ( unsigned char * ) & old , new ) ;
return ret ;
}
2008-12-12 11:15:45 +01:00
/* run from ftrace_init with irqs disabled */
2008-05-31 14:23:50 +05:30
int __init ftrace_dyn_arch_init ( void * data )
{
ftrace_mcount_set ( data ) ;
return 0 ;
}