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@linux.vnet.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: Tue, 24 Mar 2009 20:56:59 +0530 [thread overview]
Message-ID: <20090324152659.GI17918@in.ibm.com> (raw)
In-Reply-To: 20090324152028.754123712@K.Prasad
[-- Attachment #1: 8 --]
[-- Type: text/plain, Size: 8696 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/arch/x86/kernel/ptrace.c
===================================================================
--- linux-2.6-tip.orig/arch/x86/kernel/ptrace.c
+++ linux-2.6-tip/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; hbp_user_refcount[i]; i++)
+ if (bp->info.address == thread->hbp[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.
+ */
+ thread->dr7 = data;
+ for (i = 0; i < HB_NUM; i++) {
+ int enabled;
+ unsigned len, type;
+
+ bp = thread->hbp[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.
+ * The original settings will take effect the next time the thread
+ * is scheduled
+ */
+ if (rc < 0)
+ data = old_dr7;
+ 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->hbp[n])
+ val = thread->hbp[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->hbp[n]) {
+ thread->hbp[n]->info.address = val;
+ rc = __modify_user_hw_breakpoint(n, tsk,
+ thread->hbp[n]);
+ goto ret_path;
+ }
+ thread->hbp[n] = kzalloc(sizeof(struct hw_breakpoint),
+ GFP_KERNEL);
+ if (!thread->hbp[n]) {
+ rc = -ENOMEM;
+ goto ret_path;
+ } else
+ thread->hbp[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-24 15:28 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <20090324152028.754123712@K.Prasad>
2009-03-24 15:24 ` [Patch 01/11] Introducing generic hardware breakpoint handler interfaces K.Prasad
2009-03-24 15:25 ` [Patch 02/11] x86 architecture implementation of Hardware Breakpoint interfaces K.Prasad
2009-03-24 15:25 ` [Patch 03/11] Modifying generic debug exception to use thread-specific debug registers K.Prasad
2009-03-24 15:26 ` [Patch 04/11] Introduce user-space " K.Prasad
2009-03-24 15:26 ` [Patch 05/11] Use wrapper routines around debug registers in processor related functions K.Prasad
2009-03-24 15:26 ` [Patch 06/11] Use the new wrapper routines to access debug registers in process/thread code K.Prasad
2009-03-24 15:26 ` [Patch 07/11] Modify signal handling code to refrain from re-enabling HW Breakpoints K.Prasad
2009-03-24 15:26 ` K.Prasad [this message]
2009-03-24 15:27 ` [Patch 09/11] Cleanup HW Breakpoint registers before kexec K.Prasad
2009-03-24 15:27 ` [Patch 10/11] Sample HW breakpoint over kernel data address K.Prasad
2009-03-24 15:28 ` [Patch 11/11] ftrace plugin for kernel symbol tracing using HW Breakpoint interfaces - v2 K.Prasad
2009-03-22 9:35 ` Pavel Machek
2009-03-25 3:03 ` Steven Rostedt
2009-03-25 3:30 ` K.Prasad
2009-03-25 3:48 ` Steven Rostedt
2009-03-25 6:35 ` [Patch 11/11] ftrace plugin for kernel symbol tracing using HW Breakpoint interfaces - v3 K.Prasad
2009-03-25 13:24 ` Steven Rostedt
[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] <20090319234044.410725944@K.Prasad>
2009-03-19 23:49 ` 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=20090324152659.GI17918@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@linux.vnet.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 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.