All of lore.kernel.org
 help / color / mirror / Atom feed
From: will.deacon@arm.com (Will Deacon)
To: linux-arm-kernel@lists.infradead.org
Subject: [RFC PATCH 3/4] ARM: hw-breakpoint: add new ptrace requests for hw-breakpoint interaction
Date: Tue,  2 Feb 2010 16:22:27 +0000	[thread overview]
Message-ID: <1265127748-4587-4-git-send-email-will.deacon@arm.com> (raw)
In-Reply-To: <1265127748-4587-3-git-send-email-will.deacon@arm.com>

For debuggers to take advantage of the hw-breakpoint framework in the kernel,
it is necessary to expose the API calls via a ptrace interface.

This patch adds support for a debugger to insert, modify and remove hardware
breakpoints using PTRACE_SET_HWBKPT requests. The breakpoints are stored in
the debug_info struct of the running thread. A further request,
PTRACE_GET_HWBKPT can be used to query the number of hardware resources
available.

The ptrace interaction is controlled using a 32-bit word, as described in
arch/arm/include/asm/hw_breakpoint.h.

Signed-off-by: Will Deacon <will.deacon@arm.com>
---
 arch/arm/include/asm/processor.h |    4 +
 arch/arm/include/asm/ptrace.h    |    2 +
 arch/arm/kernel/ptrace.c         |  158 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 164 insertions(+), 0 deletions(-)

diff --git a/arch/arm/include/asm/processor.h b/arch/arm/include/asm/processor.h
index 6a89567..ddb0953 100644
--- a/arch/arm/include/asm/processor.h
+++ b/arch/arm/include/asm/processor.h
@@ -19,6 +19,7 @@
 
 #ifdef __KERNEL__
 
+#include <asm/hw_breakpoint.h>
 #include <asm/ptrace.h>
 #include <asm/types.h>
 
