2008-01-30 15:30:50 +03:00
/*
* x86 single - step support code , common to 32 - bit and 64 - bit .
*/
# include <linux/sched.h>
# include <linux/mm.h>
# include <linux/ptrace.h>
2009-07-18 19:08:54 +04:00
# include <asm/desc.h>
2008-01-30 15:30:50 +03:00
2008-01-30 15:33:12 +03:00
unsigned long convert_ip_to_linear ( struct task_struct * child , struct pt_regs * regs )
2008-01-30 15:30:50 +03:00
{
unsigned long addr , seg ;
2008-01-30 15:30:56 +03:00
addr = regs - > ip ;
2008-01-30 15:30:50 +03:00
seg = regs - > cs & 0xffff ;
2008-01-30 15:30:56 +03:00
if ( v8086_mode ( regs ) ) {
2008-01-30 15:30:50 +03:00
addr = ( addr & 0xffff ) + ( seg < < 4 ) ;
return addr ;
}
2008-01-30 15:30:50 +03:00
/*
* We ' ll assume that the code segments in the GDT
* are all zero - based . That is largely true : the
* TLS segments are used for data , and the PNPBIOS
* and APM bios ones we just ignore here .
*/
2008-01-30 15:30:50 +03:00
if ( ( seg & SEGMENT_TI_MASK ) = = SEGMENT_LDT ) {
2009-07-18 19:08:54 +04:00
struct desc_struct * desc ;
2008-01-30 15:30:50 +03:00
unsigned long base ;
seg & = ~ 7UL ;
mutex_lock ( & child - > mm - > context . lock ) ;
if ( unlikely ( ( seg > > 3 ) > = child - > mm - > context . size ) )
addr = - 1L ; /* bogus selector, access would fault */
else {
desc = child - > mm - > context . ldt + seg ;
2009-07-18 19:08:54 +04:00
base = get_desc_base ( desc ) ;
2008-01-30 15:30:50 +03:00
/* 16-bit code segment? */
2009-07-18 19:08:54 +04:00
if ( ! desc - > d )
2008-01-30 15:30:50 +03:00
addr & = 0xffff ;
addr + = base ;
}
mutex_unlock ( & child - > mm - > context . lock ) ;
}
return addr ;
}
static int is_setting_trap_flag ( struct task_struct * child , struct pt_regs * regs )
{
int i , copied ;
unsigned char opcode [ 15 ] ;
2008-01-30 15:33:12 +03:00
unsigned long addr = convert_ip_to_linear ( child , regs ) ;
2008-01-30 15:30:50 +03:00
copied = access_process_vm ( child , addr , opcode , sizeof ( opcode ) , 0 ) ;
for ( i = 0 ; i < copied ; i + + ) {
switch ( opcode [ i ] ) {
/* popf and iret */
case 0x9d : case 0xcf :
return 1 ;
/* CHECKME: 64 65 */
/* opcode and address size prefixes */
case 0x66 : case 0x67 :
continue ;
/* irrelevant prefixes (segment overrides and repeats) */
case 0x26 : case 0x2e :
case 0x36 : case 0x3e :
case 0x64 : case 0x65 :
2008-01-30 15:30:50 +03:00
case 0xf0 : case 0xf2 : case 0xf3 :
2008-01-30 15:30:50 +03:00
continue ;
2008-01-30 15:30:50 +03:00
# ifdef CONFIG_X86_64
2008-01-30 15:30:50 +03:00
case 0x40 . . . 0x4f :
2011-08-03 17:31:53 +04:00
if ( ! user_64bit_mode ( regs ) )
2008-01-30 15:30:50 +03:00
/* 32-bit mode: register increment */
return 0 ;
/* 64-bit mode: REX prefix */
continue ;
2008-01-30 15:30:50 +03:00
# endif
2008-01-30 15:30:50 +03:00
/* CHECKME: f2, f3 */
/*
* pushf : NOTE ! We should probably not let
* the user see the TF bit being set . But
* it ' s more pain than it ' s worth to avoid
* it , and a debugger could emulate this
* all in user space if it _really_ cares .
*/
case 0x9c :
default :
return 0 ;
}
}
return 0 ;
}
2008-01-30 15:30:54 +03:00
/*
* Enable single - stepping . Return nonzero if user mode is not using TF itself .
*/
static int enable_single_step ( struct task_struct * child )
2008-01-30 15:30:50 +03:00
{
struct pt_regs * regs = task_pt_regs ( child ) ;
2008-07-09 12:07:02 +04:00
unsigned long oflags ;
2008-01-30 15:30:50 +03:00
2008-07-09 13:39:29 +04:00
/*
* If we stepped into a sysenter / syscall insn , it trapped in
* kernel mode ; do_debug ( ) cleared TF and set TIF_SINGLESTEP .
* If user - mode had set TF itself , then it ' s still clear from
* do_debug ( ) and we need to set it again to restore the user
* state so we don ' t wrongly set TIF_FORCED_TF below .
* If enable_single_step ( ) was used last and that is what
* set TIF_SINGLESTEP , then both TF and TIF_FORCED_TF are
* already set and our bookkeeping is fine .
*/
if ( unlikely ( test_tsk_thread_flag ( child , TIF_SINGLESTEP ) ) )
regs - > flags | = X86_EFLAGS_TF ;
2008-01-30 15:30:50 +03:00
/*
* Always set TIF_SINGLESTEP - this guarantees that
* we single - step system calls etc . . This will also
* cause us to set TF when returning to user mode .
*/
set_tsk_thread_flag ( child , TIF_SINGLESTEP ) ;
2008-07-09 12:07:02 +04:00
oflags = regs - > flags ;
2008-01-30 15:30:50 +03:00
/* Set TF on the kernel stack.. */
2008-01-30 15:30:56 +03:00
regs - > flags | = X86_EFLAGS_TF ;
2008-01-30 15:30:50 +03:00
/*
* . . but if TF is changed by the instruction we will trace ,
* don ' t mark it as being " us " that set it , so that we
* won ' t clear it by hand later .
2008-07-09 12:07:02 +04:00
*
* Note that if we don ' t actually execute the popf because
* of a signal arriving right now or suchlike , we will lose
* track of the fact that it really was " us " that set it .
2008-01-30 15:30:50 +03:00
*/
2008-07-09 12:07:02 +04:00
if ( is_setting_trap_flag ( child , regs ) ) {
clear_tsk_thread_flag ( child , TIF_FORCED_TF ) ;
2008-01-30 15:30:54 +03:00
return 0 ;
2008-07-09 12:07:02 +04:00
}
/*
* If TF was already set , check whether it was us who set it .
* If not , we should never attempt a block step .
*/
if ( oflags & X86_EFLAGS_TF )
return test_tsk_thread_flag ( child , TIF_FORCED_TF ) ;
2008-01-30 15:30:50 +03:00
2008-01-30 15:30:50 +03:00
set_tsk_thread_flag ( child , TIF_FORCED_TF ) ;
2008-01-30 15:30:54 +03:00
return 1 ;
}
/*
* Enable single or block step .
*/
static void enable_step ( struct task_struct * child , bool block )
{
/*
* Make sure block stepping ( BTF ) is not enabled unless it should be .
* Note that we don ' t try to worry about any is_setting_trap_flag ( )
* instructions after the first when using block stepping .
2011-03-17 22:24:16 +03:00
* So no one should try to use debugger block stepping in a program
2008-01-30 15:30:54 +03:00
* that uses user - mode single stepping itself .
*/
2010-03-25 16:51:51 +03:00
if ( enable_single_step ( child ) & & block ) {
unsigned long debugctl = get_debugctlmsr ( ) ;
debugctl | = DEBUGCTLMSR_BTF ;
update_debugctlmsr ( debugctl ) ;
set_tsk_thread_flag ( child , TIF_BLOCKSTEP ) ;
} else if ( test_tsk_thread_flag ( child , TIF_BLOCKSTEP ) ) {
unsigned long debugctl = get_debugctlmsr ( ) ;
debugctl & = ~ DEBUGCTLMSR_BTF ;
update_debugctlmsr ( debugctl ) ;
clear_tsk_thread_flag ( child , TIF_BLOCKSTEP ) ;
}
2008-01-30 15:30:54 +03:00
}
void user_enable_single_step ( struct task_struct * child )
{
enable_step ( child , 0 ) ;
}
void user_enable_block_step ( struct task_struct * child )
{
enable_step ( child , 1 ) ;
2008-01-30 15:30:50 +03:00
}
void user_disable_single_step ( struct task_struct * child )
{
2008-01-30 15:30:54 +03:00
/*
* Make sure block stepping ( BTF ) is disabled .
*/
2010-03-25 16:51:51 +03:00
if ( test_tsk_thread_flag ( child , TIF_BLOCKSTEP ) ) {
unsigned long debugctl = get_debugctlmsr ( ) ;
debugctl & = ~ DEBUGCTLMSR_BTF ;
update_debugctlmsr ( debugctl ) ;
clear_tsk_thread_flag ( child , TIF_BLOCKSTEP ) ;
}
2008-01-30 15:30:54 +03:00
2008-01-30 15:30:50 +03:00
/* Always clear TIF_SINGLESTEP... */
clear_tsk_thread_flag ( child , TIF_SINGLESTEP ) ;
/* But touch TF only if it was set by us.. */
2008-01-30 15:30:50 +03:00
if ( test_and_clear_tsk_thread_flag ( child , TIF_FORCED_TF ) )
2008-01-30 15:30:56 +03:00
task_pt_regs ( child ) - > flags & = ~ X86_EFLAGS_TF ;
2008-01-30 15:30:50 +03:00
}