Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
[GDB] Add watch and rwatch hardware watchpoints
- Loading branch information
Showing
7 changed files
with
221 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
#include <stddef.h> | ||
#include <stdio.h> | ||
#include <assert.h> | ||
#include <virtaddr.h> | ||
#include <gpxe/gdbstub.h> | ||
#include <gdbmach.h> | ||
|
||
enum { | ||
DR7_CLEAR = 0x00000400, /* disable hardware breakpoints */ | ||
DR6_CLEAR = 0xffff0ff0, /* clear breakpoint status */ | ||
}; | ||
|
||
/** Hardware breakpoint, fields stored in x86 bit pattern form */ | ||
struct hwbp { | ||
int type; /* type (1=write watchpoint, 3=access watchpoint) */ | ||
unsigned long addr; /* linear address */ | ||
size_t len; /* length (0=1-byte, 1=2-byte, 3=4-byte) */ | ||
int enabled; | ||
}; | ||
|
||
static struct hwbp hwbps [ 4 ]; | ||
static gdbreg_t dr7 = DR7_CLEAR; | ||
static gdbreg_t dr6; | ||
|
||
static struct hwbp *gdbmach_find_hwbp ( int type, unsigned long addr, size_t len ) { | ||
struct hwbp *available = NULL; | ||
unsigned int i; | ||
for ( i = 0; i < sizeof hwbps / sizeof hwbps [ 0 ]; i++ ) { | ||
if ( hwbps [ i ].type == type && hwbps [ i ].addr == addr && hwbps [ i ].len == len ) { | ||
return &hwbps [ i ]; | ||
} | ||
if ( !hwbps [ i ].enabled ) { | ||
available = &hwbps [ i ]; | ||
} | ||
} | ||
return available; | ||
} | ||
|
||
static void gdbmach_commit_hwbp ( struct hwbp *bp ) { | ||
int regnum = bp - hwbps; | ||
|
||
/* Set breakpoint address */ | ||
assert ( regnum >= 0 && regnum < sizeof hwbps / sizeof hwbps [ 0 ] ); | ||
switch ( regnum ) { | ||
case 0: | ||
__asm__ __volatile__ ( "movl %0, %%dr0\n" : : "r" ( bp->addr ) ); | ||
break; | ||
case 1: | ||
__asm__ __volatile__ ( "movl %0, %%dr1\n" : : "r" ( bp->addr ) ); | ||
break; | ||
case 2: | ||
__asm__ __volatile__ ( "movl %0, %%dr2\n" : : "r" ( bp->addr ) ); | ||
break; | ||
case 3: | ||
__asm__ __volatile__ ( "movl %0, %%dr3\n" : : "r" ( bp->addr ) ); | ||
break; | ||
} | ||
|
||
/* Set type */ | ||
dr7 &= ~( 0x3 << ( 16 + 4 * regnum ) ); | ||
dr7 |= bp->type << ( 16 + 4 * regnum ); | ||
|
||
/* Set length */ | ||
dr7 &= ~( 0x3 << ( 18 + 4 * regnum ) ); | ||
dr7 |= bp->len << ( 18 + 4 * regnum ); | ||
|
||
/* Set/clear local enable bit */ | ||
dr7 &= ~( 0x3 << 2 * regnum ); | ||
dr7 |= bp->enabled << 2 * regnum; | ||
} | ||
|
||
int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len, int enable ) { | ||
struct hwbp *bp; | ||
|
||
/* Check and convert breakpoint type to x86 type */ | ||
switch ( type ) { | ||
case GDBMACH_WATCH: | ||
type = 0x1; | ||
break; | ||
case GDBMACH_AWATCH: | ||
type = 0x3; | ||
break; | ||
default: | ||
return 0; /* unsupported breakpoint type */ | ||
} | ||
|
||
/* Only lengths 1, 2, and 4 are supported */ | ||
if ( len != 2 && len != 4 ) { | ||
len = 1; | ||
} | ||
len--; /* convert to x86 breakpoint length bit pattern */ | ||
|
||
/* Calculate linear address by adding segment base */ | ||
addr += virt_offset; | ||
|
||
/* Set up the breakpoint */ | ||
bp = gdbmach_find_hwbp ( type, addr, len ); | ||
if ( !bp ) { | ||
return 0; /* ran out of hardware breakpoints */ | ||
} | ||
bp->type = type; | ||
bp->addr = addr; | ||
bp->len = len; | ||
bp->enabled = enable; | ||
gdbmach_commit_hwbp ( bp ); | ||
return 1; | ||
} | ||
|
||
static void gdbmach_disable_hwbps ( void ) { | ||
/* Store and clear breakpoint status register */ | ||
__asm__ __volatile__ ( "movl %%dr6, %0\n" "movl %1, %%dr6\n" : "=r" ( dr6 ) : "r" ( DR6_CLEAR ) ); | ||
|
||
/* Store and clear hardware breakpoints */ | ||
__asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( DR7_CLEAR ) ); | ||
} | ||
|
||
static void gdbmach_enable_hwbps ( void ) { | ||
/* Restore hardware breakpoints */ | ||
__asm__ __volatile__ ( "movl %0, %%dr7\n" : : "r" ( dr7 ) ); | ||
} | ||
|
||
__cdecl void gdbmach_handler ( int signo, gdbreg_t *regs ) { | ||
gdbmach_disable_hwbps(); | ||
gdbstub_handler ( signo, regs ); | ||
gdbmach_enable_hwbps(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters