Mirror of the gdb mailing list
 help / color / mirror / Atom feed
* how are debug registers supposed to work?
@ 2003-08-29  0:41 Ben Johnson
  2003-09-03 22:55 ` Andrew Cagney
  2003-09-17  0:30 ` Ben Johnson
  0 siblings, 2 replies; 4+ messages in thread
From: Ben Johnson @ 2003-08-29  0:41 UTC (permalink / raw)
  To: gdb

Hi.

I hope this is an acceptable topic for this list.  I'm not sure where to
send this question.

I am experimenting with debugging an old (2.0.14?), hacked Linux kernel.
I'm using an old module written by someone at HP that enables remote
debugging of the the kernel over a serial line.  This works very well
for the most part.  the only bad thing is that data watchpoints don't
work.  This bug I'm trying to kill would be relatively easy to find if I
could make watchpoints work.

After researching a little I've ended up thinking that the debug
registers should be able to make this happen.  For some reason though I
can't get them to do anything.  Well, almost nothing.  I can get the cpu
to generate a debug exception when I read the debug registers, which is
not useful.

here's a good reference I've been using:
http://my.tele2.ee/mtx/i386/chp12-02.htm


here's what I'm using to test....


schedule() {
...
	static int has_run = 0;
        static unsigned long has_run_2 = 1;

        if( ! has_run && jiffies > 7000 )
        {
                has_run = 1;

                has_run_2 = 0xffffffff;

                asm ("movl %0, %%db0\n"
                     "  movl %1, %%db7\n"
                     "  wbinvd\n"
                     : /* no output */
                     :"a"(&has_run_2),
                      "b"(0x000f0202)
                    );

                /* this should cause an int1 */
                has_run_2 = 0;

                asm ("wbinvd\n"
                     "  movl %0, %%db7\n"
                     "  wbinvd\n"
                     : /* no output */
                     :"b"(0x00030202)
                    );

                /* this should cause an int1 */
                has_run_2 = 0xffffffff;

                asm ("wbinvd\n"
                     "  movl %0, %%db7\n"
                     "  wbinvd\n"
                     : /* no output */
                     :"b"(0x00070202)
                    );

                /* this should cause an int1 */
                has_run_2 = 0;

                asm ("wbinvd\n"
                     "  movl %0, %%db7\n"
                     "  wbinvd\n"
                     : /* no output */
                     :"b"(0x00032202)
                    );

                /* this should cause an int1 */
                has_run_2 = 0xffffffff;

                asm ("wbinvd\n"
                     "  movl %%db7, %0\n"
                     "  movl %%db0, %1\n"
                     "  movl %%db6, %2\n"
                     :"=a"(db7), "=b"(addr0), "=c"(db6)
                     :/* no input */
                    );

                printk(KERN_DEBUG "%s: regs: a0:0x%8.8x "
                                  "db7:0x%8.8x db6:0x%8.8x\n",
                        __FUNCTION__, addr0, db7, db6);
        }
...
}





so, right at the top of the old schedule() function I put some stuff that
messes only once with the debug registers.  Here's the gdb output after the cpu
throws a debug exception:



