2005-04-17 02:20:36 +04:00
/* arch/sparc64/kernel/kprobes.c
*
* Copyright ( C ) 2004 David S . Miller < davem @ davemloft . net >
*/
# include <linux/config.h>
# include <linux/kernel.h>
# include <linux/kprobes.h>
# include <asm/kdebug.h>
# include <asm/signal.h>
/* We do not have hardware single-stepping on sparc64.
* So we implement software single - stepping with breakpoint
* traps . The top - level scheme is similar to that used
* in the x86 kprobes implementation .
*
* In the kprobe - > ainsn . insn [ ] array we store the original
* instruction at index zero and a break instruction at
* index one .
*
* When we hit a kprobe we :
* - Run the pre - handler
* - Remember " regs->tnpc " and interrupt level stored in
* " regs->tstate " so we can restore them later
* - Disable PIL interrupts
* - Set regs - > tpc to point to kprobe - > ainsn . insn [ 0 ]
* - Set regs - > tnpc to point to kprobe - > ainsn . insn [ 1 ]
* - Mark that we are actively in a kprobe
*
* At this point we wait for the second breakpoint at
* kprobe - > ainsn . insn [ 1 ] to hit . When it does we :
* - Run the post - handler
* - Set regs - > tpc to " remembered " regs - > tnpc stored above ,
* restore the PIL interrupt level in " regs->tstate " as well
* - Make any adjustments necessary to regs - > tnpc in order
* to handle relative branches correctly . See below .
* - Mark that we are no longer actively in a kprobe .
*/
int arch_prepare_kprobe ( struct kprobe * p )
{
return 0 ;
}
void arch_copy_kprobe ( struct kprobe * p )
{
p - > ainsn . insn [ 0 ] = * p - > addr ;
p - > ainsn . insn [ 1 ] = BREAKPOINT_INSTRUCTION_2 ;
2005-06-23 11:09:25 +04:00
p - > opcode = * p - > addr ;
}
void arch_arm_kprobe ( struct kprobe * p )
{
* p - > addr = BREAKPOINT_INSTRUCTION ;
flushi ( p - > addr ) ;
}
void arch_disarm_kprobe ( struct kprobe * p )
{
* p - > addr = p - > opcode ;
flushi ( p - > addr ) ;
2005-04-17 02:20:36 +04:00
}
void arch_remove_kprobe ( struct kprobe * p )
{
}
/* kprobe_status settings */
# define KPROBE_HIT_ACTIVE 0x00000001
# define KPROBE_HIT_SS 0x00000002
static struct kprobe * current_kprobe ;
static unsigned long current_kprobe_orig_tnpc ;
static unsigned long current_kprobe_orig_tstate_pil ;
static unsigned int kprobe_status ;
static inline void prepare_singlestep ( struct kprobe * p , struct pt_regs * regs )
{
current_kprobe_orig_tnpc = regs - > tnpc ;
current_kprobe_orig_tstate_pil = ( regs - > tstate & TSTATE_PIL ) ;
regs - > tstate | = TSTATE_PIL ;
/*single step inline, if it a breakpoint instruction*/
if ( p - > opcode = = BREAKPOINT_INSTRUCTION ) {
regs - > tpc = ( unsigned long ) p - > addr ;
regs - > tnpc = current_kprobe_orig_tnpc ;
} else {
regs - > tpc = ( unsigned long ) & p - > ainsn . insn [ 0 ] ;
regs - > tnpc = ( unsigned long ) & p - > ainsn . insn [ 1 ] ;
}
}
static int kprobe_handler ( struct pt_regs * regs )
{
struct kprobe * p ;
void * addr = ( void * ) regs - > tpc ;
int ret = 0 ;
preempt_disable ( ) ;
if ( kprobe_running ( ) ) {
/* We *are* holding lock here, so this is safe.
* Disarm the probe we just hit , and ignore it .
*/
p = get_kprobe ( addr ) ;
if ( p ) {
if ( kprobe_status = = KPROBE_HIT_SS ) {
regs - > tstate = ( ( regs - > tstate & ~ TSTATE_PIL ) |
current_kprobe_orig_tstate_pil ) ;
unlock_kprobes ( ) ;
goto no_kprobe ;
}
2005-06-23 11:09:25 +04:00
arch_disarm_kprobe ( p ) ;
regs - > tpc = ( unsigned long ) p - > addr ;
regs - > tnpc = current_kprobe_orig_tnpc ;
regs - > tstate = ( ( regs - > tstate & ~ TSTATE_PIL ) |
current_kprobe_orig_tstate_pil ) ;
2005-04-17 02:20:36 +04:00
ret = 1 ;
} else {
p = current_kprobe ;
if ( p - > break_handler & & p - > break_handler ( p , regs ) )
goto ss_probe ;
}
/* If it's not ours, can't be delete race, (we hold lock). */
goto no_kprobe ;
}
lock_kprobes ( ) ;
p = get_kprobe ( addr ) ;
if ( ! p ) {
unlock_kprobes ( ) ;
if ( * ( u32 * ) addr ! = BREAKPOINT_INSTRUCTION ) {
/*
* The breakpoint instruction was removed right
* after we hit it . Another cpu has removed
* either a probepoint or a debugger breakpoint
* at this address . In either case , no further
* handling of this interrupt is appropriate .
*/
ret = 1 ;
}
/* Not one of ours: let kernel handle it */
goto no_kprobe ;
}
kprobe_status = KPROBE_HIT_ACTIVE ;
current_kprobe = p ;
if ( p - > pre_handler & & p - > pre_handler ( p , regs ) )
return 1 ;
ss_probe :
prepare_singlestep ( p , regs ) ;
kprobe_status = KPROBE_HIT_SS ;
return 1 ;
no_kprobe :
preempt_enable_no_resched ( ) ;
return ret ;
}
/* If INSN is a relative control transfer instruction,
* return the corrected branch destination value .
*
* The original INSN location was REAL_PC , it actually
* executed at PC and produced destination address NPC .
*/
static unsigned long relbranch_fixup ( u32 insn , unsigned long real_pc ,
unsigned long pc , unsigned long npc )
{
/* Branch not taken, no mods necessary. */
if ( npc = = pc + 0x4UL )
return real_pc + 0x4UL ;
/* The three cases are call, branch w/prediction,
* and traditional branch .
*/
if ( ( insn & 0xc0000000 ) = = 0x40000000 | |
( insn & 0xc1c00000 ) = = 0x00400000 | |
( insn & 0xc1c00000 ) = = 0x00800000 ) {
/* The instruction did all the work for us
* already , just apply the offset to the correct
* instruction location .
*/
return ( real_pc + ( npc - pc ) ) ;
}
return real_pc + 0x4UL ;
}
/* If INSN is an instruction which writes it's PC location
* into a destination register , fix that up .
*/
static void retpc_fixup ( struct pt_regs * regs , u32 insn , unsigned long real_pc )
{
unsigned long * slot = NULL ;
/* Simplest cast is call, which always uses %o7 */
if ( ( insn & 0xc0000000 ) = = 0x40000000 ) {
slot = & regs - > u_regs [ UREG_I7 ] ;
}
/* Jmpl encodes the register inside of the opcode */
if ( ( insn & 0xc1f80000 ) = = 0x81c00000 ) {
unsigned long rd = ( ( insn > > 25 ) & 0x1f ) ;
if ( rd < = 15 ) {
slot = & regs - > u_regs [ rd ] ;
} else {
/* Hard case, it goes onto the stack. */
flushw_all ( ) ;
rd - = 16 ;
slot = ( unsigned long * )
( regs - > u_regs [ UREG_FP ] + STACK_BIAS ) ;
slot + = rd ;
}
}
if ( slot ! = NULL )
* slot = real_pc ;
}
/*
* Called after single - stepping . p - > addr is the address of the
* instruction whose first byte has been replaced by the breakpoint
* instruction . To avoid the SMP problems that can occur when we
* temporarily put back the original opcode to single - step , we
* single - stepped a copy of the instruction . The address of this
* copy is p - > ainsn . insn .
*
* This function prepares to return from the post - single - step
* breakpoint trap .
*/
static void resume_execution ( struct kprobe * p , struct pt_regs * regs )
{
u32 insn = p - > ainsn . insn [ 0 ] ;
regs - > tpc = current_kprobe_orig_tnpc ;
regs - > tnpc = relbranch_fixup ( insn ,
( unsigned long ) p - > addr ,
( unsigned long ) & p - > ainsn . insn [ 0 ] ,
regs - > tnpc ) ;
retpc_fixup ( regs , insn , ( unsigned long ) p - > addr ) ;
regs - > tstate = ( ( regs - > tstate & ~ TSTATE_PIL ) |
current_kprobe_orig_tstate_pil ) ;
}
static inline int post_kprobe_handler ( struct pt_regs * regs )
{
if ( ! kprobe_running ( ) )
return 0 ;
if ( current_kprobe - > post_handler )
current_kprobe - > post_handler ( current_kprobe , regs , 0 ) ;
resume_execution ( current_kprobe , regs ) ;
unlock_kprobes ( ) ;
preempt_enable_no_resched ( ) ;
return 1 ;
}
/* Interrupts disabled, kprobe_lock held. */
static inline int kprobe_fault_handler ( struct pt_regs * regs , int trapnr )
{
if ( current_kprobe - > fault_handler
& & current_kprobe - > fault_handler ( current_kprobe , regs , trapnr ) )
return 1 ;
if ( kprobe_status & KPROBE_HIT_SS ) {
resume_execution ( current_kprobe , regs ) ;
unlock_kprobes ( ) ;
preempt_enable_no_resched ( ) ;
}
return 0 ;
}
/*
* Wrapper routine to for handling exceptions .
*/
int kprobe_exceptions_notify ( struct notifier_block * self , unsigned long val ,
void * data )
{
struct die_args * args = ( struct die_args * ) data ;
switch ( val ) {
case DIE_DEBUG :
if ( kprobe_handler ( args - > regs ) )
return NOTIFY_STOP ;
break ;
case DIE_DEBUG_2 :
if ( post_kprobe_handler ( args - > regs ) )
return NOTIFY_STOP ;
break ;
case DIE_GPF :
if ( kprobe_running ( ) & &
kprobe_fault_handler ( args - > regs , args - > trapnr ) )
return NOTIFY_STOP ;
break ;
case DIE_PAGE_FAULT :
if ( kprobe_running ( ) & &
kprobe_fault_handler ( args - > regs , args - > trapnr ) )
return NOTIFY_STOP ;
break ;
default :
break ;
}
return NOTIFY_DONE ;
}
asmlinkage void kprobe_trap ( unsigned long trap_level , struct pt_regs * regs )
{
BUG_ON ( trap_level ! = 0x170 & & trap_level ! = 0x171 ) ;
if ( user_mode ( regs ) ) {
local_irq_enable ( ) ;
bad_trap ( regs , trap_level ) ;
return ;
}
/* trap_level == 0x170 --> ta 0x70
* trap_level = = 0x171 - - > ta 0x71
*/
if ( notify_die ( ( trap_level = = 0x170 ) ? DIE_DEBUG : DIE_DEBUG_2 ,
( trap_level = = 0x170 ) ? " debug " : " debug_2 " ,
regs , 0 , trap_level , SIGTRAP ) ! = NOTIFY_STOP )
bad_trap ( regs , trap_level ) ;
}
/* Jprobes support. */
static struct pt_regs jprobe_saved_regs ;
static struct pt_regs * jprobe_saved_regs_location ;
static struct sparc_stackf jprobe_saved_stack ;
int setjmp_pre_handler ( struct kprobe * p , struct pt_regs * regs )
{
struct jprobe * jp = container_of ( p , struct jprobe , kp ) ;
jprobe_saved_regs_location = regs ;
memcpy ( & jprobe_saved_regs , regs , sizeof ( * regs ) ) ;
/* Save a whole stack frame, this gets arguments
* pushed onto the stack after using up all the
* arg registers .
*/
memcpy ( & jprobe_saved_stack ,
( char * ) ( regs - > u_regs [ UREG_FP ] + STACK_BIAS ) ,
sizeof ( jprobe_saved_stack ) ) ;
regs - > tpc = ( unsigned long ) jp - > entry ;
regs - > tnpc = ( ( unsigned long ) jp - > entry ) + 0x4UL ;
regs - > tstate | = TSTATE_PIL ;
return 1 ;
}
void jprobe_return ( void )
{
preempt_enable_no_resched ( ) ;
__asm__ __volatile__ (
" .globl jprobe_return_trap_instruction \n "
" jprobe_return_trap_instruction: \n \t "
" ta 0x70 " ) ;
}
extern void jprobe_return_trap_instruction ( void ) ;
extern void __show_regs ( struct pt_regs * regs ) ;
int longjmp_break_handler ( struct kprobe * p , struct pt_regs * regs )
{
u32 * addr = ( u32 * ) regs - > tpc ;
if ( addr = = ( u32 * ) jprobe_return_trap_instruction ) {
if ( jprobe_saved_regs_location ! = regs ) {
printk ( " JPROBE: Current regs (%p) does not match "
" saved regs (%p). \n " ,
regs , jprobe_saved_regs_location ) ;
printk ( " JPROBE: Saved registers \n " ) ;
__show_regs ( jprobe_saved_regs_location ) ;
printk ( " JPROBE: Current registers \n " ) ;
__show_regs ( regs ) ;
BUG ( ) ;
}
/* Restore old register state. Do pt_regs
* first so that UREG_FP is the original one for
* the stack frame restore .
*/
memcpy ( regs , & jprobe_saved_regs , sizeof ( * regs ) ) ;
memcpy ( ( char * ) ( regs - > u_regs [ UREG_FP ] + STACK_BIAS ) ,
& jprobe_saved_stack ,
sizeof ( jprobe_saved_stack ) ) ;
return 1 ;
}
return 0 ;
}