2012-09-28 17:15:20 +09:00
/*
* Dynamic Ftrace based Kprobes Optimization
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*
* Copyright ( C ) Hitachi Ltd . , 2012
*/
# include <linux/kprobes.h>
# include <linux/ptrace.h>
# include <linux/hardirq.h>
# include <linux/preempt.h>
# include <linux/ftrace.h>
2012-09-28 17:15:22 +09:00
# include "common.h"
2012-09-28 17:15:20 +09:00
2014-04-17 17:18:14 +09:00
static nokprobe_inline
int __skip_singlestep ( struct kprobe * p , struct pt_regs * regs ,
2014-10-09 13:01:06 +00:00
struct kprobe_ctlblk * kcb , unsigned long orig_ip )
2012-09-28 17:15:20 +09:00
{
/*
* Emulate singlestep ( and also recover regs - > ip )
* as if there is a 5 byte nop
*/
regs - > ip = ( unsigned long ) p - > addr + MCOUNT_INSN_SIZE ;
if ( unlikely ( p - > post_handler ) ) {
kcb - > kprobe_status = KPROBE_HIT_SSDONE ;
p - > post_handler ( p , regs , 0 ) ;
}
__this_cpu_write ( current_kprobe , NULL ) ;
2014-10-09 13:01:06 +00:00
if ( orig_ip )
regs - > ip = orig_ip ;
2012-09-28 17:15:20 +09:00
return 1 ;
}
2014-04-17 17:18:14 +09:00
int skip_singlestep ( struct kprobe * p , struct pt_regs * regs ,
struct kprobe_ctlblk * kcb )
2012-09-28 17:15:20 +09:00
{
if ( kprobe_ftrace ( p ) )
2014-10-09 13:01:06 +00:00
return __skip_singlestep ( p , regs , kcb , 0 ) ;
2012-09-28 17:15:20 +09:00
else
return 0 ;
}
2014-04-17 17:18:14 +09:00
NOKPROBE_SYMBOL ( skip_singlestep ) ;
2012-09-28 17:15:20 +09:00
/* Ftrace callback handler for kprobes */
2014-04-17 17:18:14 +09:00
void kprobe_ftrace_handler ( unsigned long ip , unsigned long parent_ip ,
struct ftrace_ops * ops , struct pt_regs * regs )
2012-09-28 17:15:20 +09:00
{
struct kprobe * p ;
struct kprobe_ctlblk * kcb ;
unsigned long flags ;
/* Disable irq for emulating a breakpoint and avoiding preempt */
local_irq_save ( flags ) ;
p = get_kprobe ( ( kprobe_opcode_t * ) ip ) ;
if ( unlikely ( ! p ) | | kprobe_disabled ( p ) )
goto end ;
kcb = get_kprobe_ctlblk ( ) ;
if ( kprobe_running ( ) ) {
kprobes_inc_nmissed_count ( p ) ;
} else {
2014-10-09 13:01:06 +00:00
unsigned long orig_ip = regs - > ip ;
2012-09-28 17:15:20 +09:00
/* Kprobe handler expects regs->ip = ip + 1 as breakpoint hit */
regs - > ip = ip + sizeof ( kprobe_opcode_t ) ;
__this_cpu_write ( current_kprobe , p ) ;
kcb - > kprobe_status = KPROBE_HIT_ACTIVE ;
if ( ! p - > pre_handler | | ! p - > pre_handler ( p , regs ) )
2014-10-09 13:01:06 +00:00
__skip_singlestep ( p , regs , kcb , orig_ip ) ;
2012-09-28 17:15:20 +09:00
/*
* If pre_handler returns ! 0 , it sets regs - > ip and
* resets current kprobe .
*/
}
end :
local_irq_restore ( flags ) ;
}
2014-04-17 17:18:14 +09:00
NOKPROBE_SYMBOL ( kprobe_ftrace_handler ) ;
2012-09-28 17:15:20 +09:00
2014-04-17 17:17:47 +09:00
int arch_prepare_kprobe_ftrace ( struct kprobe * p )
2012-09-28 17:15:20 +09:00
{
p - > ainsn . insn = NULL ;
p - > ainsn . boostable = - 1 ;
return 0 ;
}