// SPDX-License-Identifier: GPL-2.0-only
/*
* AArch64 KGDB support
*
* Based on arch / arm / kernel / kgdb . c
*
* Copyright ( C ) 2013 Cavium Inc .
* Author : Vijaya Kumar K < vijaya . kumar @ caviumnetworks . com >
*/
#include <linux/bug.h>
#include <linux/irq.h>
#include <linux/kdebug.h>
#include <linux/kgdb.h>
#include <linux/kprobes.h>
#include <linux/sched/task_stack.h>
#include <asm /debug-monitors.h>
#include <asm /insn.h>
#include <asm /text-patching.h>
#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)},
/*
* 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
},
{ "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)
{
struct cpu_context *cpu_context = &task->thread.cpu_context;
/* Initialize to zero */
memset((char *)gdb_regs, 0 , NUMREGBYTES);
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;
}
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
{
regs->pc = pc;
}
static int compiled_break;
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 ;
}
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 .
*/
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 ;
/*
* Enable single step handling
*/
if (!kernel_active_single_step())
kernel_enable_single_step(linux_regs);
else
kernel_rewind_single_step(linux_regs);
err = 0 ;
break ;
default :
err = -1 ;
}
return err;
}
int kgdb_brk_handler(struct pt_regs *regs, unsigned long esr)
{
kgdb_handle_exception(1 , SIGTRAP, 0 , regs);
return DBG_HOOK_HANDLED;
}
NOKPROBE_SYMBOL(kgdb_brk_handler)
int kgdb_compiled_brk_handler(struct pt_regs *regs, unsigned long esr)
{
compiled_break = 1 ;
kgdb_handle_exception(1 , SIGTRAP, 0 , regs);
return DBG_HOOK_HANDLED;
}
NOKPROBE_SYMBOL(kgdb_compiled_brk_handler);
int kgdb_single_step_handler(struct pt_regs *regs, unsigned long esr)
{
if (!kgdb_single_step)
return DBG_HOOK_ERROR;
kgdb_handle_exception(0 , SIGTRAP, 0 , regs);
return DBG_HOOK_HANDLED;
}
NOKPROBE_SYMBOL(kgdb_single_step_handler);
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,
};
/*
* kgdb_arch_init - Perform any architecture specific initialization .
* This function will handle the initialization of any architecture
* specific callbacks .
*/
int kgdb_arch_init(void )
{
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);
}
const struct kgdb_arch arch_kgdb_ops;
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);
}
Messung V0.5 in Prozent C=97 H=95 G=95
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland