logo       

Re: [PATCH] i386 Hardware watchpoints using kgdb-2-2.6.14.tar.gz: msg#00155

linux.kernel.debugging.kgdb.bugs

Subject: Re: [PATCH] i386 Hardware watchpoints using kgdb-2-2.6.14.tar.gz

Hello again, at long last,

So much for having patches later that day. When
you've got a bug in your code and need to use the debugger,
but the thing you're debugging *is* the debugger, life
gets unpleasant :(

Attached is a slightly modified version of the debug
register allocation patch from IBM that Tom posted.
It is meant to be applied to a kernel.org 2.6.14.3
kernel which has first had the kgdb patches from
kgdb-2-2.6.14.tar.gz, though I don't think the two
patch sets collide.

Aside from the minor changes for the backport to
2.6.14.3, I also moved sync_dr() into debugreg.c
from the associated kwatch patch. It seemed
necessary for the subsequent hardware watchpoint
patch to be able to sync debug registers across
cpus.

Comments welcome. The dependent hardware breakpoint
patch follows in the next message.

--Dennis

Dennis W. Tokarski wrote:


Amit Kale wrote:

Thanks, Dennis. Let's stick to "debug register allocation" as
suggested by you.

kgdb_set_hw_break definition contains only one function parameter,
which is address. Current i386.patch includes a kgdb_set_hw_break
function which always sets a data watchpoints. That's the reason you
couldn't get execution watchpoints as you have mentioned in an earlier
email.


Right, I discovered that. It's called in response to a Z1 pacaket and
was intended, I think, to set a code breakpoint--at least it puts type=1
and length=1 into a breakinfo entry. But then it copies those values
verbatim into dr7, which is wrong as the register fields are zero-based.
So you get a write-data watchpoint instead.


kgdb_ops->set_hw_breakpoint has three parameters, which is the correct
way of doing it.


Also right, which is why I chose to use it in the first place. Fewer
code changes that way.

I expect to have a completed patch to post later today.

--Dennis


-Amit

On Thursday 12 Jan 2006 8:52 am, Dennis W. Tokarski wrote:

Hey there Amit,

Amit Kale wrote:

On Saturday 07 Jan 2006 3:46 am, Dennis W. Tokarski wrote:


[snip]


2.6.14.3 kernel that I'm using. I did not include the second patch
which adds kwatch-points--it's a neat feature but not directly
related to kgdb.

Do you want a copy of the resulting debug register allocation
patch?


Not sure about this. When soon is kprobes patch expected to make it to
the kernel? If not soon, there isn't any point in using that tmechanism
as we can survive with some minimal code for that.


Is that a change of position? I incorporated the patch (which I should
probably have called a "debug register allocation" patch rather than
a "kprobe" patch) into my work mainly because of your earlier reply
to Tom's posting on 12/16/05:

=============

Amit Kale wrote:
> That's a good scheme. We definitely want to use it when enabling
> watchpoints in KGDB. Even if it gets accepted in the kernel in a
> different form or some other scheme gets accepted, we can quickly
change
> our code base accordingly.
>
> -Amit

=============

I would advocate keeping it. Certainly something like it becomes
necessary
as soon as anything other than kgdb wants to use the hardware debug
registers in the kernel. For anyone debugging a complex problem in user
space and the kernel concurently (and I've been there many times) it's
necessary as well to keep user space gdb and kgdb from conflicting.

The patch is quite minimal, being largely confined to its own header
file
and one arch-specific source file. It touches the kernel elsewhere only
where it's unavoidable, and even then only a couple of lines here and
there.

I don't see how it could be reduced without breaking something.



[snip]


Is one of those mechanisms deprecated? I chose for the moment to
use the call through kgdb_ops.


kgdb_ops function pointers are deprecated. We should be using weak
mechanisms only. set_breakpoint should also be changed this way.

Milind, can you do the set_breakpoint change?


OK, I'll do it that way. I've captured the essentials of Milands
patch and will follow that example for the hw_break/watch stuff.


OK, I lied, there is actually a third issue. You can't, in
kernel/kgdb.c, just pass the ASCII type code from the Z packet


[snip]


I would prefer changing the type of second parameter of
set_hw_breakpoint
to "const char bptype". gdb remote protocol defines the values
bptype can
take ('0-4'). Hence they are stable, besides they allow flexibility
wrt.
achitecture.
-Amit


Noted. I'll do that.

--Dennis


--Dennis

Tom Rini wrote:

On Fri, Dec 16, 2005 at 10:39:42AM +0530, Amit Kale wrote:

Dennis,

That would be splendid. When a watchpoint is required, no other
feature
can do the job!


Note that to do this correctly, you have to be aware of other
potential
users. Prasanna Panchamukhi of the kprobes project forwarded me some
code that would do this and I'll send that along to the list
momentarily.


-Amit

On Friday 16 Dec 2005 12:41 am, Dennis W. Tokarski wrote:

Hi,

Amit Kale wrote:

On Thursday 15 Dec 2005 8:40 pm, Tom Rini wrote:

On Fri, Dec 09, 2005 at 12:42:39PM +0530, Amit Kale wrote:

On Thursday 08 Dec 2005 11:13 pm, Dennis W. Tokarski wrote:


[snip]


I'm fairly certain we don't support watchpoints, and I don't
_think_ we ever did on i386 (we may have on ia64 at some
point). If
the packet isn't [zZ][01] we relpy back to GDB that it's
unsupported.


True. But the first problem was gdb wasn't sending out Z packets
anyway. I have a temporary hack to fix that, but gkdb still doesn't
set an execution break point with Z1. It seems instead to set a
data
access watchpoint. As I mentioned before, I was able to manually
send Z packets using the maintenance command and got the same
result.

So, kgdb isn't setting the watchpoint up even when it gets the
right packets. btw, I'm working exclusively on i386, so that's
all I can speak to.

Anyway, what I propose to do is look back at an earlier kgdb
which did to all those things correctly--I've got a 2.4.18
kernel here set up that way--and bring the code forward into
the current kgdb for i386. As part of that I'll handle the
additional Z packets.

And there's still the problem with gdb itself, and why they
broke Z packets, but I'll take that up on the gdb list.


We didn't have support for official version of gdb watchpoints,
though one could send "y" packets using "maintenance packet"
commands. It worked. That was a couple of years ago, I believe. I
can't recall when we removed support for y packets. Present code
doesn't support it for sure.

-Amit


I was certainly working with the macros back in the 2.4.18 days,
but this is the first time I've used kdgb in 2.6. A lot has
changed, not just with kgdb.

--Dennis


-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep
through log
files for problems? Stop! Download the new AJAX search engine
that
makes searching your log files as easy as surfing the web.
DOWNLOAD
SPLUNK! http://ads.osdn.com/?ad_id=7637&alloc_id=16865&op=click
_______________________________________________
Kgdb-bugreport mailing list
Kgdb-bugreport@xxxxxxxxxxxxxxxxxxxxx
https://lists.sourceforge.net/lists/listinfo/kgdb-bugreport


-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep
through log
files for problems? Stop! Download the new AJAX search engine that
makes searching your log files as easy as surfing the web.
DOWNLOAD
SPLUNK! http://ads.osdn.com/?ad_id=7637&alloc_id=16865&op=click
_______________________________________________
Kgdb-bugreport mailing list
Kgdb-bugreport@xxxxxxxxxxxxxxxxxxxxx
https://lists.sourceforge.net/lists/listinfo/kgdb-bugreport


-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep through log
files for problems? Stop! Download the new AJAX search engine that
makes searching your log files as easy as surfing the web. DOWNLOAD
SPLUNK! http://ads.osdn.com/?ad_id=7637&alloc_id=16865&op=click
_______________________________________________
Kgdb-bugreport mailing list
Kgdb-bugreport@xxxxxxxxxxxxxxxxxxxxx
https://lists.sourceforge.net/lists/listinfo/kgdb-bugreport



-------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc. Do you grep through log
files for problems? Stop! Download the new AJAX search engine that
makes
searching your log files as easy as surfing the web. DOWNLOAD SPLUNK!
http://ads.osdn.com/?ad_id=7637&alloc_id=16865&op=click
_______________________________________________
Kgdb-bugreport mailing list
Kgdb-bugreport@xxxxxxxxxxxxxxxxxxxxx
https://lists.sourceforge.net/lists/listinfo/kgdb-bugreport



!DSPAM:43c5f2da318441558920709!


Index: arch/i386/Kconfig.debug
===================================================================
RCS file:
/usr/src/kernel-devel/../cvs-repository/linux-2.6.14.3-vanilla/arch/i386/Kconfig.debug,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.1
diff -u -r1.1.1.1 -r1.1.1.1.2.1
--- arch/i386/Kconfig.debug 30 Nov 2005 21:36:42 -0000 1.1.1.1
+++ arch/i386/Kconfig.debug 6 Jan 2006 20:03:09 -0000 1.1.1.1.2.1
@@ -32,6 +32,14 @@
for kernel debugging, non-intrusive instrumentation and testing.
If in doubt, say "N".

+config DEBUGREG
+ bool "Global Debug Registers"
+ depends on DEBUG_KERNEL
+ default off
+ help
+ Global debug register allocation mechanism is useful for debuggers
+ IOW, Kgdb, Kdb, Kernel Watchpoint probes. If in doubt say "N"
+
config DEBUG_STACK_USAGE
bool "Stack utilization instrumentation"
depends on DEBUG_KERNEL
Index: arch/i386/kernel/Makefile
===================================================================
RCS file:
/usr/src/kernel-devel/../cvs-repository/linux-2.6.14.3-vanilla/arch/i386/kernel/Makefile,v
retrieving revision 1.1.1.1.2.1
retrieving revision 1.1.1.1.2.2
diff -u -r1.1.1.1.2.1 -r1.1.1.1.2.2
--- arch/i386/kernel/Makefile 9 Dec 2005 22:36:18 -0000 1.1.1.1.2.1
+++ arch/i386/kernel/Makefile 6 Jan 2006 20:03:29 -0000 1.1.1.1.2.2
@@ -34,6 +34,7 @@
obj-$(CONFIG_HPET_TIMER) += time_hpet.o
obj-$(CONFIG_EFI) += efi.o efi_stub.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
+obj-$(CONFIG_DEBUGREG) += debugreg.o
obj-$(CONFIG_KGDB) += kgdb.o kgdb-jmp.o

EXTRA_AFLAGS := -traditional
Index: arch/i386/kernel/debugreg.c
===================================================================
RCS file: arch/i386/kernel/debugreg.c
diff -N arch/i386/kernel/debugreg.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ arch/i386/kernel/debugreg.c 30 Jan 2006 01:56:25 -0000 1.1.2.2
@@ -0,0 +1,307 @@
+/*
+ * Debug register
+ * arch/i386/kernel/debugreg.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) IBM Corporation, 2002, 2004
+ *
+ * 2002-Oct Created by Vamsi Krishna S <vamsi_krishna@xxxxxxxxxx> and
+ * Bharata Rao <bharata@xxxxxxxxxx> to provide debug register
+ * allocation mechanism.
+ * 2004-Oct Updated by Prasanna S Panchamukhi <prasanna@xxxxxxxxxx> with
+ * idr_allocations mechanism as suggested by Andi Kleen.
+ */
+/*
+ * This provides a debug register allocation mechanism, to be
+ * used by all debuggers, which need debug registers.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/idr.h>
+#include <asm/system.h>
+#include <asm/debugreg.h>
+
+struct debugreg dr_list[DR_MAX];
+unsigned long dr7_global_mask = 0;
+static spinlock_t dr_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_IDR(debugreg_idr);
+static DECLARE_MUTEX(debugreg_idr_mutex);
+static spinlock_t debugreg_idr_lock = SPIN_LOCK_UNLOCKED;
+
+static unsigned long dr7_global_bits[] = {
+ DR7_DR0_BITS, DR7_DR1_BITS, DR7_DR2_BITS, DR7_DR3_BITS
+};
+
+
+struct dr_info {
+ int debugreg;
+ unsigned long addr;
+ int type;
+};
+
+static inline void write_smp_dr(void *info)
+{
+ struct dr_info *dr = (struct dr_info *)info;
+
+ if (cpu_has_de && dr->type == DR_TYPE_IO)
+ set_in_cr4(X86_CR4_DE);
+ write_dr(dr->debugreg, dr->addr);
+}
+
+static inline void set_dr7_global_mask(int regnum)
+{
+ if (DR_IS_ADDR(regnum))
+ dr7_global_mask |= dr7_global_bits[regnum];
+}
+
+static inline void clear_dr7_global_mask(int regnum)
+{
+ if (DR_IS_ADDR(regnum))
+ dr7_global_mask &= ~dr7_global_bits[regnum];
+}
+
+/*
+ * See if specific debug register is free.
+ */
+static int specific_debugreg(unsigned int regnum)
+{
+ int r, n;
+
+ if (regnum >= DR_MAX)
+ return -EINVAL;
+
+ down(&debugreg_idr_mutex);
+ r = idr_pre_get(&debugreg_idr, GFP_KERNEL);
+ up(&debugreg_idr_mutex);
+ if (!r)
+ return -ENOMEM;
+
+ spin_lock(&debugreg_idr_lock);
+
+ if (idr_find(&debugreg_idr, regnum)) {
+ r = -EBUSY;
+ goto out;
+ }
+
+ r = idr_get_new_above(&debugreg_idr, specific_debugreg, regnum, &n);
+ if (r)
+ goto out;
+
+ if (n != regnum) {
+ idr_remove(&debugreg_idr, n);
+ r = -EBUSY;
+ goto out;
+ }
+
+ out:
+ spin_unlock(&debugreg_idr_lock);
+ return r;
+}
+
+static int next_free_debugreg(unsigned int *regnum)
+{
+ int r;
+ unsigned int n;
+
+ down(&debugreg_idr_mutex);
+ r = idr_pre_get(&debugreg_idr, GFP_KERNEL);
+ up(&debugreg_idr_mutex);
+ if (!r)
+ return -ENOMEM;
+
+ spin_lock(&debugreg_idr_lock);
+
+ r = idr_get_new(&debugreg_idr, next_free_debugreg, &n);
+ if (r)
+ goto out;
+
+ if (n >= DR_MAX) {
+ idr_remove(&debugreg_idr, n);
+ r = -ENOSPC;
+ goto out;
+ }
+
+ *regnum = n;
+ out:
+ spin_unlock(&debugreg_idr_lock);
+ return r;
+}
+
+static void free_debugreg(int regnum)
+{
+ spin_lock(&debugreg_idr_lock);
+ idr_remove(&debugreg_idr, regnum);
+ spin_unlock(&debugreg_idr_lock);
+}
+
+static int get_dr(int regnum, int flag)
+{
+ int ret;
+
+ ret = specific_debugreg(regnum);
+
+ if ((flag == DR_ALLOC_GLOBAL) && (ret >= 0)) {
+ dr_list[regnum].flag = DR_GLOBAL;
+ set_dr7_global_mask(regnum);
+ return regnum;
+ } else if (dr_list[regnum].flag != DR_GLOBAL) {
+ dr_list[regnum].use_count++;
+ dr_list[regnum].flag = DR_LOCAL;
+ return regnum;
+ }
+ return ret;
+}
+
+static int get_any_dr(int flag)
+{
+ int i, ret = 0;
+
+ if (flag == DR_ALLOC_LOCAL) {
+ for (i = 0; i < DR_MAX; i++) {
+ if ((idr_find(&debugreg_idr, i)) && (dr_list[i].flag ==
DR_LOCAL)) {
+ dr_list[i].use_count++;
+ return i;
+ }
+ }
+ if ((ret = next_free_debugreg(&i)) >= 0) {
+ dr_list[i].flag = DR_LOCAL;
+ dr_list[i].use_count = 1;
+ return i;
+ }
+ } else {
+ if ((ret = next_free_debugreg(&i)) >= 0) {
+ dr_list[i].flag = DR_GLOBAL;
+ set_dr7_global_mask(i);
+ return i;
+ }
+ }
+ return ret;
+}
+
+static inline void dr_free_local(int regnum)
+{
+ if (!(--dr_list[regnum].use_count)) {
+ free_debugreg(regnum);
+ dr_list[regnum].flag = DR_UNUSED;
+ }
+}
+
+static inline void dr_free_global(int regnum)
+{
+ free_debugreg(regnum);
+ dr_list[regnum].flag = DR_UNUSED;
+ dr_list[regnum].use_count = 0;
+ clear_dr7_global_mask(regnum);
+}
+
+int dr_alloc(int regnum, int flag)
+{
+ int ret = 0;
+
+ spin_lock(&dr_lock);
+ if (regnum == DR_ANY)
+ ret = get_any_dr(flag);
+ else if (regnum >= DR_MAX)
+ ret = -1;
+ else
+ ret = get_dr(regnum, flag);
+ spin_unlock(&dr_lock);
+ if (ret < 0)
+ printk("dr_alloc:Cannot allocate debug register %d\n", regnum);
+ return ret;
+}
+
+int dr_free(int regnum)
+{
+ spin_lock(&dr_lock);
+ if (regnum >= DR_MAX || (!idr_find(&debugreg_idr, regnum))) {
+ spin_unlock(&dr_lock);
+ printk("dr_free:Cannot free debug register %d\n", regnum);
+ return -1;
+ }
+ if (dr_list[regnum].flag == DR_LOCAL)
+ dr_free_local(regnum);
+ else
+ dr_free_global(regnum);
+ spin_unlock(&dr_lock);
+ return 0;
+}
+
+void dr_inc_use_count(unsigned long debugreg)
+{
+ int i;
+
+ spin_lock(&dr_lock);
+ for (i = 0; i < DR_MAX; i++) {
+ if ((idr_find(&debugreg_idr, i)) && (DR_IS_LOCAL(debugreg, i)))
+ dr_list[i].use_count++;
+ }
+ spin_unlock(&dr_lock);
+}
+
+void dr_dec_use_count(unsigned long debugreg)
+{
+ int i;
+
+ spin_lock(&dr_lock);
+ for (i = 0; i < DR_MAX; i++) {
+ if ((idr_find(&debugreg_idr, i)) && (DR_IS_LOCAL(debugreg, i)))
+ dr_free_local(i);
+ }
+ spin_unlock(&dr_lock);
+}
+
+/*
+ * This routine decides if the ptrace request is for enabling or disabling
+ * a debug reg, and accordingly calls dr_alloc() or dr_free().
+ *
+ * gdb uses ptrace to write to debug registers. It assumes that writing to
+ * debug register always succeds and it doesn't check the return value of
+ * ptrace. Now with this new global debug register allocation/freeing,
+ * ptrace request for a local debug register can fail, if the required debug
+ * register is already globally allocated. Since gdb fails to notice this
+ * failure, it sometimes tries to free a debug register, which is not
+ * allocated for it.
+ */
+int enable_debugreg(unsigned long old_dr7, unsigned long new_dr7)
+{
+ int i, dr_shift = 1UL;
+ for (i = 0; i < DR_MAX; i++, dr_shift <<= 2) {
+ if ((old_dr7 ^ new_dr7) & dr_shift) {
+ if (new_dr7 & dr_shift)
+ dr_alloc(i, DR_ALLOC_LOCAL);
+ else
+ dr_free(i);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/* Update the debug register on all CPUs */
+void sync_dr(int debugreg, unsigned long addr, int type)
+{
+ struct dr_info dr;
+ dr.debugreg = debugreg;
+ dr.addr = addr;
+ dr.type = type;
+ smp_call_function(write_smp_dr, &dr, 0, 0);
+}
+
+EXPORT_SYMBOL(dr_alloc);
+EXPORT_SYMBOL(dr_free);
Index: arch/i386/kernel/process.c
===================================================================
RCS file:
/usr/src/kernel-devel/../cvs-repository/linux-2.6.14.3-vanilla/arch/i386/kernel/process.c,v
retrieving revision 1.1.1.1.2.1
retrieving revision 1.1.1.1.2.2
diff -u -r1.1.1.1.2.1 -r1.1.1.1.2.2
--- arch/i386/kernel/process.c 9 Dec 2005 22:36:18 -0000 1.1.1.1.2.1
+++ arch/i386/kernel/process.c 6 Jan 2006 20:03:29 -0000 1.1.1.1.2.2
@@ -51,6 +51,7 @@
#ifdef CONFIG_MATH_EMULATION
#include <asm/math_emu.h>
#endif
+#include <asm/debugreg.h>

#include <linux/err.h>

@@ -413,6 +414,8 @@
tss->io_bitmap_base = INVALID_IO_BITMAP_OFFSET;
put_cpu();
}
+ if (tsk->thread.debugreg[7])
+ dr_dec_use_count(tsk->thread.debugreg[7]);
}

void flush_thread(void)
@@ -426,6 +429,8 @@
*/
kprobe_flush_task(tsk);

+ if (tsk->thread.debugreg[7])
+ dr_dec_use_count(tsk->thread.debugreg[7]);
memset(tsk->thread.debugreg, 0, sizeof(unsigned long)*8);
memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array));
/*
@@ -527,6 +532,9 @@
desc->b = LDT_entry_b(&info);
}

+ if (tsk->thread.debugreg[7])
+ dr_inc_use_count(tsk->thread.debugreg[7]);
+
err = 0;
out:
if (err && p->thread.io_bitmap_ptr) {
@@ -690,6 +698,7 @@
*next = &next_p->thread;
int cpu = smp_processor_id();
struct tss_struct *tss = &per_cpu(init_tss, cpu);
+ unsigned long next_dr7 = next->debugreg[7];

/* never put a printk in __switch_to... printk() calls wake_up*()
indirectly */

@@ -738,14 +747,22 @@
/*
* Now maybe reload the debug registers
*/
- if (unlikely(next->debugreg[7])) {
- set_debugreg(next->debugreg[0], 0);
- set_debugreg(next->debugreg[1], 1);
- set_debugreg(next->debugreg[2], 2);
- set_debugreg(next->debugreg[3], 3);
+ /*
+ * Don't reload global debug registers. Don't touch the global debug
+ * register settings in dr7.
+ */
+ if (unlikely(next_dr7)) {
+ if (prev->debugreg[0] != next->debugreg[0])
+ set_debugreg(current->thread.debugreg[0], 0);
+ if (prev->debugreg[1] != next->debugreg[1])
+ set_debugreg(current->thread.debugreg[1], 1);
+ if (prev->debugreg[2] != next->debugreg[2])
+ set_debugreg(current->thread.debugreg[2], 2);
+ if (prev->debugreg[3] != next->debugreg[3])
+ set_debugreg(current->thread.debugreg[3], 3);
/* no 4 and 5 */
- set_debugreg(next->debugreg[6], 6);
- set_debugreg(next->debugreg[7], 7);
+ set_debugreg(current->thread.debugreg[6], 6);
+ load_process_dr7(next_dr7);
}

if (unlikely(prev->io_bitmap_ptr || next->io_bitmap_ptr))
Index: arch/i386/kernel/ptrace.c
===================================================================
RCS file:
/usr/src/kernel-devel/../cvs-repository/linux-2.6.14.3-vanilla/arch/i386/kernel/ptrace.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.1
diff -u -r1.1.1.1 -r1.1.1.1.2.1
--- arch/i386/kernel/ptrace.c 30 Nov 2005 21:36:42 -0000 1.1.1.1
+++ arch/i386/kernel/ptrace.c 6 Jan 2006 20:03:29 -0000 1.1.1.1.2.1
@@ -506,6 +506,11 @@

addr -= (long) &dummy->u_debugreg;
addr = addr >> 2;
+
+ if (addr == 7 &&
(enable_debugreg(child->thread.debugreg[addr], data)) < 0) {
+ ret = -EBUSY;
+ break;
+ }
child->thread.debugreg[addr] = data;
ret = 0;
}
Index: arch/i386/kernel/signal.c
===================================================================
RCS file:
/usr/src/kernel-devel/../cvs-repository/linux-2.6.14.3-vanilla/arch/i386/kernel/signal.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.1
diff -u -r1.1.1.1 -r1.1.1.1.2.1
--- arch/i386/kernel/signal.c 30 Nov 2005 21:36:42 -0000 1.1.1.1
+++ arch/i386/kernel/signal.c 6 Jan 2006 20:03:29 -0000 1.1.1.1.2.1
@@ -25,6 +25,7 @@
#include <asm/ucontext.h>
#include <asm/uaccess.h>
#include <asm/i387.h>
+#include <asm/debugreg.h>
#include "sigframe.h"

#define DEBUG_SIG 0
@@ -629,7 +630,7 @@
* inside the kernel.
*/
if (unlikely(current->thread.debugreg[7])) {
- set_debugreg(current->thread.debugreg[7], 7);
+ load_process_dr7(current->thread.debugreg[7]);
}

/* Whee! Actually deliver the signal. */
Index: arch/i386/kernel/traps.c
===================================================================
RCS file:
/usr/src/kernel-devel/../cvs-repository/linux-2.6.14.3-vanilla/arch/i386/kernel/traps.c,v
retrieving revision 1.1.1.1.2.1
retrieving revision 1.1.1.1.2.2
diff -u -r1.1.1.1.2.1 -r1.1.1.1.2.2
--- arch/i386/kernel/traps.c 9 Dec 2005 22:36:18 -0000 1.1.1.1.2.1
+++ arch/i386/kernel/traps.c 6 Jan 2006 20:03:29 -0000 1.1.1.1.2.2
@@ -758,7 +758,7 @@
* the signal is delivered.
*/
clear_dr7:
- set_debugreg(0, 7);
+ load_process_dr7(0);
notify_die(DIE_DEBUG, "debug2", regs, condition, error_code, SIGTRAP);
return;

Index: include/asm-i386/debugreg.h
===================================================================
RCS file:
/usr/src/kernel-devel/../cvs-repository/linux-2.6.14.3-vanilla/include/asm-i386/debugreg.h,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.2
diff -u -r1.1.1.1 -r1.1.1.1.2.2
--- include/asm-i386/debugreg.h 30 Nov 2005 21:35:59 -0000 1.1.1.1
+++ include/asm-i386/debugreg.h 30 Jan 2006 02:00:12 -0000 1.1.1.1.2.2
@@ -61,4 +61,194 @@
#define DR_LOCAL_SLOWDOWN (0x100) /* Local slow the pipeline */
#define DR_GLOBAL_SLOWDOWN (0x200) /* Global slow the pipeline */

+struct debugreg {
+ unsigned long flag;
+ unsigned long use_count;
+};
+
+/* debugreg flags */
+#define DR_UNUSED 0
+#define DR_LOCAL 1
+#define DR_GLOBAL 2
+
+#define DR_MAX 4
+#define DR_ANY DR_MAX + 1
+
+/* global or local allocation requests */
+#define DR_ALLOC_GLOBAL 0
+#define DR_ALLOC_LOCAL 1
+
+#define DR7_RW_SET(dr7, regnum, rw) do { \
+ (dr7) &= ~(0x3 << (16 + (4 * (regnum)))); \
+ (dr7) |= (((rw) & 0x3) << (16 + (4 * (regnum)))); \
+ } while (0)
+
+#define DR7_RW_VAL(dr7, regnum) \
+ (((dr7) >> (16 + (4 * (regnum)))) & 0x3)
+
+#define DR7_LEN_SET(dr7, regnum, len) do { \
+ (dr7) &= ~(0x3 << (18 + (4 * (regnum)))); \
+ (dr7) |= (((len-1) & 0x3) << (18 + (4 * (regnum)))); \
+ } while (0)
+
+#define DR7_LEN_VAL(dr7, regnum) \
+ (((dr7) >> (18 + (4 * (regnum)))) & 0x3)
+
+#define DR7_L0(dr0) (((dr0))&0x1)
+#define DR7_L1(dr1) (((dr1)>>2)&0x1)
+#define DR7_L2(dr2) (((dr2)>>4)&0x1)
+#define DR7_L3(dr3) (((dr3)>>6)&0x1)
+
+/* Check if local breakpoint is enabled */
+#define DR_IS_LOCAL(dr7, regnum) ((dr7) & (1UL << (regnum <<1)))
+
+/* Set the rw, len and global flag in dr7 for a debug register */
+#define SET_DR7(dr7, regnum, access, len) do { \
+ DR7_RW_SET(dr7, regnum, access); \
+ DR7_LEN_SET(dr7, regnum, len); \
+ dr7 |= (2UL << regnum*2); \
+ } while (0)
+
+/* Disable a debug register by clearing the global/local flag in dr7 */
+#define RESET_DR7(dr7, regnum) dr7 &= ~(3UL << regnum*2)
+
+#define DR_IS_ADDR(regnum) (0xf & (1 << (regnum)))
+
+#define DR7_DR0_BITS 0x000F0003
+#define DR7_DR1_BITS 0x00F0000C
+#define DR7_DR2_BITS 0x0F000030
+#define DR7_DR3_BITS 0xF00000C0
+
+#define DR_TRAP_MASK 0xF
+
+#define DR_TYPE_EXECUTE 0x0
+#define DR_TYPE_WRITE 0x1
+#define DR_TYPE_IO 0x2
+#define DR_TYPE_RW 0x3
+
+#define get_dr(regnum, val) get_debugreg(val, regnum)
+
+static inline unsigned long read_dr(int regnum)
+{
+ unsigned long val = 0;
+ switch (regnum) {
+ case 0:
+ get_dr(0, val);
+ break;
+ case 1:
+ get_dr(1, val);
+ break;
+ case 2:
+ get_dr(2, val);
+ break;
+ case 3:
+ get_dr(3, val);
+ break;
+ case 6:
+ get_dr(6, val);
+ break;
+ case 7:
+ get_dr(7, val);
+ break;
+ }
+ return val;
+}
+
+#undef get_dr
+
+static inline void write_dr(int regnum, unsigned long val)
+{
+ switch (regnum) {
+ case 0:
+ set_debugreg(val, 0);
+ break;
+ case 1:
+ set_debugreg(val, 1);
+ break;
+ case 2:
+ set_debugreg(val, 2);
+ break;
+ case 3:
+ set_debugreg(val, 3);
+ break;
+ case 7:
+ set_debugreg(val, 7);
+ break;
+ }
+}
+
+#ifdef CONFIG_DEBUGREG
+/*
+ * Given the debug status register, returns the debug register number
+ * which caused the debug trap.
+ */
+static inline int dr_trap(unsigned int condition)
+{
+ int i, reg_shift = 1UL;
+ for (i = 0; i < DR_MAX; i++, reg_shift <<= 1)
+ if ((condition & reg_shift))
+ return i;
+ return -1;
+}
+
+/*
+ * Given the debug status register, returns the address due to which
+ * the debug trap occured.
+ */
+static inline unsigned long dr_trap_addr(unsigned int condition)
+{
+ int regnum = dr_trap(condition);
+
+ if (regnum == -1)
+ return -1;
+ return read_dr(regnum);
+}
+
+/*
+ * Given the debug status register, returns the type of debug trap:
+ * execute, read/write, write or io.
+ */
+static inline int dr_trap_type(unsigned int condition)
+{
+ int regnum = dr_trap(condition);
+
+ if (regnum == -1)
+ return -1;
+ return DR7_RW_VAL(read_dr(7), regnum);
+}
+
+/* Function declarations */
+
+extern int dr_alloc(int regnum, int flag);
+extern int dr_free(int regnum);
+extern void dr_inc_use_count(unsigned long mask);
+extern void dr_dec_use_count(unsigned long mask);
+extern struct debugreg dr_list[DR_MAX];
+extern unsigned long dr7_global_mask;
+extern int enable_debugreg(unsigned long old_dr7, unsigned long new_dr7);
+extern void sync_dr(int debugreg, unsigned long addr, int type);
+
+static inline void load_process_dr7(unsigned long curr_dr7)
+{
+ write_dr(7, (read_dr(7) & dr7_global_mask) | curr_dr7);
+}
+#else
+static inline int enable_debugreg(unsigned long old_dr7, unsigned long new_dr7)
+{
+ return 0;
+}
+static inline void load_process_dr7(unsigned long curr_dr7)
+{
+ write_dr(7, curr_dr7);
+}
+
+static void dr_inc_use_count(unsigned long mask)
+{
+}
+
+static void dr_dec_use_count(unsigned long mask)
+{
+}
+
+#endif /* CONFIG_DEBUGREG */
#endif

Attachment: signature.asc
Description: OpenPGP digital signature

<Prev in Thread] Current Thread [Next in Thread>
Google Custom Search

News | FAQ | advertise