@@ -41,6 +42,9 @@ struct debug_entry {
 struct debug_info {
 	int			nsaved;
 	struct debug_entry	bp[2];
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+	struct perf_event	*hbp[HBP_NUM];
+#endif
 };
 
 struct thread_struct {
diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h
index eec6e89..ea35e4e 100644
--- a/arch/arm/include/asm/ptrace.h
+++ b/arch/arm/include/asm/ptrace.h
@@ -29,6 +29,8 @@
 #define PTRACE_SETCRUNCHREGS	26
 #define PTRACE_GETVFPREGS	27
 #define PTRACE_SETVFPREGS	28
+#define PTRACE_GET_HWBKPT	29
+#define PTRACE_SET_HWBKPT	30
 
 /*
  * PSR bits
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index a2ea385..1e02bc5 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -19,6 +19,8 @@
 #include <linux/init.h>
 #include <linux/signal.h>
 #include <linux/uaccess.h>
+#include <linux/perf_event.h>
+#include <linux/hw_breakpoint.h>
 
 #include <asm/pgtable.h>
 #include <asm/system.h>
@@ -707,6 +709,152 @@ static int ptrace_setvfpregs(struct task_struct *tsk, void __user *data)
 }
 #endif
 
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+/*
+ * Handle hitting a HW-breakpoint.
+ */
+static void ptrace_hwbreak_triggered(struct perf_event *bp, int unused,
+					struct perf_sample_data *data,
+					struct pt_regs *regs)
+{
+	struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp);
+	siginfo_t info;
+
+	info.si_signo	= SIGTRAP;
+	info.si_errno	= bp->attr.bp_type;
+	info.si_code	= TRAP_HWBKPT;
+	info.si_addr	= (void __user *)(bkpt->trigger);
+
+	force_sig_info(SIGTRAP, &info, current);
+}
+
+static int ptrace_insert_hwbkpt(struct task_struct *tsk, unsigned long addr,
+				u8 n, u8 len, u8 type)
+{
+	struct perf_event *bp;
+	struct thread_struct *t = &tsk->thread;
+	struct perf_event_attr attr;
+	int ret = 0;
+
+	/* Watchpoints live in the upper indices. */
+	if ((type & HW_BREAKPOINT_X) == 0)
+		n += ARM_MAX_BRP;
+
+	if (n >= HBP_NUM) {
+		ret = -ENOSPC;
+		goto out;
+	}
+
+	if (!t->debug.hbp[n]) {
+		/* New breakpoint. */
+		hw_breakpoint_init(&attr);
+		attr.bp_addr = addr;
+		attr.bp_len = len;
+		attr.bp_type = type;
+
+		bp = register_user_hw_breakpoint(&attr,
+				ptrace_hwbreak_triggered, tsk);
+		if (IS_ERR(bp))
+			ret = PTR_ERR(bp);
+		else
+			t->debug.hbp[n] = bp;
+	} else {
+		/* Existing breakpoint. */
+		bp = t->debug.hbp[n];
+		attr = bp->attr;
+		attr.bp_addr = addr;
+		attr.bp_len = len;
+		attr.disabled = 0;
+		ret = modify_user_hw_breakpoint(bp, &attr);
+	}
+
+out:
+	return ret;
+}
+
+static int ptrace_remove_hwbkpt(struct task_struct *tsk, u8 n, u8 type)
+{
+	struct perf_event *bp;
+	struct thread_struct *t = &tsk->thread;
+	struct perf_event_attr attr;
+	int ret = -EINVAL;
+
+	if ((type & HW_BREAKPOINT_X) == 0)
+		n += ARM_MAX_BRP;
+
+	if (n < HBP_NUM) {
+		bp = t->debug.hbp[n];
+		if (bp == NULL)
+			goto out;
+		attr = bp->attr;
+		attr.disabled = 1;
+		ret = modify_user_hw_breakpoint(bp, &attr);
+	} else {
+		ret = -ENOSPC;
+	}
+
+out:
+	return ret;
+}
+
+static int ptrace_get_hwbkpt(struct task_struct *tsk, long addr,
+				unsigned long __user *data)
+{
+	unsigned long num;
+	int ret = 0;
+
+	switch (addr) {
+	case PTRACE_HWBREAK_GET_NUM_BRPS:
+		num = ptrace_get_num_brps();
+		break;
+	case PTRACE_HWBREAK_GET_NUM_WRPS:
+		num = ptrace_get_num_wrps();
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	if (!ret && put_user(num, data))
+		ret = -EFAULT;
+
+	return ret;
+}
+
+static int ptrace_set_hwbkpt(struct task_struct *tsk, long addr,
+				unsigned long data)
+{
+	int ret;
+	u8 num, len, type;
+
+	/* Check version field. */
+	if (PTRACE_HWBREAK_VER(data) != 0) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* Extract data fields. */
+	num = PTRACE_HWBREAK_NUM(data);
+	len = PTRACE_HWBREAK_LEN(data);
+	type = PTRACE_HWBREAK_TYPE(data);
+
+	/* Insert or remove the breakpoint. */
+	switch (PTRACE_HWBREAK_OP(data)) {
+	case PTRACE_HWBREAK_OP_REMOVE:
+		ret = ptrace_remove_hwbkpt(tsk, num, type);
+		break;
+	case PTRACE_HWBREAK_OP_INSERT:
+		ret = ptrace_insert_hwbkpt(tsk, addr, num, len, type);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+out:
+	return ret;
+}
+
+#endif
+
 long arch_ptrace(struct task_struct *child, long request, long addr, long data)
 {
 	int ret;
@@ -839,6 +987,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
 			break;
 #endif
 
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+		case PTRACE_GET_HWBKPT:
+			ret = ptrace_get_hwbkpt(child, addr,
+						(unsigned long __user *)data);
+			break;
+		case PTRACE_SET_HWBKPT:
+			ret = ptrace_set_hwbkpt(child, addr, data);
+			break;
+#endif
+
 		default:
 			ret = ptrace_request(child, request, addr, data);
 			break;
-- 
1.6.5.7

  reply	other threads:[~2010-02-02 16:22 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-02-02 16:22 [RFC PATCH 0/4] ARM: add support for hw-breakpoints Will Deacon
2010-02-02 16:22 ` [RFC PATCH 1/4] ARM: hw-breakpoint: add mechanism for hooking into prefetch aborts Will Deacon
2010-02-02 16:22   ` [RFC PATCH 2/4] ARM: hw-breakpoint: add ARM backend for the hw-breakpoint framework Will Deacon
2010-02-02 16:22     ` Will Deacon [this message]
2010-02-02 16:22       ` [RFC PATCH 4/4] ARM: hw-breakpoint: add HAVE_HW_BREAKPOINT to Kconfig Will Deacon
  -- strict thread matches above, loose matches on Subject: below --
2010-03-10 16:01 [RFC PATCH 0/4] ARM: add support for hw-breakpoints [v2] Will Deacon
2010-03-10 16:01 ` [RFC PATCH 1/4] ARM: hw-breakpoint: add mechanism for hooking into prefetch aborts Will Deacon
2010-03-10 16:01   ` [RFC PATCH 2/4] ARM: hw-breakpoint: add ARM backend for the hw-breakpoint framework Will Deacon
2010-03-10 16:01     ` [RFC PATCH 3/4] ARM: hw-breakpoint: add new ptrace requests for hw-breakpoint interaction Will Deacon

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1265127748-4587-4-git-send-email-will.deacon@arm.com \
    --to=will.deacon@arm.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.