2008-02-20 22:33:40 +03:00
/*
* arch / arm / kernel / kgdb . c
*
* ARM KGDB support
*
* Copyright ( c ) 2002 - 2004 MontaVista Software , Inc
* Copyright ( c ) 2008 Wind River Systems , Inc .
*
* Authors : George Davis < davis_g @ mvista . com >
* Deepak Saxena < dsaxena @ plexity . net >
*/
2010-03-12 13:03:58 +03:00
# include <linux/irq.h>
2008-02-20 22:33:40 +03:00
# include <linux/kgdb.h>
# include <asm/traps.h>
2010-08-05 18:22:21 +04:00
struct dbg_reg_def_t dbg_reg_def [ DBG_MAX_REG_NUM ] =
2008-02-20 22:33:40 +03:00
{
2010-08-05 18:22:21 +04:00
{ " r0 " , 4 , offsetof ( struct pt_regs , ARM_r0 ) } ,
{ " r1 " , 4 , offsetof ( struct pt_regs , ARM_r1 ) } ,
{ " r2 " , 4 , offsetof ( struct pt_regs , ARM_r2 ) } ,
{ " r3 " , 4 , offsetof ( struct pt_regs , ARM_r3 ) } ,
{ " r4 " , 4 , offsetof ( struct pt_regs , ARM_r4 ) } ,
{ " r5 " , 4 , offsetof ( struct pt_regs , ARM_r5 ) } ,
{ " r6 " , 4 , offsetof ( struct pt_regs , ARM_r6 ) } ,
{ " r7 " , 4 , offsetof ( struct pt_regs , ARM_r7 ) } ,
{ " r8 " , 4 , offsetof ( struct pt_regs , ARM_r8 ) } ,
{ " r9 " , 4 , offsetof ( struct pt_regs , ARM_r9 ) } ,
{ " r10 " , 4 , offsetof ( struct pt_regs , ARM_r10 ) } ,
{ " fp " , 4 , offsetof ( struct pt_regs , ARM_fp ) } ,
{ " ip " , 4 , offsetof ( struct pt_regs , ARM_ip ) } ,
{ " sp " , 4 , offsetof ( struct pt_regs , ARM_sp ) } ,
{ " lr " , 4 , offsetof ( struct pt_regs , ARM_lr ) } ,
{ " pc " , 4 , offsetof ( struct pt_regs , ARM_pc ) } ,
{ " f0 " , 12 , - 1 } ,
{ " f1 " , 12 , - 1 } ,
{ " f2 " , 12 , - 1 } ,
{ " f3 " , 12 , - 1 } ,
{ " f4 " , 12 , - 1 } ,
{ " f5 " , 12 , - 1 } ,
{ " f6 " , 12 , - 1 } ,
{ " f7 " , 12 , - 1 } ,
{ " fps " , 4 , - 1 } ,
{ " cpsr " , 4 , offsetof ( struct pt_regs , ARM_cpsr ) } ,
} ;
2008-02-20 22:33:40 +03:00
2010-08-05 18:22:21 +04:00
char * dbg_get_reg ( int regno , void * mem , struct pt_regs * regs )
{
if ( regno > = DBG_MAX_REG_NUM | | regno < 0 )
return NULL ;
2008-02-20 22:33:40 +03:00
2010-08-05 18:22:21 +04:00
if ( dbg_reg_def [ regno ] . offset ! = - 1 )
memcpy ( mem , ( void * ) regs + dbg_reg_def [ regno ] . offset ,
dbg_reg_def [ regno ] . size ) ;
else
memset ( mem , 0 , dbg_reg_def [ regno ] . size ) ;
return dbg_reg_def [ regno ] . name ;
2008-02-20 22:33:40 +03:00
}
2010-08-05 18:22:21 +04:00
int dbg_set_reg ( int regno , void * mem , struct pt_regs * regs )
2008-02-20 22:33:40 +03:00
{
2010-08-05 18:22:21 +04:00
if ( regno > = DBG_MAX_REG_NUM | | regno < 0 )
return - EINVAL ;
if ( dbg_reg_def [ regno ] . offset ! = - 1 )
memcpy ( ( void * ) regs + dbg_reg_def [ regno ] . offset , mem ,
dbg_reg_def [ regno ] . size ) ;
return 0 ;
2008-02-20 22:33:40 +03:00
}
void
sleeping_thread_to_gdb_regs ( unsigned long * gdb_regs , struct task_struct * task )
{
struct pt_regs * thread_regs ;
int regno ;
/* Just making sure... */
if ( task = = NULL )
return ;
/* Initialize to zero */
for ( regno = 0 ; regno < GDB_MAX_REGS ; regno + + )
gdb_regs [ regno ] = 0 ;
/* Otherwise, we have only some registers from switch_to() */
thread_regs = task_pt_regs ( task ) ;
gdb_regs [ _R0 ] = thread_regs - > ARM_r0 ;
gdb_regs [ _R1 ] = thread_regs - > ARM_r1 ;
gdb_regs [ _R2 ] = thread_regs - > ARM_r2 ;
gdb_regs [ _R3 ] = thread_regs - > ARM_r3 ;
gdb_regs [ _R4 ] = thread_regs - > ARM_r4 ;
gdb_regs [ _R5 ] = thread_regs - > ARM_r5 ;
gdb_regs [ _R6 ] = thread_regs - > ARM_r6 ;
gdb_regs [ _R7 ] = thread_regs - > ARM_r7 ;
gdb_regs [ _R8 ] = thread_regs - > ARM_r8 ;
gdb_regs [ _R9 ] = thread_regs - > ARM_r9 ;
gdb_regs [ _R10 ] = thread_regs - > ARM_r10 ;
gdb_regs [ _FP ] = thread_regs - > ARM_fp ;
gdb_regs [ _IP ] = thread_regs - > ARM_ip ;
gdb_regs [ _SPT ] = thread_regs - > ARM_sp ;
gdb_regs [ _LR ] = thread_regs - > ARM_lr ;
gdb_regs [ _PC ] = thread_regs - > ARM_pc ;
gdb_regs [ _CPSR ] = thread_regs - > ARM_cpsr ;
}
2010-05-21 06:04:21 +04:00
void kgdb_arch_set_pc ( struct pt_regs * regs , unsigned long pc )
{
regs - > ARM_pc = pc ;
}
2008-02-20 22:33:40 +03:00
static int compiled_break ;
int kgdb_arch_handle_exception ( int exception_vector , int signo ,
int err_code , char * remcom_in_buffer ,
char * remcom_out_buffer ,
struct pt_regs * linux_regs )
{
unsigned long addr ;
char * ptr ;
switch ( remcom_in_buffer [ 0 ] ) {
case ' D ' :
case ' k ' :
case ' c ' :
/*
* Try to read optional parameter , pc unchanged if no parm .
* If this was a compiled breakpoint , we need to move
* to the next instruction or we will just breakpoint
* over and over again .
*/
ptr = & remcom_in_buffer [ 1 ] ;
if ( kgdb_hex2long ( & ptr , & addr ) )
linux_regs - > ARM_pc = addr ;
else if ( compiled_break = = 1 )
linux_regs - > ARM_pc + = 4 ;
compiled_break = 0 ;
return 0 ;
}
return - 1 ;
}
static int kgdb_brk_fn ( struct pt_regs * regs , unsigned int instr )
{
kgdb_handle_exception ( 1 , SIGTRAP , 0 , regs ) ;
return 0 ;
}
static int kgdb_compiled_brk_fn ( struct pt_regs * regs , unsigned int instr )
{
compiled_break = 1 ;
kgdb_handle_exception ( 1 , SIGTRAP , 0 , regs ) ;
return 0 ;
}
static struct undef_hook kgdb_brkpt_hook = {
. instr_mask = 0xffffffff ,
. instr_val = KGDB_BREAKINST ,
. fn = kgdb_brk_fn
} ;
static struct undef_hook kgdb_compiled_brkpt_hook = {
. instr_mask = 0xffffffff ,
. instr_val = KGDB_COMPILED_BREAK ,
. fn = kgdb_compiled_brk_fn
} ;
2010-03-12 13:03:58 +03:00
static void kgdb_call_nmi_hook ( void * ignored )
{
kgdb_nmicallback ( raw_smp_processor_id ( ) , get_irq_regs ( ) ) ;
}
void kgdb_roundup_cpus ( unsigned long flags )
{
local_irq_enable ( ) ;
smp_call_function ( kgdb_call_nmi_hook , NULL , 0 ) ;
local_irq_disable ( ) ;
}
2008-02-20 22:33:40 +03:00
/**
* kgdb_arch_init - Perform any architecture specific initalization .
*
* This function will handle the initalization of any architecture
* specific callbacks .
*/
int kgdb_arch_init ( void )
{
register_undef_hook ( & kgdb_brkpt_hook ) ;
register_undef_hook ( & kgdb_compiled_brkpt_hook ) ;
return 0 ;
}
/**
* kgdb_arch_exit - Perform any architecture specific uninitalization .
*
* This function will handle the uninitalization of any architecture
* specific callbacks , for dynamic registration and unregistration .
*/
void kgdb_arch_exit ( void )
{
unregister_undef_hook ( & kgdb_brkpt_hook ) ;
unregister_undef_hook ( & kgdb_compiled_brkpt_hook ) ;
}
/*
* Register our undef instruction hooks with ARM undef core .
* We regsiter a hook specifically looking for the KGB break inst
* and we handle the normal undef case within the do_undefinstr
* handler .
*/
struct kgdb_arch arch_kgdb_ops = {
# ifndef __ARMEB__
. gdb_bpt_instr = { 0xfe , 0xde , 0xff , 0xe7 }
# else /* ! __ARMEB__ */
. gdb_bpt_instr = { 0xe7 , 0xff , 0xde , 0xfe }
# endif
} ;