From: "K.Prasad" <prasad@linux.vnet.ibm.com>
To: Ingo Molnar <mingo@elte.hu>,
Linux Kernel Mailing List <linux-kernel@vger.kernel.org>
Cc: Alan Stern <stern@rowland.harvard.edu>,
Andrew Morton <akpm@linux-foundation.org>,
Benjamin Herrenschmidt <benh@au1.ibm.com>,
Frederic Weisbecker <fweisbec@gmail.com>,
Maneesh Soni <maneesh@in.ibm.com>,
Roland McGrath <roland@redhat.com>,
Steven Rostedt <rostedt@goodmis.org>,
"K.Prasad" <prasad@linux.vnet.ibm.com>
Subject: [Patch 08/11] Modify Ptrace routines to access breakpoint registers
Date: Fri, 20 Mar 2009 05:19:40 +0530 [thread overview]
Message-ID: <20090319234940.GI10517@in.ibm.com> (raw)
In-Reply-To: 20090319234044.410725944@K.Prasad
[-- Attachment #1: 8 --]
[-- Type: text/plain, Size: 8692 bytes --]
From: Alan Stern <stern@rowland.harvard.edu>
This patch modifies the ptrace code to use the new wrapper routines around the
debug/breakpoint registers.
[K.Prasad: Adapted the ptrace routines and to changes post x86/x86_64 merger,
split the minor patch from bigger patch. Re-wrote ptrace_write_dr7()
and ptrace_set_debugreg() functions to use new data-structures]
[K.Prasad: Changed code to suit the simplified HW breakpoint implementation]
Signed-off-by: K.Prasad <prasad@linux.vnet.ibm.com>
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
---
arch/x86/kernel/ptrace.c | 229 ++++++++++++++++++++++++++++-------------------
1 file changed, 138 insertions(+), 91 deletions(-)
Index: linux-2.6-tip.hbkpt/arch/x86/kernel/ptrace.c
===================================================================
--- linux-2.6-tip.hbkpt.orig/arch/x86/kernel/ptrace.c
+++ linux-2.6-tip.hbkpt/arch/x86/kernel/ptrace.c
@@ -34,6 +34,7 @@
#include <asm/prctl.h>
#include <asm/proto.h>
#include <asm/ds.h>
+#include <asm/hw_breakpoint.h>
#include "tls.h"
@@ -134,11 +135,6 @@ static int set_segment_reg(struct task_s
return 0;
}
-static unsigned long debugreg_addr_limit(struct task_struct *task)
-{
- return TASK_SIZE - 3;
-}
-
#else /* CONFIG_X86_64 */
#define FLAG_MASK (FLAG_MASK_32 | X86_EFLAGS_NT)
@@ -263,15 +259,6 @@ static int set_segment_reg(struct task_s
return 0;
}
-static unsigned long debugreg_addr_limit(struct task_struct *task)
-{
-#ifdef CONFIG_IA32_EMULATION
- if (test_tsk_thread_flag(task, TIF_IA32))
- return IA32_PAGE_OFFSET - 3;
-#endif
- return TASK_SIZE_MAX - 7;
-}
-
#endif /* CONFIG_X86_32 */
static unsigned long get_flags(struct task_struct *task)
@@ -462,95 +449,155 @@ static int genregs_set(struct task_struc
}
/*
- * This function is trivial and will be inlined by the compiler.
- * Having it separates the implementation details of debug
- * registers from the interface details of ptrace.
+ * Decode the length and type bits for a particular breakpoint as
+ * stored in debug register 7. Return the "enabled" status.
*/
-static unsigned long ptrace_get_debugreg(struct task_struct *child, int n)
+static int decode_dr7(unsigned long dr7, int bpnum, unsigned *len,
+ unsigned *type)
{
- switch (n) {
- case 0: return child->thread.debugreg0;
- case 1: return child->thread.debugreg1;
- case 2: return child->thread.debugreg2;
- case 3: return child->thread.debugreg3;
- case 6: return child->thread.debugreg6;
- case 7: return child->thread.debugreg7;
- }
- return 0;
+ int temp = dr7 >> (DR_CONTROL_SHIFT + bpnum * DR_CONTROL_SIZE);
+
+ *len = (temp & 0xc) | 0x40;
+ *type = (temp & 0x3) | 0x80;
+ return (dr7 >> (bpnum * DR_ENABLE_SIZE)) & 0x3;
}
-static int ptrace_set_debugreg(struct task_struct *child,
- int n, unsigned long data)
+static void ptrace_triggered(struct hw_breakpoint *bp, struct pt_regs *regs)
{
+ struct thread_struct *thread = &(current->thread);
int i;
- if (unlikely(n == 4 || n == 5))
- return -EIO;
+ /* Store in the virtual DR6 register the fact that the breakpoint
+ * was hit so the thread's debugger will see it.
+ */
+ for (i = 0; i < hbkpt_user_max; i++)
+ if (bp->info.address == thread->hbkpt[i]->info.address)
+ break;
- if (n < 4 && unlikely(data >= debugreg_addr_limit(child)))
- return -EIO;
+ thread->dr6 |= (DR_TRAP0 << i);
+}
- switch (n) {
- case 0: child->thread.debugreg0 = data; break;
- case 1: child->thread.debugreg1 = data; break;
- case 2: child->thread.debugreg2 = data; break;
- case 3: child->thread.debugreg3 = data; break;
+/*
+ * Handle ptrace writes to debug register 7.
+ */
+static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data)
+{
+ struct hw_breakpoint *bp;
+ struct thread_struct *thread = &(tsk->thread);
+ int i;
+ int rc = 0;
+ unsigned long old_dr7 = thread->dr7;
- case 6:
- if ((data & ~0xffffffffUL) != 0)
- return -EIO;
- child->thread.debugreg6 = data;
- break;
+ data &= ~DR_CONTROL_RESERVED;
+ /* Loop through all the hardware breakpoints, making the
+ * appropriate changes to each.
+ */
+restore_settings:
+ thread->dr7 = data;
+ for (i = 0; i < HB_NUM; i++) {
+ int enabled;
+ unsigned len, type;
+
+ bp = thread->hbkpt[i];
+ if (!bp)
+ continue;
+
+ enabled = decode_dr7(data, i, &len, &type);
+ if (!enabled) {
+ if (bp->triggered)
+ __unregister_user_hw_breakpoint(i, tsk, bp);
+ continue;
+ }
- case 7:
- /*
- * Sanity-check data. Take one half-byte at once with
- * check = (val >> (16 + 4*i)) & 0xf. It contains the
- * R/Wi and LENi bits; bits 0 and 1 are R/Wi, and bits
- * 2 and 3 are LENi. Given a list of invalid values,
- * we do mask |= 1 << invalid_value, so that
- * (mask >> check) & 1 is a correct test for invalid
- * values.
- *
- * R/Wi contains the type of the breakpoint /
- * watchpoint, LENi contains the length of the watched
- * data in the watchpoint case.
- *
- * The invalid values are:
- * - LENi == 0x10 (undefined), so mask |= 0x0f00. [32-bit]
- * - R/Wi == 0x10 (break on I/O reads or writes), so
- * mask |= 0x4444.
- * - R/Wi == 0x00 && LENi != 0x00, so we have mask |=
- * 0x1110.
- *
- * Finally, mask = 0x0f00 | 0x4444 | 0x1110 == 0x5f54.
- *
- * See the Intel Manual "System Programming Guide",
- * 15.2.4
- *
- * Note that LENi == 0x10 is defined on x86_64 in long
- * mode (i.e. even for 32-bit userspace software, but
- * 64-bit kernel), so the x86_64 mask value is 0x5454.
- * See the AMD manual no. 24593 (AMD64 System Programming)
- */
-#ifdef CONFIG_X86_32
-#define DR7_MASK 0x5f54
-#else
-#define DR7_MASK 0x5554
-#endif
- data &= ~DR_CONTROL_RESERVED;
- for (i = 0; i < 4; i++)
- if ((DR7_MASK >> ((data >> (16 + 4*i)) & 0xf)) & 1)
- return -EIO;
- child->thread.debugreg7 = data;
- if (data)
- set_tsk_thread_flag(child, TIF_DEBUG);
+ if (bp->triggered)
+ rc = __modify_user_hw_breakpoint(i, tsk, bp);
+ else {
+ bp->triggered = ptrace_triggered;
+ bp->info.len = len;
+ bp->info.type = type;
+ rc = __register_user_hw_breakpoint(i, tsk, bp);
+ }
+ if (rc < 0)
+ break;
else
- clear_tsk_thread_flag(child, TIF_DEBUG);
- break;
+ set_tsk_thread_flag(tsk, TIF_DEBUG);
+ }
+ /* If anything above failed, restore the original settings */
+ if (rc < 0) {
+ data = old_dr7;
+ goto restore_settings;
}
+ return rc;
+}
- return 0;
+/*
+ * Handle PTRACE_PEEKUSR calls for the debug register area.
+ */
+unsigned long ptrace_get_debugreg(struct task_struct *tsk, int n)
+{
+ struct thread_struct *thread = &(tsk->thread);
+ unsigned long val = 0;
+
+ mutex_lock(&hw_breakpoint_mutex);
+ if (n < HB_NUM) {
+ if (thread->hbkpt[n])
+ val = thread->hbkpt[n]->info.address;
+ } else if (n == 6) {
+ val = thread->dr6;
+ } else if (n == 7) {
+ val = thread->dr7;
+ }
+ mutex_unlock(&hw_breakpoint_mutex);
+ return val;
+}
+
+/*
+ * Handle PTRACE_POKEUSR calls for the debug register area.
+ */
+int ptrace_set_debugreg(struct task_struct *tsk, int n, unsigned long val)
+{
+ struct thread_struct *thread = &(tsk->thread);
+ int rc = -EIO;
+
+ mutex_lock(&hw_breakpoint_mutex);
+
+ /* There are no DR4 or DR5 registers */
+ if (n == 4 || n == 5)
+ goto ret_path;
+
+ /* Writes to DR6 modify the virtualized value */
+ if (n == 6) {
+ tsk->thread.dr6 = val;
+ rc = 0;
+ goto ret_path;
+ }
+
+ /* Writes to DR0 - DR3 change a breakpoint address */
+ rc = 0;
+ if (n < HB_NUM) {
+ if (!val)
+ goto ret_path;
+ if (thread->hbkpt[n]) {
+ thread->hbkpt[n]->info.address = val;
+ rc = __modify_user_hw_breakpoint(n, tsk,
+ thread->hbkpt[n]);
+ goto ret_path;
+ }
+ thread->hbkpt[n] = kzalloc(sizeof(struct hw_breakpoint),
+ GFP_KERNEL);
+ if (!thread->hbkpt[n]) {
+ rc = -ENOMEM;
+ goto ret_path;
+ } else
+ thread->hbkpt[n]->info.address = val;
+ }
+ /* All that's left is DR7 */
+ if (n == 7)
+ rc = ptrace_write_dr7(tsk, val);
+
+ret_path:
+ mutex_unlock(&hw_breakpoint_mutex);
+ return rc;
}
/*
@@ -871,7 +918,7 @@ long arch_ptrace(struct task_struct *chi
else if (addr >= offsetof(struct user, u_debugreg[0]) &&
addr <= offsetof(struct user, u_debugreg[7])) {
addr -= offsetof(struct user, u_debugreg[0]);
- tmp = ptrace_get_debugreg(child, addr / sizeof(data));
+ tmp = ptrace_get_debugreg(child, addr/sizeof(data));
}
ret = put_user(tmp, datap);
break;
@@ -889,7 +936,7 @@ long arch_ptrace(struct task_struct *chi
addr <= offsetof(struct user, u_debugreg[7])) {
addr -= offsetof(struct user, u_debugreg[0]);
ret = ptrace_set_debugreg(child,
- addr / sizeof(data), data);
+ addr/sizeof(data), data);
}
break;
next prev parent reply other threads:[~2009-03-19 23:51 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <20090319234044.410725944@K.Prasad>
2009-03-19 23:48 ` [Patch 01/11] Introducing generic hardware breakpoint handler interfaces K.Prasad
2009-03-20 14:33 ` Alan Stern
2009-03-20 18:30 ` Ingo Molnar
2009-03-21 17:32 ` K.Prasad
2009-03-20 18:32 ` Ingo Molnar
2009-03-21 17:26 ` K.Prasad
2009-03-21 21:39 ` Alan Stern
2009-03-23 19:03 ` K.Prasad
2009-03-23 19:21 ` Alan Stern
2009-03-23 20:42 ` K.Prasad
2009-03-23 21:20 ` Alan Stern
2009-03-19 23:48 ` [Patch 02/11] x86 architecture implementation of Hardware Breakpoint interfaces K.Prasad
2009-03-19 23:48 ` [Patch 03/11] Modifying generic debug exception to use thread-specific debug registers K.Prasad
2009-03-19 23:49 ` [Patch 04/11] Introduce user-space " K.Prasad
2009-03-19 23:49 ` [Patch 05/11] Use wrapper routines around debug registers in processor related functions K.Prasad
2009-03-19 23:49 ` [Patch 06/11] Use the new wrapper routines to access debug registers in process/thread code K.Prasad
2009-03-19 23:49 ` [Patch 07/11] Modify signal handling code to refrain from re-enabling HW Breakpoints K.Prasad
2009-03-19 23:49 ` K.Prasad [this message]
2009-03-19 23:49 ` [Patch 09/11] Cleanup HW Breakpoint registers before kexec K.Prasad
2009-03-19 23:50 ` [Patch 10/11] Sample HW breakpoint over kernel data address K.Prasad
2009-03-19 23:50 ` [Patch 11/11] ftrace plugin for kernel symbol tracing using HW Breakpoint interfaces - v2 K.Prasad
2009-03-20 9:04 ` Frederic Weisbecker
2009-03-21 16:24 ` K.Prasad
2009-03-21 16:39 ` Steven Rostedt
2009-03-23 19:08 ` K.Prasad
[not found] <20090407063058.301701787@prasadkr_t60p.in.ibm.com>
2009-04-07 6:36 ` [Patch 08/11] Modify Ptrace routines to access breakpoint registers K.Prasad
[not found] <20090324152028.754123712@K.Prasad>
2009-03-24 15:26 ` K.Prasad
[not found] <20090307045120.039324630@linux.vnet.ibm.com>
2009-03-07 5:07 ` prasad
[not found] <20090305043440.189041194@linux.vnet.ibm.com>
2009-03-05 4:40 ` [patch " prasad
2009-03-10 14:40 ` Ingo Molnar
2009-03-10 15:54 ` Alan Stern
2009-03-12 3:14 ` Roland McGrath
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=20090319234940.GI10517@in.ibm.com \
--to=prasad@linux.vnet.ibm.com \
--cc=akpm@linux-foundation.org \
--cc=benh@au1.ibm.com \
--cc=fweisbec@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=maneesh@in.ibm.com \
--cc=mingo@elte.hu \
--cc=roland@redhat.com \
--cc=rostedt@goodmis.org \
--cc=stern@rowland.harvard.edu \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox