2008-05-31 12:53:50 +04:00
/*
* Dynamic function tracing support .
*
* Copyright ( C ) 2008 Abhishek Sagar < sagar . abhishek @ gmail . com >
2010-08-10 22:43:28 +04:00
* Copyright ( C ) 2010 Rabin Vincent < rabin @ rab . in >
2008-05-31 12:53:50 +04:00
*
* 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
2010-08-10 22:43:28 +04:00
* mcount call - sites get patched with NOP till they are enabled .
* All code mutation routines here are called under stop_machine ( ) .
2008-05-31 12:53:50 +04:00
*/
# include <linux/ftrace.h>
2010-08-10 22:43:28 +04:00
# include <linux/uaccess.h>
2014-04-03 21:46:45 +04:00
# include <linux/module.h>
2014-04-04 00:29:50 +04:00
# include <linux/stop_machine.h>
2008-06-21 22:17:27 +04:00
2008-05-31 12:53:50 +04:00
# include <asm/cacheflush.h>
2012-02-18 20:47:03 +04:00
# include <asm/opcodes.h>
2008-06-21 22:17:27 +04:00
# include <asm/ftrace.h>
2015-01-09 09:37:36 +03:00
# include <asm/insn.h>
2017-05-09 01:58:02 +03:00
# include <asm/set_memory.h>
2012-02-18 20:50:06 +04:00
2010-08-10 22:52:35 +04:00
# ifdef CONFIG_THUMB2_KERNEL
2012-02-18 20:47:03 +04:00
# define NOP 0xf85deb04 /* pop.w {lr} */
2010-08-10 22:52:35 +04:00
# else
2010-08-10 22:43:28 +04:00
# define NOP 0xe8bd4000 /* pop {lr} */
2010-08-10 22:52:35 +04:00
# endif
2008-05-31 12:53:50 +04:00
2010-10-09 20:54:38 +04:00
# ifdef CONFIG_DYNAMIC_FTRACE
2010-08-10 22:43:28 +04:00
2014-04-04 00:29:50 +04:00
static int __ftrace_modify_code ( void * data )
{
int * command = data ;
set_kernel_text_rw ( ) ;
ftrace_modify_all_code ( * command ) ;
set_kernel_text_ro ( ) ;
return 0 ;
}
void arch_ftrace_update_code ( int command )
{
stop_machine ( __ftrace_modify_code , & command , NULL ) ;
}
2010-08-10 22:43:28 +04:00
static unsigned long ftrace_nop_replace ( struct dyn_ftrace * rec )
{
return NOP ;
}
static unsigned long adjust_address ( struct dyn_ftrace * rec , unsigned long addr )
2008-05-31 12:53:50 +04:00
{
2010-08-10 22:43:28 +04:00
return addr ;
2008-05-31 12:53:50 +04:00
}
2014-04-03 21:46:45 +04:00
int ftrace_arch_code_modify_prepare ( void )
{
set_all_modules_text_rw ( ) ;
return 0 ;
}
int ftrace_arch_code_modify_post_process ( void )
{
set_all_modules_text_ro ( ) ;
2014-04-04 00:29:50 +04:00
/* Make sure any TLB misses during machine stop are cleared. */
flush_tlb_all ( ) ;
2014-04-03 21:46:45 +04:00
return 0 ;
}
2010-11-06 20:33:21 +03:00
static unsigned long ftrace_call_replace ( unsigned long pc , unsigned long addr )
{
2012-02-18 20:50:06 +04:00
return arm_gen_branch_link ( pc , addr ) ;
2010-11-06 20:33:21 +03:00
}
2010-08-10 22:43:28 +04:00
static int ftrace_modify_code ( unsigned long pc , unsigned long old ,
2012-02-29 18:59:07 +04:00
unsigned long new , bool validate )
2010-08-10 22:43:28 +04:00
{
unsigned long replaced ;
2008-05-31 12:53:50 +04:00
2012-02-18 20:47:03 +04:00
if ( IS_ENABLED ( CONFIG_THUMB2_KERNEL ) ) {
old = __opcode_to_mem_thumb32 ( old ) ;
new = __opcode_to_mem_thumb32 ( new ) ;
} else {
old = __opcode_to_mem_arm ( old ) ;
new = __opcode_to_mem_arm ( new ) ;
}
2012-02-29 18:59:07 +04:00
if ( validate ) {
if ( probe_kernel_read ( & replaced , ( void * ) pc , MCOUNT_INSN_SIZE ) )
return - EFAULT ;
2008-05-31 12:53:50 +04:00
2012-02-29 18:59:07 +04:00
if ( replaced ! = old )
return - EINVAL ;
}
2008-05-31 12:53:50 +04:00
2010-08-10 22:43:28 +04:00
if ( probe_kernel_write ( ( void * ) pc , & new , MCOUNT_INSN_SIZE ) )
return - EPERM ;
2008-05-31 12:53:50 +04:00
2010-08-10 22:43:28 +04:00
flush_icache_range ( pc , pc + MCOUNT_INSN_SIZE ) ;
2008-05-31 12:53:50 +04:00
2010-08-10 22:43:28 +04:00
return 0 ;
2008-05-31 12:53:50 +04:00
}
int ftrace_update_ftrace_func ( ftrace_func_t func )
{
2012-02-29 18:59:07 +04:00
unsigned long pc ;
2010-08-10 22:43:28 +04:00
unsigned long new ;
int ret ;
2008-05-31 12:53:50 +04:00
pc = ( unsigned long ) & ftrace_call ;
new = ftrace_call_replace ( pc , ( unsigned long ) func ) ;
2010-08-10 22:43:28 +04:00
2012-02-29 18:59:07 +04:00
ret = ftrace_modify_code ( pc , 0 , new , false ) ;
2010-08-10 22:43:28 +04:00
2017-05-26 23:49:47 +03:00
# ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
if ( ! ret ) {
pc = ( unsigned long ) & ftrace_regs_call ;
new = ftrace_call_replace ( pc , ( unsigned long ) func ) ;
ret = ftrace_modify_code ( pc , 0 , new , false ) ;
}
# endif
2010-08-10 22:43:28 +04:00
return ret ;
}
int ftrace_make_call ( struct dyn_ftrace * rec , unsigned long addr )
{
unsigned long new , old ;
unsigned long ip = rec - > ip ;
old = ftrace_nop_replace ( rec ) ;
2017-05-26 23:49:47 +03:00
new = ftrace_call_replace ( ip , adjust_address ( rec , addr ) ) ;
return ftrace_modify_code ( rec - > ip , old , new , true ) ;
}
# ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
int ftrace_modify_call ( struct dyn_ftrace * rec , unsigned long old_addr ,
unsigned long addr )
{
unsigned long new , old ;
unsigned long ip = rec - > ip ;
old = ftrace_call_replace ( ip , adjust_address ( rec , old_addr ) ) ;
2010-08-10 22:43:28 +04:00
new = ftrace_call_replace ( ip , adjust_address ( rec , addr ) ) ;
2012-02-29 18:59:07 +04:00
return ftrace_modify_code ( rec - > ip , old , new , true ) ;
2010-08-10 22:43:28 +04:00
}
2017-05-26 23:49:47 +03:00
# endif
2010-08-10 22:43:28 +04:00
int ftrace_make_nop ( struct module * mod ,
struct dyn_ftrace * rec , unsigned long addr )
{
unsigned long ip = rec - > ip ;
unsigned long old ;
unsigned long new ;
int ret ;
old = ftrace_call_replace ( ip , adjust_address ( rec , addr ) ) ;
new = ftrace_nop_replace ( rec ) ;
2012-02-29 18:59:07 +04:00
ret = ftrace_modify_code ( ip , old , new , true ) ;
2010-08-10 22:43:28 +04:00
2008-05-31 12:53:50 +04:00
return ret ;
}
2014-02-24 22:59:59 +04:00
int __init ftrace_dyn_arch_init ( void )
2008-05-31 12:53:50 +04:00
{
return 0 ;
}
2010-10-09 20:54:38 +04:00
# endif /* CONFIG_DYNAMIC_FTRACE */
# ifdef CONFIG_FUNCTION_GRAPH_TRACER
void prepare_ftrace_return ( unsigned long * parent , unsigned long self_addr ,
unsigned long frame_pointer )
{
unsigned long return_hooker = ( unsigned long ) & return_to_handler ;
unsigned long old ;
if ( unlikely ( atomic_read ( & current - > tracing_graph_pause ) ) )
return ;
old = * parent ;
* parent = return_hooker ;
2018-11-19 01:19:26 +03:00
if ( function_graph_enter ( old , self_addr , frame_pointer , NULL ) )
2010-10-09 20:54:38 +04:00
* parent = old ;
}
2010-11-06 20:33:21 +03:00
# ifdef CONFIG_DYNAMIC_FTRACE
extern unsigned long ftrace_graph_call ;
extern unsigned long ftrace_graph_call_old ;
extern void ftrace_graph_caller_old ( void ) ;
2017-05-26 23:49:47 +03:00
extern unsigned long ftrace_graph_regs_call ;
extern void ftrace_graph_regs_caller ( void ) ;
2010-11-06 20:33:21 +03:00
static int __ftrace_modify_caller ( unsigned long * callsite ,
void ( * func ) ( void ) , bool enable )
{
unsigned long caller_fn = ( unsigned long ) func ;
unsigned long pc = ( unsigned long ) callsite ;
2012-02-18 20:50:06 +04:00
unsigned long branch = arm_gen_branch ( pc , caller_fn ) ;
2010-11-06 20:33:21 +03:00
unsigned long nop = 0xe1a00000 ; /* mov r0, r0 */
unsigned long old = enable ? nop : branch ;
unsigned long new = enable ? branch : nop ;
2012-02-29 18:59:07 +04:00
return ftrace_modify_code ( pc , old , new , true ) ;
2010-11-06 20:33:21 +03:00
}
static int ftrace_modify_graph_caller ( bool enable )
{
int ret ;
ret = __ftrace_modify_caller ( & ftrace_graph_call ,
ftrace_graph_caller ,
enable ) ;
2017-05-26 23:49:47 +03:00
# ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
if ( ! ret )
ret = __ftrace_modify_caller ( & ftrace_graph_regs_call ,
ftrace_graph_regs_caller ,
enable ) ;
# endif
2010-11-06 20:33:21 +03:00
return ret ;
}
int ftrace_enable_ftrace_graph_caller ( void )
{
return ftrace_modify_graph_caller ( true ) ;
}
int ftrace_disable_ftrace_graph_caller ( void )
{
return ftrace_modify_graph_caller ( false ) ;
}
# endif /* CONFIG_DYNAMIC_FTRACE */
2010-10-09 20:54:38 +04:00
# endif /* CONFIG_FUNCTION_GRAPH_TRACER */