// SPDX-License-Identifier: GPL-2.0
/*
* 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 >
*/
#include <linux/irq.h>
#include <linux/kdebug.h>
#include <linux/kgdb.h>
#include <linux/uaccess.h>
#include <asm /text-patching.h>
#include <asm /traps.h>
struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] =
{
{ "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)},
};
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 thread_info *ti;
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() */
ti = task_thread_info(task);
gdb_regs[_R4] = ti->cpu_context.r4;
gdb_regs[_R5] = ti->cpu_context.r5;
gdb_regs[_R6] = ti->cpu_context.r6;
gdb_regs[_R7] = ti->cpu_context.r7;
gdb_regs[_R8] = ti->cpu_context.r8;
gdb_regs[_R9] = ti->cpu_context.r9;
gdb_regs[_R10] = ti->cpu_context.sl;
gdb_regs[_FP] = ti->cpu_context.fp;
gdb_regs[_SPT] = ti->cpu_context.sp;
gdb_regs[_PC] = ti->cpu_context.pc;
}
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
{
regs->ARM_pc = pc;
}
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_arm_hook = {
.instr_mask = 0 xffffffff,
.instr_val = KGDB_BREAKINST,
.cpsr_mask = PSR_T_BIT | MODE_MASK,
.cpsr_val = SVC_MODE,
.fn = kgdb_brk_fn
};
static struct undef_hook kgdb_brkpt_thumb_hook = {
.instr_mask = 0 xffff,
.instr_val = KGDB_BREAKINST & 0 xffff,
.cpsr_mask = PSR_T_BIT | MODE_MASK,
.cpsr_val = PSR_T_BIT | SVC_MODE,
.fn = kgdb_brk_fn
};
static struct undef_hook kgdb_compiled_brkpt_arm_hook = {
.instr_mask = 0 xffffffff,
.instr_val = KGDB_COMPILED_BREAK,
.cpsr_mask = PSR_T_BIT | MODE_MASK,
.cpsr_val = SVC_MODE,
.fn = kgdb_compiled_brk_fn
};
static struct undef_hook kgdb_compiled_brkpt_thumb_hook = {
.instr_mask = 0 xffff,
.instr_val = KGDB_COMPILED_BREAK & 0 xffff,
.cpsr_mask = PSR_T_BIT | MODE_MASK,
.cpsr_val = PSR_T_BIT | SVC_MODE,
.fn = kgdb_compiled_brk_fn
};
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,
.priority = -INT_MAX,
};
/**
* kgdb_arch_init - Perform any architecture specific initalization .
*
* This function will handle the initalization of any architecture
* specific callbacks .
*/
int kgdb_arch_init(void )
{
int ret = register_die_notifier(&kgdb_notifier);
if (ret != 0 )
return ret;
register_undef_hook(&kgdb_brkpt_arm_hook);
register_undef_hook(&kgdb_brkpt_thumb_hook);
register_undef_hook(&kgdb_compiled_brkpt_arm_hook);
register_undef_hook(&kgdb_compiled_brkpt_thumb_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_arm_hook);
unregister_undef_hook(&kgdb_brkpt_thumb_hook);
unregister_undef_hook(&kgdb_compiled_brkpt_arm_hook);
unregister_undef_hook(&kgdb_compiled_brkpt_thumb_hook);
unregister_die_notifier(&kgdb_notifier);
}
int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
{
int err;
/* patch_text() only supports int-sized breakpoints */
BUILD_BUG_ON(sizeof (int ) != BREAK_INSTR_SIZE);
err = copy_from_kernel_nofault(bpt->saved_instr, (char *)bpt->bpt_addr,
BREAK_INSTR_SIZE);
if (err)
return err;
/* Machine is already stopped, so we can use __patch_text() directly */
__patch_text((void *)bpt->bpt_addr,
*(unsigned int *)arch_kgdb_ops.gdb_bpt_instr);
return err;
}
int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt)
{
/* Machine is already stopped, so we can use __patch_text() directly */
__patch_text((void *)bpt->bpt_addr, *(unsigned int *)bpt->saved_instr);
return 0 ;
}
/*
* Register our undef instruction hooks with ARM undef core .
* We register a hook specifically looking for the KGB break inst
* and we handle the normal undef case within the do_undefinstr
* handler .
*/
const struct kgdb_arch arch_kgdb_ops = {
#ifndef __ARMEB__
.gdb_bpt_instr = {0 xfe, 0 xde, 0 xff, 0 xe7}
#else /* ! __ARMEB__ */
.gdb_bpt_instr = {0 xe7, 0 xff, 0 xde, 0 xfe}
#endif
};
Messung V0.5 in Prozent C=96 H=96 G=95
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland