2019-06-03 07:44:50 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2014-01-28 16:50:18 +05:30
/*
* AArch64 KGDB support
*
* Based on arch / arm / kernel / kgdb . c
*
* Copyright ( C ) 2013 Cavium Inc .
* Author : Vijaya Kumar K < vijaya . kumar @ caviumnetworks . com >
*/
2016-09-23 16:42:08 +09:00
# include <linux/bug.h>
2014-01-28 16:50:18 +05:30
# include <linux/irq.h>
# include <linux/kdebug.h>
# include <linux/kgdb.h>
2016-07-08 12:35:49 -04:00
# include <linux/kprobes.h>
2017-02-08 18:51:37 +01:00
# include <linux/sched/task_stack.h>
2016-09-23 16:42:08 +09:00
# include <asm/debug-monitors.h>
# include <asm/insn.h>
2014-01-28 16:50:18 +05:30
# include <asm/traps.h>
struct dbg_reg_def_t dbg_reg_def [ DBG_MAX_REG_NUM ] = {
{ " x0 " , 8 , offsetof ( struct pt_regs , regs [ 0 ] ) } ,
{ " x1 " , 8 , offsetof ( struct pt_regs , regs [ 1 ] ) } ,
{ " x2 " , 8 , offsetof ( struct pt_regs , regs [ 2 ] ) } ,
{ " x3 " , 8 , offsetof ( struct pt_regs , regs [ 3 ] ) } ,
{ " x4 " , 8 , offsetof ( struct pt_regs , regs [ 4 ] ) } ,
{ " x5 " , 8 , offsetof ( struct pt_regs , regs [ 5 ] ) } ,
{ " x6 " , 8 , offsetof ( struct pt_regs , regs [ 6 ] ) } ,
{ " x7 " , 8 , offsetof ( struct pt_regs , regs [ 7 ] ) } ,
{ " x8 " , 8 , offsetof ( struct pt_regs , regs [ 8 ] ) } ,
{ " x9 " , 8 , offsetof ( struct pt_regs , regs [ 9 ] ) } ,
{ " x10 " , 8 , offsetof ( struct pt_regs , regs [ 10 ] ) } ,
{ " x11 " , 8 , offsetof ( struct pt_regs , regs [ 11 ] ) } ,
{ " x12 " , 8 , offsetof ( struct pt_regs , regs [ 12 ] ) } ,
{ " x13 " , 8 , offsetof ( struct pt_regs , regs [ 13 ] ) } ,
{ " x14 " , 8 , offsetof ( struct pt_regs , regs [ 14 ] ) } ,
{ " x15 " , 8 , offsetof ( struct pt_regs , regs [ 15 ] ) } ,
{ " x16 " , 8 , offsetof ( struct pt_regs , regs [ 16 ] ) } ,
{ " x17 " , 8 , offsetof ( struct pt_regs , regs [ 17 ] ) } ,
{ " x18 " , 8 , offsetof ( struct pt_regs , regs [ 18 ] ) } ,
{ " x19 " , 8 , offsetof ( struct pt_regs , regs [ 19 ] ) } ,
{ " x20 " , 8 , offsetof ( struct pt_regs , regs [ 20 ] ) } ,
{ " x21 " , 8 , offsetof ( struct pt_regs , regs [ 21 ] ) } ,
{ " x22 " , 8 , offsetof ( struct pt_regs , regs [ 22 ] ) } ,
{ " x23 " , 8 , offsetof ( struct pt_regs , regs [ 23 ] ) } ,
{ " x24 " , 8 , offsetof ( struct pt_regs , regs [ 24 ] ) } ,
{ " x25 " , 8 , offsetof ( struct pt_regs , regs [ 25 ] ) } ,
{ " x26 " , 8 , offsetof ( struct pt_regs , regs [ 26 ] ) } ,
{ " x27 " , 8 , offsetof ( struct pt_regs , regs [ 27 ] ) } ,
{ " x28 " , 8 , offsetof ( struct pt_regs , regs [ 28 ] ) } ,
{ " x29 " , 8 , offsetof ( struct pt_regs , regs [ 29 ] ) } ,
{ " x30 " , 8 , offsetof ( struct pt_regs , regs [ 30 ] ) } ,
{ " sp " , 8 , offsetof ( struct pt_regs , sp ) } ,
{ " pc " , 8 , offsetof ( struct pt_regs , pc ) } ,
2016-06-16 16:51:52 +01:00
/*
* struct pt_regs thinks PSTATE is 64 - bits wide but gdb remote
* protocol disagrees . Therefore we must extract only the lower
* 32 - bits . Look for the big comment in asm / kgdb . h for more
* detail .
*/
{ " pstate " , 4 , offsetof ( struct pt_regs , pstate )
# ifdef CONFIG_CPU_BIG_ENDIAN
+ 4
# endif
} ,
2014-01-28 16:50:18 +05:30
{ " v0 " , 16 , - 1 } ,
{ " v1 " , 16 , - 1 } ,
{ " v2 " , 16 , - 1 } ,
{ " v3 " , 16 , - 1 } ,
{ " v4 " , 16 , - 1 } ,
{ " v5 " , 16 , - 1 } ,
{ " v6 " , 16 , - 1 } ,
{ " v7 " , 16 , - 1 } ,
{ " v8 " , 16 , - 1 } ,
{ " v9 " , 16 , - 1 } ,
{ " v10 " , 16 , - 1 } ,
{ " v11 " , 16 , - 1 } ,
{ " v12 " , 16 , - 1 } ,
{ " v13 " , 16 , - 1 } ,
{ " v14 " , 16 , - 1 } ,
{ " v15 " , 16 , - 1 } ,
{ " v16 " , 16 , - 1 } ,
{ " v17 " , 16 , - 1 } ,
{ " v18 " , 16 , - 1 } ,
{ " v19 " , 16 , - 1 } ,
{ " v20 " , 16 , - 1 } ,
{ " v21 " , 16 , - 1 } ,
{ " v22 " , 16 , - 1 } ,
{ " v23 " , 16 , - 1 } ,
{ " v24 " , 16 , - 1 } ,
{ " v25 " , 16 , - 1 } ,
{ " v26 " , 16 , - 1 } ,
{ " v27 " , 16 , - 1 } ,
{ " v28 " , 16 , - 1 } ,
{ " v29 " , 16 , - 1 } ,
{ " v30 " , 16 , - 1 } ,
{ " v31 " , 16 , - 1 } ,
{ " fpsr " , 4 , - 1 } ,
{ " fpcr " , 4 , - 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 * task )
{
2018-03-05 15:43:09 -08:00
struct cpu_context * cpu_context = & task - > thread . cpu_context ;
2014-01-28 16:50:18 +05:30
/* Initialize to zero */
memset ( ( char * ) gdb_regs , 0 , NUMREGBYTES ) ;
2018-03-05 15:43:09 -08:00
gdb_regs [ 19 ] = cpu_context - > x19 ;
gdb_regs [ 20 ] = cpu_context - > x20 ;
gdb_regs [ 21 ] = cpu_context - > x21 ;
gdb_regs [ 22 ] = cpu_context - > x22 ;
gdb_regs [ 23 ] = cpu_context - > x23 ;
gdb_regs [ 24 ] = cpu_context - > x24 ;
gdb_regs [ 25 ] = cpu_context - > x25 ;
gdb_regs [ 26 ] = cpu_context - > x26 ;
gdb_regs [ 27 ] = cpu_context - > x27 ;
gdb_regs [ 28 ] = cpu_context - > x28 ;
gdb_regs [ 29 ] = cpu_context - > fp ;
gdb_regs [ 31 ] = cpu_context - > sp ;
gdb_regs [ 32 ] = cpu_context - > pc ;
2014-01-28 16:50:18 +05:30
}
void kgdb_arch_set_pc ( struct pt_regs * regs , unsigned long pc )
{
regs - > pc = pc ;
}
static int compiled_break ;
2014-01-28 11:20:19 +00:00
static void kgdb_arch_update_addr ( struct pt_regs * regs ,
char * remcom_in_buffer )
{
unsigned long addr ;
char * ptr ;
ptr = & remcom_in_buffer [ 1 ] ;
if ( kgdb_hex2long ( & ptr , & addr ) )
kgdb_arch_set_pc ( regs , addr ) ;
else if ( compiled_break = = 1 )
kgdb_arch_set_pc ( regs , regs - > pc + 4 ) ;
compiled_break = 0 ;
}
2014-01-28 16:50:18 +05:30
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 )
{
int err ;
switch ( remcom_in_buffer [ 0 ] ) {
case ' D ' :
case ' k ' :
/*
* Packet D ( Detach ) , k ( kill ) . No special handling
* is required here . Handle same as c packet .
*/
case ' c ' :
/*
* Packet c ( Continue ) to continue executing .
* Set pc to required address .
* Try to read optional parameter and set pc .
* If this was a compiled breakpoint , we need to move
* to the next instruction else we will just breakpoint
* over and over again .
*/
2014-01-28 11:20:19 +00:00
kgdb_arch_update_addr ( linux_regs , remcom_in_buffer ) ;
atomic_set ( & kgdb_cpu_doing_single_step , - 1 ) ;
kgdb_single_step = 0 ;
/*
* Received continue command , disable single step
*/
if ( kernel_active_single_step ( ) )
kernel_disable_single_step ( ) ;
err = 0 ;
break ;
case ' s ' :
/*
* Update step address value with address passed
* with step packet .
* On debug exception return PC is copied to ELR
* So just update PC .
* If no step address is passed , resume from the address
* pointed by PC . Do not update PC
*/
kgdb_arch_update_addr ( linux_regs , remcom_in_buffer ) ;
atomic_set ( & kgdb_cpu_doing_single_step , raw_smp_processor_id ( ) ) ;
kgdb_single_step = 1 ;
2014-01-28 16:50:18 +05:30
2014-01-28 11:20:19 +00:00
/*
* Enable single step handling
*/
if ( ! kernel_active_single_step ( ) )
kernel_enable_single_step ( linux_regs ) ;
2014-01-28 16:50:18 +05:30
err = 0 ;
break ;
default :
err = - 1 ;
}
return err ;
}
static int kgdb_brk_fn ( struct pt_regs * regs , unsigned int esr )
{
kgdb_handle_exception ( 1 , SIGTRAP , 0 , regs ) ;
2019-03-01 13:28:01 +00:00
return DBG_HOOK_HANDLED ;
2014-01-28 16:50:18 +05:30
}
2016-07-08 12:35:49 -04:00
NOKPROBE_SYMBOL ( kgdb_brk_fn )
2014-01-28 16:50:18 +05:30
static int kgdb_compiled_brk_fn ( struct pt_regs * regs , unsigned int esr )
{
compiled_break = 1 ;
kgdb_handle_exception ( 1 , SIGTRAP , 0 , regs ) ;
2019-03-01 13:28:01 +00:00
return DBG_HOOK_HANDLED ;
2014-01-28 16:50:18 +05:30
}
2016-07-08 12:35:49 -04:00
NOKPROBE_SYMBOL ( kgdb_compiled_brk_fn ) ;
2014-01-28 16:50:18 +05:30
2014-01-28 11:20:19 +00:00
static int kgdb_step_brk_fn ( struct pt_regs * regs , unsigned int esr )
{
2019-02-26 15:37:09 +00:00
if ( ! kgdb_single_step )
2016-11-02 14:40:42 +05:30
return DBG_HOOK_ERROR ;
2014-01-28 11:20:19 +00:00
kgdb_handle_exception ( 1 , SIGTRAP , 0 , regs ) ;
2019-03-01 13:28:01 +00:00
return DBG_HOOK_HANDLED ;
2014-01-28 11:20:19 +00:00
}
2016-07-08 12:35:49 -04:00
NOKPROBE_SYMBOL ( kgdb_step_brk_fn ) ;
2014-01-28 11:20:19 +00:00
2014-01-28 16:50:18 +05:30
static struct break_hook kgdb_brkpt_hook = {
2019-02-26 12:52:47 +00:00
. fn = kgdb_brk_fn ,
. imm = KGDB_DYN_DBG_BRK_IMM ,
2014-01-28 16:50:18 +05:30
} ;
static struct break_hook kgdb_compiled_brkpt_hook = {
2019-02-26 12:52:47 +00:00
. fn = kgdb_compiled_brk_fn ,
. imm = KGDB_COMPILED_DBG_BRK_IMM ,
2014-01-28 16:50:18 +05:30
} ;
2014-01-28 11:20:19 +00:00
static struct step_hook kgdb_step_hook = {
. fn = kgdb_step_brk_fn
} ;
2014-01-28 16:50:18 +05:30
static int __kgdb_notify ( struct die_args * args , unsigned long cmd )
{
struct pt_regs * regs = args - > regs ;
if ( kgdb_handle_exception ( 1 , args - > signr , cmd , regs ) )
return NOTIFY_DONE ;
return NOTIFY_STOP ;
}
static int
kgdb_notify ( struct notifier_block * self , unsigned long cmd , void * ptr )
{
unsigned long flags ;
int ret ;
local_irq_save ( flags ) ;
ret = __kgdb_notify ( ptr , cmd ) ;
local_irq_restore ( flags ) ;
return ret ;
}
static struct notifier_block kgdb_notifier = {
. notifier_call = kgdb_notify ,
/*
* Want to be lowest priority
*/
. priority = - INT_MAX ,
} ;
/*
2016-02-24 09:52:41 -08:00
* kgdb_arch_init - Perform any architecture specific initialization .
* This function will handle the initialization of any architecture
2014-01-28 16:50:18 +05:30
* specific callbacks .
*/
int kgdb_arch_init ( void )
{
int ret = register_die_notifier ( & kgdb_notifier ) ;
if ( ret ! = 0 )
return ret ;
2019-02-26 12:52:47 +00:00
register_kernel_break_hook ( & kgdb_brkpt_hook ) ;
register_kernel_break_hook ( & kgdb_compiled_brkpt_hook ) ;
register_kernel_step_hook ( & kgdb_step_hook ) ;
2014-01-28 16:50:18 +05:30
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 )
{
2019-02-26 12:52:47 +00:00
unregister_kernel_break_hook ( & kgdb_brkpt_hook ) ;
unregister_kernel_break_hook ( & kgdb_compiled_brkpt_hook ) ;
unregister_kernel_step_hook ( & kgdb_step_hook ) ;
2014-01-28 16:50:18 +05:30
unregister_die_notifier ( & kgdb_notifier ) ;
}
2018-12-06 20:07:40 +00:00
const struct kgdb_arch arch_kgdb_ops ;
2016-09-23 16:42:08 +09:00
int kgdb_arch_set_breakpoint ( struct kgdb_bkpt * bpt )
{
int err ;
BUILD_BUG_ON ( AARCH64_INSN_SIZE ! = BREAK_INSTR_SIZE ) ;
err = aarch64_insn_read ( ( void * ) bpt - > bpt_addr , ( u32 * ) bpt - > saved_instr ) ;
if ( err )
return err ;
return aarch64_insn_write ( ( void * ) bpt - > bpt_addr ,
( u32 ) AARCH64_BREAK_KGDB_DYN_DBG ) ;
}
int kgdb_arch_remove_breakpoint ( struct kgdb_bkpt * bpt )
{
return aarch64_insn_write ( ( void * ) bpt - > bpt_addr ,
* ( u32 * ) bpt - > saved_instr ) ;
}