2019-05-19 15:51:43 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-02-16 19:26:43 +08:00
/*
* Nios2 KGDB support
*
* Copyright ( C ) 2015 Altera Corporation
* Copyright ( C ) 2011 Tobias Klauser < tklauser @ distanz . ch >
*
* Based on the code posted by Kazuyasu on the Altera Forum at :
* http : //www.alteraforum.com/forum/showpost.php?p=77003&postcount=20
*/
# include <linux/ptrace.h>
# include <linux/kgdb.h>
# include <linux/kdebug.h>
# include <linux/io.h>
static int wait_for_remote_debugger ;
struct dbg_reg_def_t dbg_reg_def [ DBG_MAX_REG_NUM ] =
{
{ " zero " , GDB_SIZEOF_REG , - 1 } ,
{ " at " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , r1 ) } ,
{ " r2 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , r2 ) } ,
{ " r3 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , r3 ) } ,
{ " r4 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , r4 ) } ,
{ " r5 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , r5 ) } ,
{ " r6 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , r6 ) } ,
{ " r7 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , r7 ) } ,
{ " r8 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , r8 ) } ,
{ " r9 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , r9 ) } ,
{ " r10 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , r10 ) } ,
{ " r11 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , r11 ) } ,
{ " r12 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , r12 ) } ,
{ " r13 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , r13 ) } ,
{ " r14 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , r14 ) } ,
{ " r15 " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , r15 ) } ,
{ " r16 " , GDB_SIZEOF_REG , - 1 } ,
{ " r17 " , GDB_SIZEOF_REG , - 1 } ,
{ " r18 " , GDB_SIZEOF_REG , - 1 } ,
{ " r19 " , GDB_SIZEOF_REG , - 1 } ,
{ " r20 " , GDB_SIZEOF_REG , - 1 } ,
{ " r21 " , GDB_SIZEOF_REG , - 1 } ,
{ " r22 " , GDB_SIZEOF_REG , - 1 } ,
{ " r23 " , GDB_SIZEOF_REG , - 1 } ,
{ " et " , GDB_SIZEOF_REG , - 1 } ,
{ " bt " , GDB_SIZEOF_REG , - 1 } ,
{ " gp " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , gp ) } ,
{ " sp " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , sp ) } ,
{ " fp " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , fp ) } ,
{ " ea " , GDB_SIZEOF_REG , - 1 } ,
{ " ba " , GDB_SIZEOF_REG , - 1 } ,
{ " ra " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , ra ) } ,
{ " pc " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , ea ) } ,
{ " status " , GDB_SIZEOF_REG , - 1 } ,
{ " estatus " , GDB_SIZEOF_REG , offsetof ( struct pt_regs , estatus ) } ,
{ " bstatus " , GDB_SIZEOF_REG , - 1 } ,
{ " ienable " , GDB_SIZEOF_REG , - 1 } ,
{ " ipending " , GDB_SIZEOF_REG , - 1 } ,
{ " cpuid " , GDB_SIZEOF_REG , - 1 } ,
{ " ctl6 " , GDB_SIZEOF_REG , - 1 } ,
{ " exception " , GDB_SIZEOF_REG , - 1 } ,
{ " pteaddr " , GDB_SIZEOF_REG , - 1 } ,
{ " tlbacc " , GDB_SIZEOF_REG , - 1 } ,
{ " tlbmisc " , GDB_SIZEOF_REG , - 1 } ,
{ " eccinj " , GDB_SIZEOF_REG , - 1 } ,
{ " badaddr " , GDB_SIZEOF_REG , - 1 } ,
{ " config " , GDB_SIZEOF_REG , - 1 } ,
{ " mpubase " , GDB_SIZEOF_REG , - 1 } ,
{ " mpuacc " , GDB_SIZEOF_REG , - 1 } ,
} ;
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 ;
}
void sleeping_thread_to_gdb_regs ( unsigned long * gdb_regs , struct task_struct * p )
{
memset ( ( char * ) gdb_regs , 0 , NUMREGBYTES ) ;
gdb_regs [ GDB_SP ] = p - > thread . kregs - > sp ;
gdb_regs [ GDB_PC ] = p - > thread . kregs - > ea ;
}
void kgdb_arch_set_pc ( struct pt_regs * regs , unsigned long pc )
{
regs - > ea = pc ;
}
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 addr ;
switch ( remcom_in_buffer [ 0 ] ) {
case ' s ' :
case ' c ' :
/* handle the optional parameters */
ptr = & remcom_in_buffer [ 1 ] ;
if ( kgdb_hex2long ( & ptr , & addr ) )
regs - > ea = addr ;
return 0 ;
}
return - 1 ; /* this means that we do not want to exit from the handler */
}
asmlinkage void kgdb_breakpoint_c ( struct pt_regs * regs )
{
/*
* The breakpoint entry code has moved the PC on by 4 bytes , so we must
* move it back . This could be done on the host but we do it here
*/
if ( ! wait_for_remote_debugger )
regs - > ea - = 4 ;
else /* pass the first trap 30 code */
wait_for_remote_debugger = 0 ;
kgdb_handle_exception ( 30 , SIGTRAP , 0 , regs ) ;
}
int kgdb_arch_init ( void )
{
wait_for_remote_debugger = 1 ;
return 0 ;
}
void kgdb_arch_exit ( void )
{
/* Nothing to do */
}
2018-12-06 20:07:40 +00:00
const struct kgdb_arch arch_kgdb_ops = {
2015-02-16 19:26:43 +08:00
/* Breakpoint instruction: trap 30 */
. gdb_bpt_instr = { 0xba , 0x6f , 0x3b , 0x00 } ,
} ;