2013-08-28 19:53:17 -04:00
/*
* Copyright 2013 Tilera Corporation . All Rights Reserved .
*
* 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 , version 2.
*
* 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 , GOOD TITLE or
* NON INFRINGEMENT . See the GNU General Public License for
* more details .
*
* TILE - Gx KGDB support .
*/
# include <linux/ptrace.h>
# include <linux/kgdb.h>
# include <linux/kdebug.h>
# include <linux/uaccess.h>
# include <linux/module.h>
# include <asm/cacheflush.h>
static tile_bundle_bits singlestep_insn = TILEGX_BPT_BUNDLE | DIE_SSTEPBP ;
static unsigned long stepped_addr ;
static tile_bundle_bits stepped_instr ;
struct dbg_reg_def_t dbg_reg_def [ DBG_MAX_REG_NUM ] = {
{ " r0 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 0 ] ) } ,
{ " r1 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 1 ] ) } ,
{ " r2 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 2 ] ) } ,
{ " r3 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 3 ] ) } ,
{ " r4 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 4 ] ) } ,
{ " r5 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 5 ] ) } ,
{ " r6 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 6 ] ) } ,
{ " r7 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 7 ] ) } ,
{ " r8 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 8 ] ) } ,
{ " r9 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 9 ] ) } ,
{ " r10 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 10 ] ) } ,
{ " r11 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 11 ] ) } ,
{ " r12 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 12 ] ) } ,
{ " r13 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 13 ] ) } ,
{ " r14 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 14 ] ) } ,
{ " r15 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 15 ] ) } ,
{ " r16 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 16 ] ) } ,
{ " r17 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 17 ] ) } ,
{ " r18 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 18 ] ) } ,
{ " r19 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 19 ] ) } ,
{ " r20 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 20 ] ) } ,
{ " r21 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 21 ] ) } ,
{ " r22 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 22 ] ) } ,
{ " r23 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 23 ] ) } ,
{ " r24 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 24 ] ) } ,
{ " r25 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 25 ] ) } ,
{ " r26 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 26 ] ) } ,
{ " r27 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 27 ] ) } ,
{ " r28 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 28 ] ) } ,
{ " r29 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 29 ] ) } ,
{ " r30 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 30 ] ) } ,
{ " r31 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 31 ] ) } ,
{ " r32 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 32 ] ) } ,
{ " r33 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 33 ] ) } ,
{ " r34 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 34 ] ) } ,
{ " r35 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 35 ] ) } ,
{ " r36 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 36 ] ) } ,
{ " r37 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 37 ] ) } ,
{ " r38 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 38 ] ) } ,
{ " r39 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 39 ] ) } ,
{ " r40 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 40 ] ) } ,
{ " r41 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 41 ] ) } ,
{ " r42 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 42 ] ) } ,
{ " r43 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 43 ] ) } ,
{ " r44 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 44 ] ) } ,
{ " r45 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 45 ] ) } ,
{ " r46 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 46 ] ) } ,
{ " r47 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 47 ] ) } ,
{ " r48 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 48 ] ) } ,
{ " r49 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 49 ] ) } ,
{ " r50 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 50 ] ) } ,
{ " r51 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 51 ] ) } ,
{ " r52 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , regs [ 52 ] ) } ,
{ " tp " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , tp ) } ,
{ " sp " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , sp ) } ,
{ " lr " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , lr ) } ,
{ " sn " , GDB_SIZEOF_REG , - 1 } ,
{ " idn0 " , GDB_SIZEOF_REG , - 1 } ,
{ " idn1 " , GDB_SIZEOF_REG , - 1 } ,
{ " udn0 " , GDB_SIZEOF_REG , - 1 } ,
{ " udn1 " , GDB_SIZEOF_REG , - 1 } ,
{ " udn2 " , GDB_SIZEOF_REG , - 1 } ,
{ " udn3 " , GDB_SIZEOF_REG , - 1 } ,
{ " zero " , GDB_SIZEOF_REG , - 1 } ,
{ " pc " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , pc ) } ,
{ " faultnum " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , faultnum ) } ,
} ;
char * dbg_get_reg ( int regno , void * mem , struct pt_regs * regs )
{
if ( regno > = DBG_MAX_REG_NUM | | regno < 0 )
return NULL ;
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 ;
}
int dbg_set_reg ( int regno , void * mem , struct pt_regs * regs )
{
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 ;
}
/*
* Similar to pt_regs_to_gdb_regs ( ) except that process is sleeping and so
* we may not be able to get all the info .
*/
void
sleeping_thread_to_gdb_regs ( unsigned long * gdb_regs , struct task_struct * task )
{
struct pt_regs * thread_regs ;
if ( task = = NULL )
return ;
/* Initialize to zero. */
memset ( gdb_regs , 0 , NUMREGBYTES ) ;
thread_regs = task_pt_regs ( task ) ;
2014-11-12 10:11:31 +08:00
memcpy ( gdb_regs , thread_regs , TREG_LAST_GPR * sizeof ( unsigned long ) ) ;
2013-08-28 19:53:17 -04:00
gdb_regs [ TILEGX_PC_REGNUM ] = thread_regs - > pc ;
gdb_regs [ TILEGX_FAULTNUM_REGNUM ] = thread_regs - > faultnum ;
}
void kgdb_arch_set_pc ( struct pt_regs * regs , unsigned long pc )
{
regs - > pc = pc ;
}
static void kgdb_call_nmi_hook ( void * ignored )
{
kgdb_nmicallback ( raw_smp_processor_id ( ) , NULL ) ;
}
void kgdb_roundup_cpus ( unsigned long flags )
{
local_irq_enable ( ) ;
smp_call_function ( kgdb_call_nmi_hook , NULL , 0 ) ;
local_irq_disable ( ) ;
}
/*
* Convert a kernel address to the writable kernel text mapping .
*/
static unsigned long writable_address ( unsigned long addr )
{
unsigned long ret = 0 ;
if ( core_kernel_text ( addr ) )
2015-09-30 09:53:47 +08:00
ret = ktext_writable_addr ( addr ) ;
2013-08-28 19:53:17 -04:00
else if ( is_module_text_address ( addr ) )
ret = addr ;
else
pr_err ( " Unknown virtual address 0x%lx \n " , addr ) ;
return ret ;
}
/*
* Calculate the new address for after a step .
*/
static unsigned long get_step_address ( struct pt_regs * regs )
{
int src_reg ;
int jump_off ;
int br_off ;
unsigned long addr ;
unsigned int opcode ;
tile_bundle_bits bundle ;
/* Move to the next instruction by default. */
addr = regs - > pc + TILEGX_BUNDLE_SIZE_IN_BYTES ;
bundle = * ( unsigned long * ) instruction_pointer ( regs ) ;
/* 0: X mode, Otherwise: Y mode. */
if ( bundle & TILEGX_BUNDLE_MODE_MASK ) {
if ( get_Opcode_Y1 ( bundle ) = = RRR_1_OPCODE_Y1 & &
get_RRROpcodeExtension_Y1 ( bundle ) = =
UNARY_RRR_1_OPCODE_Y1 ) {
opcode = get_UnaryOpcodeExtension_Y1 ( bundle ) ;
switch ( opcode ) {
case JALR_UNARY_OPCODE_Y1 :
case JALRP_UNARY_OPCODE_Y1 :
case JR_UNARY_OPCODE_Y1 :
case JRP_UNARY_OPCODE_Y1 :
src_reg = get_SrcA_Y1 ( bundle ) ;
dbg_get_reg ( src_reg , & addr , regs ) ;
break ;
}
}
} else if ( get_Opcode_X1 ( bundle ) = = RRR_0_OPCODE_X1 ) {
if ( get_RRROpcodeExtension_X1 ( bundle ) = =
UNARY_RRR_0_OPCODE_X1 ) {
opcode = get_UnaryOpcodeExtension_X1 ( bundle ) ;
switch ( opcode ) {
case JALR_UNARY_OPCODE_X1 :
case JALRP_UNARY_OPCODE_X1 :
case JR_UNARY_OPCODE_X1 :
case JRP_UNARY_OPCODE_X1 :
src_reg = get_SrcA_X1 ( bundle ) ;
dbg_get_reg ( src_reg , & addr , regs ) ;
break ;
}
}
} else if ( get_Opcode_X1 ( bundle ) = = JUMP_OPCODE_X1 ) {
opcode = get_JumpOpcodeExtension_X1 ( bundle ) ;
switch ( opcode ) {
case JAL_JUMP_OPCODE_X1 :
case J_JUMP_OPCODE_X1 :
jump_off = sign_extend ( get_JumpOff_X1 ( bundle ) , 27 ) ;
addr = regs - > pc +
( jump_off < < TILEGX_LOG2_BUNDLE_SIZE_IN_BYTES ) ;
break ;
}
} else if ( get_Opcode_X1 ( bundle ) = = BRANCH_OPCODE_X1 ) {
br_off = 0 ;
opcode = get_BrType_X1 ( bundle ) ;
switch ( opcode ) {
case BEQZT_BRANCH_OPCODE_X1 :
case BEQZ_BRANCH_OPCODE_X1 :
if ( get_SrcA_X1 ( bundle ) = = 0 )
br_off = get_BrOff_X1 ( bundle ) ;
break ;
case BGEZT_BRANCH_OPCODE_X1 :
case BGEZ_BRANCH_OPCODE_X1 :
if ( get_SrcA_X1 ( bundle ) > = 0 )
br_off = get_BrOff_X1 ( bundle ) ;
break ;
case BGTZT_BRANCH_OPCODE_X1 :
case BGTZ_BRANCH_OPCODE_X1 :
if ( get_SrcA_X1 ( bundle ) > 0 )
br_off = get_BrOff_X1 ( bundle ) ;
break ;
case BLBCT_BRANCH_OPCODE_X1 :
case BLBC_BRANCH_OPCODE_X1 :
if ( ! ( get_SrcA_X1 ( bundle ) & 1 ) )
br_off = get_BrOff_X1 ( bundle ) ;
break ;
case BLBST_BRANCH_OPCODE_X1 :
case BLBS_BRANCH_OPCODE_X1 :
if ( get_SrcA_X1 ( bundle ) & 1 )
br_off = get_BrOff_X1 ( bundle ) ;
break ;
case BLEZT_BRANCH_OPCODE_X1 :
case BLEZ_BRANCH_OPCODE_X1 :
if ( get_SrcA_X1 ( bundle ) < = 0 )
br_off = get_BrOff_X1 ( bundle ) ;
break ;
case BLTZT_BRANCH_OPCODE_X1 :
case BLTZ_BRANCH_OPCODE_X1 :
if ( get_SrcA_X1 ( bundle ) < 0 )
br_off = get_BrOff_X1 ( bundle ) ;
break ;
case BNEZT_BRANCH_OPCODE_X1 :
case BNEZ_BRANCH_OPCODE_X1 :
if ( get_SrcA_X1 ( bundle ) ! = 0 )
br_off = get_BrOff_X1 ( bundle ) ;
break ;
}
if ( br_off ! = 0 ) {
br_off = sign_extend ( br_off , 17 ) ;
addr = regs - > pc +
( br_off < < TILEGX_LOG2_BUNDLE_SIZE_IN_BYTES ) ;
}
}
return addr ;
}
/*
* Replace the next instruction after the current instruction with a
* breakpoint instruction .
*/
static void do_single_step ( struct pt_regs * regs )
{
unsigned long addr_wr ;
/* Determine where the target instruction will send us to. */
stepped_addr = get_step_address ( regs ) ;
probe_kernel_read ( ( char * ) & stepped_instr , ( char * ) stepped_addr ,
BREAK_INSTR_SIZE ) ;
addr_wr = writable_address ( stepped_addr ) ;
probe_kernel_write ( ( char * ) addr_wr , ( char * ) & singlestep_insn ,
BREAK_INSTR_SIZE ) ;
smp_wmb ( ) ;
flush_icache_range ( stepped_addr , stepped_addr + BREAK_INSTR_SIZE ) ;
}
static void undo_single_step ( struct pt_regs * regs )
{
unsigned long addr_wr ;
if ( stepped_instr = = 0 )
return ;
addr_wr = writable_address ( stepped_addr ) ;
probe_kernel_write ( ( char * ) addr_wr , ( char * ) & stepped_instr ,
BREAK_INSTR_SIZE ) ;
stepped_instr = 0 ;
smp_wmb ( ) ;
flush_icache_range ( stepped_addr , stepped_addr + BREAK_INSTR_SIZE ) ;
}
/*
* Calls linux_debug_hook before the kernel dies . If KGDB is enabled ,
* then try to fall into the debugger .
*/
static int
kgdb_notify ( struct notifier_block * self , unsigned long cmd , void * ptr )
{
int ret ;
unsigned long flags ;
struct die_args * args = ( struct die_args * ) ptr ;
struct pt_regs * regs = args - > regs ;
# ifdef CONFIG_KPROBES
/*
* Return immediately if the kprobes fault notifier has set
* DIE_PAGE_FAULT .
*/
if ( cmd = = DIE_PAGE_FAULT )
return NOTIFY_DONE ;
# endif /* CONFIG_KPROBES */
switch ( cmd ) {
case DIE_BREAK :
case DIE_COMPILED_BPT :
break ;
case DIE_SSTEPBP :
local_irq_save ( flags ) ;
kgdb_handle_exception ( 0 , SIGTRAP , 0 , regs ) ;
local_irq_restore ( flags ) ;
return NOTIFY_STOP ;
default :
/* Userspace events, ignore. */
if ( user_mode ( regs ) )
return NOTIFY_DONE ;
}
local_irq_save ( flags ) ;
ret = kgdb_handle_exception ( args - > trapnr , args - > signr , args - > err , regs ) ;
local_irq_restore ( flags ) ;
if ( ret )
return NOTIFY_DONE ;
return NOTIFY_STOP ;
}
static struct notifier_block kgdb_notifier = {
. notifier_call = kgdb_notify ,
} ;
/*
* kgdb_arch_handle_exception - Handle architecture specific GDB packets .
* @ vector : The error vector of the exception that happened .
* @ signo : The signal number of the exception that happened .
* @ err_code : The error code of the exception that happened .
* @ remcom_in_buffer : The buffer of the packet we have read .
* @ remcom_out_buffer : The buffer of % BUFMAX bytes to write a packet into .
* @ regs : The & struct pt_regs of the current process .
*
* This function MUST handle the ' c ' and ' s ' command packets ,
* as well packets to set / remove a hardware breakpoint , if used .
* If there are additional packets which the hardware needs to handle ,
* they are handled here . The code should return - 1 if it wants to
* process more packets , and a % 0 or % 1 if it wants to exit from the
* kgdb callback .
*/
int kgdb_arch_handle_exception ( int vector , int signo , int err_code ,
char * remcom_in_buffer , char * remcom_out_buffer ,
struct pt_regs * regs )
{
char * ptr ;
unsigned long address ;
/* Undo any stepping we may have done. */
undo_single_step ( regs ) ;
switch ( remcom_in_buffer [ 0 ] ) {
case ' c ' :
case ' s ' :
case ' D ' :
case ' k ' :
/*
* Try to read optional parameter , pc unchanged if no parm .
* If this was a compiled - in 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 , & address ) )
regs - > pc = address ;
else if ( * ( unsigned long * ) regs - > pc = = compiled_bpt )
regs - > pc + = BREAK_INSTR_SIZE ;
if ( remcom_in_buffer [ 0 ] = = ' s ' ) {
do_single_step ( regs ) ;
kgdb_single_step = 1 ;
atomic_set ( & kgdb_cpu_doing_single_step ,
raw_smp_processor_id ( ) ) ;
} else
atomic_set ( & kgdb_cpu_doing_single_step , - 1 ) ;
return 0 ;
}
return - 1 ; /* this means that we do not want to exit from the handler */
}
struct kgdb_arch arch_kgdb_ops ;
/*
* kgdb_arch_init - Perform any architecture specific initalization .
*
* This function will handle the initalization of any architecture
* specific callbacks .
*/
int kgdb_arch_init ( void )
{
tile_bundle_bits bundle = TILEGX_BPT_BUNDLE ;
memcpy ( arch_kgdb_ops . gdb_bpt_instr , & bundle , BREAK_INSTR_SIZE ) ;
return register_die_notifier ( & kgdb_notifier ) ;
}
/*
* 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_die_notifier ( & kgdb_notifier ) ;
}
int kgdb_arch_set_breakpoint ( struct kgdb_bkpt * bpt )
{
int err ;
unsigned long addr_wr = writable_address ( bpt - > bpt_addr ) ;
if ( addr_wr = = 0 )
return - 1 ;
err = probe_kernel_read ( bpt - > saved_instr , ( char * ) bpt - > bpt_addr ,
BREAK_INSTR_SIZE ) ;
if ( err )
return err ;
err = probe_kernel_write ( ( char * ) addr_wr , arch_kgdb_ops . gdb_bpt_instr ,
BREAK_INSTR_SIZE ) ;
smp_wmb ( ) ;
flush_icache_range ( ( unsigned long ) bpt - > bpt_addr ,
( unsigned long ) bpt - > bpt_addr + BREAK_INSTR_SIZE ) ;
return err ;
}
int kgdb_arch_remove_breakpoint ( struct kgdb_bkpt * bpt )
{
int err ;
unsigned long addr_wr = writable_address ( bpt - > bpt_addr ) ;
if ( addr_wr = = 0 )
return - 1 ;
err = probe_kernel_write ( ( char * ) addr_wr , ( char * ) bpt - > saved_instr ,
BREAK_INSTR_SIZE ) ;
smp_wmb ( ) ;
flush_icache_range ( ( unsigned long ) bpt - > bpt_addr ,
( unsigned long ) bpt - > bpt_addr + BREAK_INSTR_SIZE ) ;
return err ;
}