Skip to content

Commit

Permalink
arm64: debug: consolidate software breakpoint handlers
Browse files Browse the repository at this point in the history
The software breakpoint handlers are hooked in directly from ptrace,
which makes it difficult to add additional handlers for things like
kprobes and kgdb.

This patch moves the handling code into debug-monitors.c, where we can
dispatch to different debug subsystems more easily.

Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
  • Loading branch information
Will Deacon authored and Catalin Marinas committed Jun 12, 2013
1 parent adace89 commit 1442b6e
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 66 deletions.
9 changes: 9 additions & 0 deletions arch/arm64/include/asm/debug-monitors.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ static inline int reinstall_suspended_bps(struct pt_regs *regs)
}
#endif

#ifdef CONFIG_COMPAT
int aarch32_break_handler(struct pt_regs *regs);
#else
static int aarch32_break_handler(struct pt_regs *regs)
{
return -EFAULT;
}
#endif

#endif /* __ASSEMBLY */
#endif /* __KERNEL__ */
#endif /* __ASM_DEBUG_MONITORS_H */
2 changes: 0 additions & 2 deletions arch/arm64/include/asm/ptrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,5 @@ extern unsigned long profile_pc(struct pt_regs *regs);
#define profile_pc(regs) instruction_pointer(regs)
#endif

extern int aarch32_break_trap(struct pt_regs *regs);

#endif /* __ASSEMBLY__ */
#endif
66 changes: 64 additions & 2 deletions arch/arm64/kernel/debug-monitors.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <linux/init.h>
#include <linux/ptrace.h>
#include <linux/stat.h>
#include <linux/uaccess.h>

#include <asm/debug-monitors.h>
#include <asm/local.h>
Expand Down Expand Up @@ -226,13 +227,74 @@ static int single_step_handler(unsigned long addr, unsigned int esr,
return 0;
}

static int __init single_step_init(void)
static int brk_handler(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
{
siginfo_t info;

if (!user_mode(regs))
return -EFAULT;

info = (siginfo_t) {
.si_signo = SIGTRAP,
.si_errno = 0,
.si_code = TRAP_BRKPT,
.si_addr = (void __user *)instruction_pointer(regs),
};

force_sig_info(SIGTRAP, &info, current);
return 0;
}

int aarch32_break_handler(struct pt_regs *regs)
{
siginfo_t info;
unsigned int instr;
bool bp = false;
void __user *pc = (void __user *)instruction_pointer(regs);

if (!compat_user_mode(regs))
return -EFAULT;

if (compat_thumb_mode(regs)) {
/* get 16-bit Thumb instruction */
get_user(instr, (u16 __user *)pc);
if (instr == AARCH32_BREAK_THUMB2_LO) {
/* get second half of 32-bit Thumb-2 instruction */
get_user(instr, (u16 __user *)(pc + 2));
bp = instr == AARCH32_BREAK_THUMB2_HI;
} else {
bp = instr == AARCH32_BREAK_THUMB;
}
} else {
/* 32-bit ARM instruction */
get_user(instr, (u32 __user *)pc);
bp = (instr & ~0xf0000000) == AARCH32_BREAK_ARM;
}

if (!bp)
return -EFAULT;

info = (siginfo_t) {
.si_signo = SIGTRAP,
.si_errno = 0,
.si_code = TRAP_BRKPT,
.si_addr = pc,
};

force_sig_info(SIGTRAP, &info, current);
return 0;
}

static int __init debug_traps_init(void)
{
hook_debug_fault_code(DBG_ESR_EVT_HWSS, single_step_handler, SIGTRAP,
TRAP_HWBKPT, "single-step handler");
hook_debug_fault_code(DBG_ESR_EVT_BRK, brk_handler, SIGTRAP,
TRAP_BRKPT, "ptrace BRK handler");
return 0;
}
arch_initcall(single_step_init);
arch_initcall(debug_traps_init);

/* Re-enable single step for syscall restarting. */
void user_rewind_single_step(struct task_struct *task)
Expand Down
59 changes: 0 additions & 59 deletions arch/arm64/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,28 +53,6 @@ void ptrace_disable(struct task_struct *child)
{
}

/*
* Handle hitting a breakpoint.
*/
static int ptrace_break(struct pt_regs *regs)
{
siginfo_t info = {
.si_signo = SIGTRAP,
.si_errno = 0,
.si_code = TRAP_BRKPT,
.si_addr = (void __user *)instruction_pointer(regs),
};

force_sig_info(SIGTRAP, &info, current);
return 0;
}

static int arm64_break_trap(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
{
return ptrace_break(regs);
}

#ifdef CONFIG_HAVE_HW_BREAKPOINT
/*
* Handle hitting a HW-breakpoint.
Expand Down Expand Up @@ -817,33 +795,6 @@ static const struct user_regset_view user_aarch32_view = {
.regsets = aarch32_regsets, .n = ARRAY_SIZE(aarch32_regsets)
};

int aarch32_break_trap(struct pt_regs *regs)
{
unsigned int instr;
bool bp = false;
void __user *pc = (void __user *)instruction_pointer(regs);

if (compat_thumb_mode(regs)) {
/* get 16-bit Thumb instruction */
get_user(instr, (u16 __user *)pc);
if (instr == AARCH32_BREAK_THUMB2_LO) {
/* get second half of 32-bit Thumb-2 instruction */
get_user(instr, (u16 __user *)(pc + 2));
bp = instr == AARCH32_BREAK_THUMB2_HI;
} else {
bp = instr == AARCH32_BREAK_THUMB;
}
} else {
/* 32-bit ARM instruction */
get_user(instr, (u32 __user *)pc);
bp = (instr & ~0xf0000000) == AARCH32_BREAK_ARM;
}

if (bp)
return ptrace_break(regs);
return 1;
}

static int compat_ptrace_read_user(struct task_struct *tsk, compat_ulong_t off,
compat_ulong_t __user *ret)
{
Expand Down Expand Up @@ -1111,16 +1062,6 @@ long arch_ptrace(struct task_struct *child, long request,
return ptrace_request(child, request, addr, data);
}


static int __init ptrace_break_init(void)
{
hook_debug_fault_code(DBG_ESR_EVT_BRK, arm64_break_trap, SIGTRAP,
TRAP_BRKPT, "ptrace BRK handler");
return 0;
}
core_initcall(ptrace_break_init);


asmlinkage int syscall_trace(int dir, struct pt_regs *regs)
{
unsigned long saved_reg;
Expand Down
5 changes: 2 additions & 3 deletions arch/arm64/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <linux/syscalls.h>

#include <asm/atomic.h>
#include <asm/debug-monitors.h>
#include <asm/traps.h>
#include <asm/stacktrace.h>
#include <asm/exception.h>
Expand Down Expand Up @@ -261,11 +262,9 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
siginfo_t info;
void __user *pc = (void __user *)instruction_pointer(regs);

#ifdef CONFIG_COMPAT
/* check for AArch32 breakpoint instructions */
if (compat_user_mode(regs) && aarch32_break_trap(regs) == 0)
if (!aarch32_break_handler(regs))
return;
#endif

if (show_unhandled_signals && unhandled_signal(current, SIGILL) &&
printk_ratelimit()) {
Expand Down

0 comments on commit 1442b6e

Please sign in to comment.