Program received signal SIGTRAP, Trace/breakpoint trap.
0x00112ea8 in schedule () at sched.c:446
446                     asm ("wbinvd\n"
(gdb) list
441                         );
442
443                     /* this should cause an int1 */
444                     has_run_2 = 0xffffffff;
445
446                     asm ("wbinvd\n"
447                          "  movl %%db7, %0\n"
448                          "  movl %%db0, %1\n"
449                          "  movl %%db6, %2\n"
450                          :"=a"(db7), "=b"(addr0), "=c"(db6)
(gdb) info reg
eax            0x192a00 1649152
ecx            0x7      7
edx            0x191d40 1645888
ebx            0x32202  205314
esp            0x1914dc 0x1914dc
ebp            0x191508 0x191508
esi            0x191564 1643876
edi            0x0      0
eip            0x112ea8 0x112ea8
eflags         0x202    514
cs             0x10     16
ss             0x18     24
ds             0x18     24
es             0x18     24
fs             0x2b     43
gs             0x18     24
fctrl          0x0      0
fstat          0x0      0
ftag           0x0      0
fiseg          0x0      0
fioff          0x0      0
foseg          0x0      0
fooff          0x0      0
fop            *value not available*






and here's the disassembled code:


  112e3e:       c7 05 00 2a 19 00 ff    movl   $0xffffffff,0x192a00     ;--- initial value
  112e45:       ff ff ff
  112e48:       b8 00 2a 19 00          mov    $0x192a00,%eax           ;--- push address into %db0
  112e4d:       bb 02 02 0f 00          mov    $0xf0202,%ebx            ;--- LEN0=11, R/W0=11, GE=1, G0=1
  112e52:       0f 23 c0                mov    %eax,%db0
  112e55:       0f 23 fb                mov    %ebx,%db7
  112e58:       0f 09                   wbinvd
  112e5a:       c7 05 00 2a 19 00 00    movl   $0x0,0x192a00            ;--- should break here
  112e61:       00 00 00
  112e64:       bb 02 02 03 00          mov    $0x30202,%ebx            ;--- LEN0=00, R/W0=11, GE=1, G0=1
  112e69:       0f 09                   wbinvd
  112e6b:       0f 23 fb                mov    %ebx,%db7
  112e6e:       0f 09                   wbinvd
  112e70:       c7 05 00 2a 19 00 ff    movl   $0xffffffff,0x192a00     ;--- should break here
  112e77:       ff ff ff
  112e7a:       bb 02 02 07 00          mov    $0x70202,%ebx            ;--- LEN0=01, R/W0=11, GE=1, G0=1
  112e7f:       0f 09                   wbinvd
  112e81:       0f 23 fb                mov    %ebx,%db7
  112e84:       0f 09                   wbinvd
  112e86:       c7 05 00 2a 19 00 00    movl   $0x0,0x192a00            ;--- should break here
  112e8d:       00 00 00
  112e90:       bb 02 22 03 00          mov    $0x32202,%ebx            ;--- LEN0=00, R/W0=11, GD=1, GE=1, G0=1 /* GD=1 means watch for debug reg access. */
  112e95:       0f 09                   wbinvd
  112e97:       0f 23 fb                mov    %ebx,%db7
  112e9a:       0f 09                   wbinvd
  112e9c:       c7 05 00 2a 19 00 ff    movl   $0xffffffff,0x192a00     ;--- should break here
  112ea3:       ff ff ff
  112ea6:       0f 09                   wbinvd
  112ea8:       0f 21 f8                mov    %db7,%eax                ;--- break!!!!   debug reg is accessed.
  112eab:       0f 21 c3                mov    %db0,%ebx
  112eae:       0f 21 f1                mov    %db6,%ecx
  112eb1:       89 c2                   mov    %eax,%edx
  112eb3:       89 de                   mov    %ebx,%esi
  112eb5:       51                      push   %ecx
  112eb6:       52                      push   %edx
  112eb7:       56                      push   %esi
  112eb8:       68 b2 36 18 00          push   $0x1836b2
  112ebd:       68 bb 36 18 00          push   $0x1836bb
  112ec2:       e8 4d 27 00 00          call   115614 <printk>




########################################################################





So, the CPU is generating a debug exception, and I am catching it.  it's
just not happening when I want or expect it to happen.  The same thing
happens for program instruction fetches.  It doesn't break.

I stuck in all the wbinvd instructions thinking it might be a cache
issue.  I have yet to turn the cache off.  it seems to me though that I
should be getting some exception with or without the cache enabled.

What am I doing wrong?  anyone know?  who knows how to use these
registers?

Thanks a million,

- Ben Johnson


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: how are debug registers supposed to work?
  2003-08-29  0:41 how are debug registers supposed to work? Ben Johnson
@ 2003-09-03 22:55 ` Andrew Cagney
  2003-09-04  0:17   ` Ben Johnson
  2003-09-17  0:30 ` Ben Johnson
  1 sibling, 1 reply; 4+ messages in thread
From: Andrew Cagney @ 2003-09-03 22:55 UTC (permalink / raw)
  To: Ben Johnson; +Cc: gdb


> So, the CPU is generating a debug exception, and I am catching it.  it's
> just not happening when I want or expect it to happen.  The same thing
> happens for program instruction fetches.  It doesn't break.
> 
> I stuck in all the wbinvd instructions thinking it might be a cache
> issue.  I have yet to turn the cache off.  it seems to me though that I
> should be getting some exception with or without the cache enabled.
> 
> What am I doing wrong?  anyone know?  who knows how to use these
> registers?

Just a thought, you're not fighting a user space process playing with 
those registers?

The other is to look at GDB using hardware debug registers on a user 
process.

Andrew



^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: how are debug registers supposed to work?
  2003-09-03 22:55 ` Andrew Cagney
@ 2003-09-04  0:17   ` Ben Johnson
  0 siblings, 0 replies; 4+ messages in thread
From: Ben Johnson @ 2003-09-04  0:17 UTC (permalink / raw)
  To: Andrew Cagney; +Cc: gdb

Thanks for the response.

Everything on this box is carefully (as possible) controlled.  I have
removed all reference to the debug registers throughout the kernel.  (I
don't know if they can be accessed directly from user space, but I would
guess not.)

I've turned on the 'GD' bit in %db7.  That causes a debug exception
(int1) to be thrown when any of the debug registers are accessed or
written to.  I believe they're not be fooled with because, unless I
purposefully read or write to the debug regs now, I never get a debug
exception.

I've tried setting both code and data break/watchpoints, and none of
them seem to work at all.  I know the interrupt handler is setup
correctly because I do get the interrupt when the GD bit is set and I
try to read %db7.  I've found no other way to make these register do
anything though.  I've given up.  I think they just don't work.

but, how could they not work?  There *must* be something I'm not
understanding.

I gave up leaving anything to chance and started putting code like this
in the (2.0 Kernel) schedule() function:

if( first_time ) {
    static unsigned long testvar = 0;
    unsigned long db7;

    first_time = 0;

    /* maybe flush and disable the cache here.  doesn't matter. */

    asm ("movl %0, %%db0\n"
         "  movl %1, %%db7\n"
         : /* no output */
         :"a"(&testvar),    /* put &testvar in db0 */
         "b"(0x000f2202)    /* watch 4 bytes for reads or writes
                             * at &testvar globally and set GD */
        );

    testvar = 0xffffffff; /* change all the bits */

    /* should get a debug exception here */

    printk(KERN_DEBUG "%s: any luck?\n", __FUNCTION__);

    asm ("movl %%db7, %0\n"  /* this movl triggers a debug exception. */
         :"=a"(db7)
        );
}


What am I not getting?  I think there must be another bit I need to set
or the data I'm using is not properly aligned (in some way I don't
understand) or maybe this register has to be enabled by the bios?  who
knows.

- Ben




On Wed, Sep 03, 2003 at 06:55:50PM -0400, Andrew Cagney wrote:
> 
> Just a thought, you're not fighting a user space process playing with 
> those registers?
> 
> The other is to look at GDB using hardware debug registers on a user 
> process.
> 
> Andrew
> 
> 


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: how are debug registers supposed to work?
  2003-08-29  0:41 how are debug registers supposed to work? Ben Johnson
  2003-09-03 22:55 ` Andrew Cagney
@ 2003-09-17  0:30 ` Ben Johnson
  1 sibling, 0 replies; 4+ messages in thread
From: Ben Johnson @ 2003-09-17  0:30 UTC (permalink / raw)
  To: gdb

I found the problem.  The addresses I'm attempting to use are logical
addresses, not linear.  The (2.0) kernel data segment's base address is
0xc0000000, so in order to get a linear address I have to add that base
address to it.

altered code that's now trapping in the right place:


schedule()
{
...
	static unsigned long has_run = 1;
	static unsigned long has_run_2 = 0;

        if( ! has_run && jiffies > 7000 )
        {
                has_run = 1;
                has_run_2 = 0;

                /* setup the debug registers */
                asm ("movl %%cr4, %%edx\n"   /* debug extensions */
                     "  orl $0x8, %%edx\n"
                     "  movl %%edx, %%cr4\n"
                     "  movl %0, %%db0\n"    /* push into db regs */
                     "  movl %1, %%db7\n"
                     "  lgdt 0x00106852\n"   /* pentium may need this */
                     : /* no output */
                     :"a"(0xc0000000 + ((unsigned long)&has_run_2)),
                      "b"(0x000f2202)
                      /*"m"((((char *)&gdt)-6))*/
                     :"%edx"
                    );
        }

        if( has_run && ! has_run_2 )  /* debug reg generate exception */
        {
		/* whatever */
		has_run_2 = 0xffffffff
        }
...
}


I'm sure the debug extensions aren't needed.  I put in the lgdt
instruction because I read section in 18.17.4 of Intel's Software
Development Manual (Volume 3) that it may help Pentium processors
recognize breakpoints.  no other processors need that though.

- Ben


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2003-09-17  0:30 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2003-08-29  0:41 how are debug registers supposed to work? Ben Johnson
2003-09-03 22:55 ` Andrew Cagney
2003-09-04  0:17   ` Ben Johnson
2003-09-17  0:30 ` Ben Johnson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox