mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-04 09:09:56 +00:00
Add 'hwatch' and 'dhwatch' ddb commands analogous to 'watch' and
'dwatch'. The new commands install hardware watchpoints if supported by the architecture and if there are enough registers to cover the desired memory area. No objection by: audit@, hackers@ MFC after: 2 weeks
This commit is contained in:
parent
761d6b7150
commit
17bbfb5897
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=79573
@ -96,6 +96,12 @@ static struct special_symbol {
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
||||
int db_md_set_watchpoint __P((db_expr_t addr, db_expr_t size));
|
||||
int db_md_clr_watchpoint __P((db_expr_t addr, db_expr_t size));
|
||||
void db_md_list_watchpoints __P((void));
|
||||
|
||||
|
||||
/*
|
||||
* Decode the function prologue for the function we're in, and note
|
||||
* which registers are stored where, and how large the stack frame is.
|
||||
@ -367,3 +373,30 @@ db_stack_trace_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, char *m
|
||||
frame += pi.pi_frame_size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
db_md_set_watchpoint(addr, size)
|
||||
db_expr_t addr;
|
||||
db_expr_t size;
|
||||
{
|
||||
return (-1);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
db_md_clr_watchpoint(addr, size)
|
||||
db_expr_t addr;
|
||||
db_expr_t size;
|
||||
{
|
||||
return (-1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
db_md_list_watchpoints()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,8 @@
|
||||
#include <sys/user.h>
|
||||
|
||||
#include <machine/cpu.h>
|
||||
#include <machine/md_var.h>
|
||||
#include <machine/reg.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
@ -107,6 +109,16 @@ static int db_numargs __P((struct i386_frame *));
|
||||
static void db_print_stack_entry __P((const char *, int, char **, int *, db_addr_t));
|
||||
static void decode_syscall __P((int, struct proc *));
|
||||
|
||||
|
||||
static char * watchtype_str __P((int type));
|
||||
int i386_set_watch __P((int watchnum, unsigned int watchaddr,
|
||||
int size, int access, struct dbreg * d));
|
||||
int i386_clr_watch __P((int watchnum, struct dbreg * d));
|
||||
int db_md_set_watchpoint __P((db_expr_t addr, db_expr_t size));
|
||||
int db_md_clr_watchpoint __P((db_expr_t addr, db_expr_t size));
|
||||
void db_md_list_watchpoints __P((void));
|
||||
|
||||
|
||||
/*
|
||||
* Figure out how many arguments were passed into the frame at "fp".
|
||||
*/
|
||||
@ -427,7 +439,7 @@ db_stack_trace_cmd(addr, have_addr, count, modif)
|
||||
}
|
||||
}
|
||||
|
||||
#define DB_DRX_FUNC(reg) \
|
||||
#define DB_DRX_FUNC(reg) \
|
||||
int \
|
||||
db_ ## reg (vp, valuep, op) \
|
||||
struct db_variable *vp; \
|
||||
@ -450,3 +462,191 @@ DB_DRX_FUNC(dr4)
|
||||
DB_DRX_FUNC(dr5)
|
||||
DB_DRX_FUNC(dr6)
|
||||
DB_DRX_FUNC(dr7)
|
||||
|
||||
|
||||
|
||||
int
|
||||
i386_set_watch(watchnum, watchaddr, size, access, d)
|
||||
int watchnum;
|
||||
unsigned int watchaddr;
|
||||
int size;
|
||||
int access;
|
||||
struct dbreg * d;
|
||||
{
|
||||
int i;
|
||||
unsigned int mask;
|
||||
|
||||
if (watchnum == -1) {
|
||||
for (i = 0, mask = 0x3; i < 4; i++, mask <<= 2)
|
||||
if ((d->dr7 & mask) == 0)
|
||||
break;
|
||||
if (i < 4)
|
||||
watchnum = i;
|
||||
else
|
||||
return (-1);
|
||||
}
|
||||
|
||||
switch (access) {
|
||||
case DBREG_DR7_EXEC:
|
||||
size = 1; /* size must be 1 for an execution breakpoint */
|
||||
/* fall through */
|
||||
case DBREG_DR7_WRONLY:
|
||||
case DBREG_DR7_RDWR:
|
||||
break;
|
||||
default : return (-1); break;
|
||||
}
|
||||
|
||||
/*
|
||||
* we can watch a 1, 2, or 4 byte sized location
|
||||
*/
|
||||
switch (size) {
|
||||
case 1 : mask = 0x00; break;
|
||||
case 2 : mask = 0x01 << 2; break;
|
||||
case 4 : mask = 0x03 << 2; break;
|
||||
default : return (-1); break;
|
||||
}
|
||||
|
||||
mask |= access;
|
||||
|
||||
/* clear the bits we are about to affect */
|
||||
d->dr7 &= ~((0x3 << (watchnum*2)) | (0x0f << (watchnum*4+16)));
|
||||
|
||||
/* set drN register to the address, N=watchnum */
|
||||
DBREG_DRX(d,watchnum) = watchaddr;
|
||||
|
||||
/* enable the watchpoint */
|
||||
d->dr7 |= (0x2 << (watchnum*2)) | (mask << (watchnum*4+16));
|
||||
|
||||
return (watchnum);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
i386_clr_watch(watchnum, d)
|
||||
int watchnum;
|
||||
struct dbreg * d;
|
||||
{
|
||||
|
||||
if (watchnum < 0 || watchnum >= 4)
|
||||
return (-1);
|
||||
|
||||
d->dr7 = d->dr7 & ~((0x3 << (watchnum*2)) | (0x0f << (watchnum*4+16)));
|
||||
DBREG_DRX(d,watchnum) = 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
db_md_set_watchpoint(addr, size)
|
||||
db_expr_t addr;
|
||||
db_expr_t size;
|
||||
{
|
||||
int avail, wsize;
|
||||
int i;
|
||||
struct dbreg d;
|
||||
|
||||
fill_dbregs(NULL, &d);
|
||||
|
||||
avail = 0;
|
||||
for(i=0; i<4; i++) {
|
||||
if ((d.dr7 & (3 << (i*2))) == 0)
|
||||
avail++;
|
||||
}
|
||||
|
||||
if (avail*4 < size)
|
||||
return (-1);
|
||||
|
||||
for (i=0; i<4 && (size != 0); i++) {
|
||||
if ((d.dr7 & (3<<(i*2))) == 0) {
|
||||
if (size > 4)
|
||||
wsize = 4;
|
||||
else
|
||||
wsize = size;
|
||||
if (wsize == 3)
|
||||
wsize++;
|
||||
i386_set_watch(i, addr, wsize,
|
||||
DBREG_DR7_WRONLY, &d);
|
||||
addr += wsize;
|
||||
size -= wsize;
|
||||
}
|
||||
}
|
||||
|
||||
set_dbregs(NULL, &d);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
db_md_clr_watchpoint(addr, size)
|
||||
db_expr_t addr;
|
||||
db_expr_t size;
|
||||
{
|
||||
int i;
|
||||
struct dbreg d;
|
||||
|
||||
fill_dbregs(NULL, &d);
|
||||
|
||||
for(i=0; i<4; i++) {
|
||||
if (d.dr7 & (3 << (i*2))) {
|
||||
if ((DBREG_DRX((&d), i) >= addr) &&
|
||||
(DBREG_DRX((&d), i) < addr+size))
|
||||
i386_clr_watch(i, &d);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
set_dbregs(NULL, &d);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
char *
|
||||
watchtype_str(type)
|
||||
int type;
|
||||
{
|
||||
switch (type) {
|
||||
case DBREG_DR7_EXEC : return "execute"; break;
|
||||
case DBREG_DR7_RDWR : return "read/write"; break;
|
||||
case DBREG_DR7_WRONLY : return "write"; break;
|
||||
default : return "invalid"; break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
db_md_list_watchpoints()
|
||||
{
|
||||
int i;
|
||||
struct dbreg d;
|
||||
|
||||
fill_dbregs(NULL, &d);
|
||||
|
||||
db_printf("\nhardware watchpoints:\n");
|
||||
db_printf(" watch status type len address\n"
|
||||
" ----- -------- ---------- --- ----------\n");
|
||||
for (i=0; i<4; i++) {
|
||||
if (d.dr7 & (0x03 << (i*2))) {
|
||||
unsigned type, len;
|
||||
type = (d.dr7 >> (16+(i*4))) & 3;
|
||||
len = (d.dr7 >> (16+(i*4)+2)) & 3;
|
||||
db_printf(" %-5d %-8s %10s %3d 0x%08x\n",
|
||||
i, "enabled", watchtype_str(type),
|
||||
len+1, DBREG_DRX((&d),i));
|
||||
}
|
||||
else {
|
||||
db_printf(" %-5d disabled\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
db_printf("\ndebug register values:\n");
|
||||
for (i=0; i<8; i++) {
|
||||
db_printf(" dr%d 0x%08x\n", i, DBREG_DRX((&d),i));
|
||||
}
|
||||
db_printf("\n");
|
||||
}
|
||||
|
||||
|
||||
|
@ -2186,15 +2186,27 @@ fill_dbregs(p, dbregs)
|
||||
{
|
||||
struct pcb *pcb;
|
||||
|
||||
pcb = &p->p_addr->u_pcb;
|
||||
dbregs->dr0 = pcb->pcb_dr0;
|
||||
dbregs->dr1 = pcb->pcb_dr1;
|
||||
dbregs->dr2 = pcb->pcb_dr2;
|
||||
dbregs->dr3 = pcb->pcb_dr3;
|
||||
dbregs->dr4 = 0;
|
||||
dbregs->dr5 = 0;
|
||||
dbregs->dr6 = pcb->pcb_dr6;
|
||||
dbregs->dr7 = pcb->pcb_dr7;
|
||||
if (p == NULL) {
|
||||
dbregs->dr0 = rdr0();
|
||||
dbregs->dr1 = rdr1();
|
||||
dbregs->dr2 = rdr2();
|
||||
dbregs->dr3 = rdr3();
|
||||
dbregs->dr4 = rdr4();
|
||||
dbregs->dr5 = rdr5();
|
||||
dbregs->dr6 = rdr6();
|
||||
dbregs->dr7 = rdr7();
|
||||
}
|
||||
else {
|
||||
pcb = &p->p_addr->u_pcb;
|
||||
dbregs->dr0 = pcb->pcb_dr0;
|
||||
dbregs->dr1 = pcb->pcb_dr1;
|
||||
dbregs->dr2 = pcb->pcb_dr2;
|
||||
dbregs->dr3 = pcb->pcb_dr3;
|
||||
dbregs->dr4 = 0;
|
||||
dbregs->dr5 = 0;
|
||||
dbregs->dr6 = pcb->pcb_dr6;
|
||||
dbregs->dr7 = pcb->pcb_dr7;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -2207,74 +2219,84 @@ set_dbregs(p, dbregs)
|
||||
int i;
|
||||
u_int32_t mask1, mask2;
|
||||
|
||||
/*
|
||||
* Don't let an illegal value for dr7 get set. Specifically,
|
||||
* check for undefined settings. Setting these bit patterns
|
||||
* result in undefined behaviour and can lead to an unexpected
|
||||
* TRCTRAP.
|
||||
*/
|
||||
for (i = 0, mask1 = 0x3<<16, mask2 = 0x2<<16; i < 8;
|
||||
i++, mask1 <<= 2, mask2 <<= 2)
|
||||
if ((dbregs->dr7 & mask1) == mask2)
|
||||
if (p == NULL) {
|
||||
load_dr0(dbregs->dr0);
|
||||
load_dr1(dbregs->dr1);
|
||||
load_dr2(dbregs->dr2);
|
||||
load_dr3(dbregs->dr3);
|
||||
load_dr4(dbregs->dr4);
|
||||
load_dr5(dbregs->dr5);
|
||||
load_dr6(dbregs->dr6);
|
||||
load_dr7(dbregs->dr7);
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Don't let an illegal value for dr7 get set. Specifically,
|
||||
* check for undefined settings. Setting these bit patterns
|
||||
* result in undefined behaviour and can lead to an unexpected
|
||||
* TRCTRAP.
|
||||
*/
|
||||
for (i = 0, mask1 = 0x3<<16, mask2 = 0x2<<16; i < 8;
|
||||
i++, mask1 <<= 2, mask2 <<= 2)
|
||||
if ((dbregs->dr7 & mask1) == mask2)
|
||||
return (EINVAL);
|
||||
|
||||
if (dbregs->dr7 & 0x0000fc00)
|
||||
return (EINVAL);
|
||||
|
||||
if (dbregs->dr7 & 0x0000fc00)
|
||||
return (EINVAL);
|
||||
pcb = &p->p_addr->u_pcb;
|
||||
|
||||
/*
|
||||
* Don't let a process set a breakpoint that is not within the
|
||||
* process's address space. If a process could do this, it
|
||||
* could halt the system by setting a breakpoint in the kernel
|
||||
* (if ddb was enabled). Thus, we need to check to make sure
|
||||
* that no breakpoints are being enabled for addresses outside
|
||||
* process's address space, unless, perhaps, we were called by
|
||||
* uid 0.
|
||||
*
|
||||
* XXX - what about when the watched area of the user's
|
||||
* address space is written into from within the kernel
|
||||
* ... wouldn't that still cause a breakpoint to be generated
|
||||
* from within kernel mode?
|
||||
*/
|
||||
|
||||
|
||||
|
||||
pcb = &p->p_addr->u_pcb;
|
||||
|
||||
/*
|
||||
* Don't let a process set a breakpoint that is not within the
|
||||
* process's address space. If a process could do this, it
|
||||
* could halt the system by setting a breakpoint in the kernel
|
||||
* (if ddb was enabled). Thus, we need to check to make sure
|
||||
* that no breakpoints are being enabled for addresses outside
|
||||
* process's address space, unless, perhaps, we were called by
|
||||
* uid 0.
|
||||
*
|
||||
* XXX - what about when the watched area of the user's
|
||||
* address space is written into from within the kernel
|
||||
* ... wouldn't that still cause a breakpoint to be generated
|
||||
* from within kernel mode?
|
||||
*/
|
||||
|
||||
if (suser(p) != 0) {
|
||||
if (dbregs->dr7 & 0x3) {
|
||||
/* dr0 is enabled */
|
||||
if (dbregs->dr0 >= VM_MAXUSER_ADDRESS)
|
||||
return (EINVAL);
|
||||
if (suser(p) != 0) {
|
||||
if (dbregs->dr7 & 0x3) {
|
||||
/* dr0 is enabled */
|
||||
if (dbregs->dr0 >= VM_MAXUSER_ADDRESS)
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
if (dbregs->dr7 & (0x3<<2)) {
|
||||
/* dr1 is enabled */
|
||||
if (dbregs->dr1 >= VM_MAXUSER_ADDRESS)
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
if (dbregs->dr7 & (0x3<<4)) {
|
||||
/* dr2 is enabled */
|
||||
if (dbregs->dr2 >= VM_MAXUSER_ADDRESS)
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
if (dbregs->dr7 & (0x3<<6)) {
|
||||
/* dr3 is enabled */
|
||||
if (dbregs->dr3 >= VM_MAXUSER_ADDRESS)
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
if (dbregs->dr7 & (0x3<<2)) {
|
||||
/* dr1 is enabled */
|
||||
if (dbregs->dr1 >= VM_MAXUSER_ADDRESS)
|
||||
return (EINVAL);
|
||||
}
|
||||
pcb->pcb_dr0 = dbregs->dr0;
|
||||
pcb->pcb_dr1 = dbregs->dr1;
|
||||
pcb->pcb_dr2 = dbregs->dr2;
|
||||
pcb->pcb_dr3 = dbregs->dr3;
|
||||
pcb->pcb_dr6 = dbregs->dr6;
|
||||
pcb->pcb_dr7 = dbregs->dr7;
|
||||
|
||||
if (dbregs->dr7 & (0x3<<4)) {
|
||||
/* dr2 is enabled */
|
||||
if (dbregs->dr2 >= VM_MAXUSER_ADDRESS)
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
if (dbregs->dr7 & (0x3<<6)) {
|
||||
/* dr3 is enabled */
|
||||
if (dbregs->dr3 >= VM_MAXUSER_ADDRESS)
|
||||
return (EINVAL);
|
||||
}
|
||||
pcb->pcb_flags |= PCB_DBREGS;
|
||||
}
|
||||
|
||||
pcb->pcb_dr0 = dbregs->dr0;
|
||||
pcb->pcb_dr1 = dbregs->dr1;
|
||||
pcb->pcb_dr2 = dbregs->dr2;
|
||||
pcb->pcb_dr3 = dbregs->dr3;
|
||||
pcb->pcb_dr6 = dbregs->dr6;
|
||||
pcb->pcb_dr7 = dbregs->dr7;
|
||||
|
||||
pcb->pcb_flags |= PCB_DBREGS;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
@ -401,6 +401,8 @@ static struct command db_command_table[] = {
|
||||
{ "break", db_breakpoint_cmd, 0, 0 },
|
||||
{ "dwatch", db_deletewatch_cmd, 0, 0 },
|
||||
{ "watch", db_watchpoint_cmd, CS_MORE,0 },
|
||||
{ "dhwatch", db_deletehwatch_cmd, 0, 0 },
|
||||
{ "hwatch", db_hwatchpoint_cmd, 0, 0 },
|
||||
{ "step", db_single_step_cmd, 0, 0 },
|
||||
{ "s", db_single_step_cmd, 0, 0 },
|
||||
{ "continue", db_continue_cmd, 0, 0 },
|
||||
|
@ -67,6 +67,10 @@ static void db_list_watchpoints __P((void));
|
||||
static void db_set_watchpoint __P((vm_map_t map, db_addr_t addr,
|
||||
vm_size_t size));
|
||||
|
||||
int db_md_set_watchpoint __P((db_expr_t addr, db_expr_t size));
|
||||
int db_md_clr_watchpoint __P((db_expr_t addr, db_expr_t size));
|
||||
void db_md_list_watchpoints __P((void));
|
||||
|
||||
|
||||
db_watchpoint_t
|
||||
db_watchpoint_alloc()
|
||||
@ -220,6 +224,7 @@ db_watchpoint_cmd(addr, have_addr, count, modif)
|
||||
DB_SHOW_COMMAND(watches, db_listwatch_cmd)
|
||||
{
|
||||
db_list_watchpoints();
|
||||
db_md_list_watchpoints();
|
||||
}
|
||||
|
||||
void
|
||||
@ -282,3 +287,43 @@ db_find_watchpoint(map, addr, regs)
|
||||
return (FALSE);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Delete hardware watchpoint */
|
||||
/*ARGSUSED*/
|
||||
void
|
||||
db_deletehwatch_cmd(addr, have_addr, count, modif)
|
||||
db_expr_t addr;
|
||||
boolean_t have_addr;
|
||||
db_expr_t count;
|
||||
char * modif;
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (count < 0)
|
||||
count = 4;
|
||||
|
||||
rc = db_md_clr_watchpoint(addr, count);
|
||||
if (rc < 0)
|
||||
db_printf("hardware watchpoint could not be deleted\n");
|
||||
}
|
||||
|
||||
/* Set hardware watchpoint */
|
||||
/*ARGSUSED*/
|
||||
void
|
||||
db_hwatchpoint_cmd(addr, have_addr, count, modif)
|
||||
db_expr_t addr;
|
||||
boolean_t have_addr;
|
||||
db_expr_t count;
|
||||
char * modif;
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (count < 0)
|
||||
count = 4;
|
||||
|
||||
rc = db_md_set_watchpoint(addr, count);
|
||||
if (rc < 0)
|
||||
db_printf("hardware watchpoint could not be set\n");
|
||||
}
|
||||
|
@ -109,8 +109,10 @@ void kdb_init __P((void));
|
||||
db_cmdfcn_t db_breakpoint_cmd;
|
||||
db_cmdfcn_t db_continue_cmd;
|
||||
db_cmdfcn_t db_delete_cmd;
|
||||
db_cmdfcn_t db_deletehwatch_cmd;
|
||||
db_cmdfcn_t db_deletewatch_cmd;
|
||||
db_cmdfcn_t db_examine_cmd;
|
||||
db_cmdfcn_t db_hwatchpoint_cmd;
|
||||
db_cmdfcn_t db_listbreak_cmd;
|
||||
db_cmdfcn_t db_print_cmd;
|
||||
db_cmdfcn_t db_ps;
|
||||
|
@ -34,6 +34,8 @@
|
||||
#include <sys/user.h>
|
||||
|
||||
#include <machine/cpu.h>
|
||||
#include <machine/md_var.h>
|
||||
#include <machine/reg.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
@ -107,6 +109,16 @@ static int db_numargs __P((struct i386_frame *));
|
||||
static void db_print_stack_entry __P((const char *, int, char **, int *, db_addr_t));
|
||||
static void decode_syscall __P((int, struct proc *));
|
||||
|
||||
|
||||
static char * watchtype_str __P((int type));
|
||||
int i386_set_watch __P((int watchnum, unsigned int watchaddr,
|
||||
int size, int access, struct dbreg * d));
|
||||
int i386_clr_watch __P((int watchnum, struct dbreg * d));
|
||||
int db_md_set_watchpoint __P((db_expr_t addr, db_expr_t size));
|
||||
int db_md_clr_watchpoint __P((db_expr_t addr, db_expr_t size));
|
||||
void db_md_list_watchpoints __P((void));
|
||||
|
||||
|
||||
/*
|
||||
* Figure out how many arguments were passed into the frame at "fp".
|
||||
*/
|
||||
@ -427,7 +439,7 @@ db_stack_trace_cmd(addr, have_addr, count, modif)
|
||||
}
|
||||
}
|
||||
|
||||
#define DB_DRX_FUNC(reg) \
|
||||
#define DB_DRX_FUNC(reg) \
|
||||
int \
|
||||
db_ ## reg (vp, valuep, op) \
|
||||
struct db_variable *vp; \
|
||||
@ -450,3 +462,191 @@ DB_DRX_FUNC(dr4)
|
||||
DB_DRX_FUNC(dr5)
|
||||
DB_DRX_FUNC(dr6)
|
||||
DB_DRX_FUNC(dr7)
|
||||
|
||||
|
||||
|
||||
int
|
||||
i386_set_watch(watchnum, watchaddr, size, access, d)
|
||||
int watchnum;
|
||||
unsigned int watchaddr;
|
||||
int size;
|
||||
int access;
|
||||
struct dbreg * d;
|
||||
{
|
||||
int i;
|
||||
unsigned int mask;
|
||||
|
||||
if (watchnum == -1) {
|
||||
for (i = 0, mask = 0x3; i < 4; i++, mask <<= 2)
|
||||
if ((d->dr7 & mask) == 0)
|
||||
break;
|
||||
if (i < 4)
|
||||
watchnum = i;
|
||||
else
|
||||
return (-1);
|
||||
}
|
||||
|
||||
switch (access) {
|
||||
case DBREG_DR7_EXEC:
|
||||
size = 1; /* size must be 1 for an execution breakpoint */
|
||||
/* fall through */
|
||||
case DBREG_DR7_WRONLY:
|
||||
case DBREG_DR7_RDWR:
|
||||
break;
|
||||
default : return (-1); break;
|
||||
}
|
||||
|
||||
/*
|
||||
* we can watch a 1, 2, or 4 byte sized location
|
||||
*/
|
||||
switch (size) {
|
||||
case 1 : mask = 0x00; break;
|
||||
case 2 : mask = 0x01 << 2; break;
|
||||
case 4 : mask = 0x03 << 2; break;
|
||||
default : return (-1); break;
|
||||
}
|
||||
|
||||
mask |= access;
|
||||
|
||||
/* clear the bits we are about to affect */
|
||||
d->dr7 &= ~((0x3 << (watchnum*2)) | (0x0f << (watchnum*4+16)));
|
||||
|
||||
/* set drN register to the address, N=watchnum */
|
||||
DBREG_DRX(d,watchnum) = watchaddr;
|
||||
|
||||
/* enable the watchpoint */
|
||||
d->dr7 |= (0x2 << (watchnum*2)) | (mask << (watchnum*4+16));
|
||||
|
||||
return (watchnum);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
i386_clr_watch(watchnum, d)
|
||||
int watchnum;
|
||||
struct dbreg * d;
|
||||
{
|
||||
|
||||
if (watchnum < 0 || watchnum >= 4)
|
||||
return (-1);
|
||||
|
||||
d->dr7 = d->dr7 & ~((0x3 << (watchnum*2)) | (0x0f << (watchnum*4+16)));
|
||||
DBREG_DRX(d,watchnum) = 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
db_md_set_watchpoint(addr, size)
|
||||
db_expr_t addr;
|
||||
db_expr_t size;
|
||||
{
|
||||
int avail, wsize;
|
||||
int i;
|
||||
struct dbreg d;
|
||||
|
||||
fill_dbregs(NULL, &d);
|
||||
|
||||
avail = 0;
|
||||
for(i=0; i<4; i++) {
|
||||
if ((d.dr7 & (3 << (i*2))) == 0)
|
||||
avail++;
|
||||
}
|
||||
|
||||
if (avail*4 < size)
|
||||
return (-1);
|
||||
|
||||
for (i=0; i<4 && (size != 0); i++) {
|
||||
if ((d.dr7 & (3<<(i*2))) == 0) {
|
||||
if (size > 4)
|
||||
wsize = 4;
|
||||
else
|
||||
wsize = size;
|
||||
if (wsize == 3)
|
||||
wsize++;
|
||||
i386_set_watch(i, addr, wsize,
|
||||
DBREG_DR7_WRONLY, &d);
|
||||
addr += wsize;
|
||||
size -= wsize;
|
||||
}
|
||||
}
|
||||
|
||||
set_dbregs(NULL, &d);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
db_md_clr_watchpoint(addr, size)
|
||||
db_expr_t addr;
|
||||
db_expr_t size;
|
||||
{
|
||||
int i;
|
||||
struct dbreg d;
|
||||
|
||||
fill_dbregs(NULL, &d);
|
||||
|
||||
for(i=0; i<4; i++) {
|
||||
if (d.dr7 & (3 << (i*2))) {
|
||||
if ((DBREG_DRX((&d), i) >= addr) &&
|
||||
(DBREG_DRX((&d), i) < addr+size))
|
||||
i386_clr_watch(i, &d);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
set_dbregs(NULL, &d);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
char *
|
||||
watchtype_str(type)
|
||||
int type;
|
||||
{
|
||||
switch (type) {
|
||||
case DBREG_DR7_EXEC : return "execute"; break;
|
||||
case DBREG_DR7_RDWR : return "read/write"; break;
|
||||
case DBREG_DR7_WRONLY : return "write"; break;
|
||||
default : return "invalid"; break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
db_md_list_watchpoints()
|
||||
{
|
||||
int i;
|
||||
struct dbreg d;
|
||||
|
||||
fill_dbregs(NULL, &d);
|
||||
|
||||
db_printf("\nhardware watchpoints:\n");
|
||||
db_printf(" watch status type len address\n"
|
||||
" ----- -------- ---------- --- ----------\n");
|
||||
for (i=0; i<4; i++) {
|
||||
if (d.dr7 & (0x03 << (i*2))) {
|
||||
unsigned type, len;
|
||||
type = (d.dr7 >> (16+(i*4))) & 3;
|
||||
len = (d.dr7 >> (16+(i*4)+2)) & 3;
|
||||
db_printf(" %-5d %-8s %10s %3d 0x%08x\n",
|
||||
i, "enabled", watchtype_str(type),
|
||||
len+1, DBREG_DRX((&d),i));
|
||||
}
|
||||
else {
|
||||
db_printf(" %-5d disabled\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
db_printf("\ndebug register values:\n");
|
||||
for (i=0; i<8; i++) {
|
||||
db_printf(" dr%d 0x%08x\n", i, DBREG_DRX((&d),i));
|
||||
}
|
||||
db_printf("\n");
|
||||
}
|
||||
|
||||
|
||||
|
@ -2186,15 +2186,27 @@ fill_dbregs(p, dbregs)
|
||||
{
|
||||
struct pcb *pcb;
|
||||
|
||||
pcb = &p->p_addr->u_pcb;
|
||||
dbregs->dr0 = pcb->pcb_dr0;
|
||||
dbregs->dr1 = pcb->pcb_dr1;
|
||||
dbregs->dr2 = pcb->pcb_dr2;
|
||||
dbregs->dr3 = pcb->pcb_dr3;
|
||||
dbregs->dr4 = 0;
|
||||
dbregs->dr5 = 0;
|
||||
dbregs->dr6 = pcb->pcb_dr6;
|
||||
dbregs->dr7 = pcb->pcb_dr7;
|
||||
if (p == NULL) {
|
||||
dbregs->dr0 = rdr0();
|
||||
dbregs->dr1 = rdr1();
|
||||
dbregs->dr2 = rdr2();
|
||||
dbregs->dr3 = rdr3();
|
||||
dbregs->dr4 = rdr4();
|
||||
dbregs->dr5 = rdr5();
|
||||
dbregs->dr6 = rdr6();
|
||||
dbregs->dr7 = rdr7();
|
||||
}
|
||||
else {
|
||||
pcb = &p->p_addr->u_pcb;
|
||||
dbregs->dr0 = pcb->pcb_dr0;
|
||||
dbregs->dr1 = pcb->pcb_dr1;
|
||||
dbregs->dr2 = pcb->pcb_dr2;
|
||||
dbregs->dr3 = pcb->pcb_dr3;
|
||||
dbregs->dr4 = 0;
|
||||
dbregs->dr5 = 0;
|
||||
dbregs->dr6 = pcb->pcb_dr6;
|
||||
dbregs->dr7 = pcb->pcb_dr7;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -2207,74 +2219,84 @@ set_dbregs(p, dbregs)
|
||||
int i;
|
||||
u_int32_t mask1, mask2;
|
||||
|
||||
/*
|
||||
* Don't let an illegal value for dr7 get set. Specifically,
|
||||
* check for undefined settings. Setting these bit patterns
|
||||
* result in undefined behaviour and can lead to an unexpected
|
||||
* TRCTRAP.
|
||||
*/
|
||||
for (i = 0, mask1 = 0x3<<16, mask2 = 0x2<<16; i < 8;
|
||||
i++, mask1 <<= 2, mask2 <<= 2)
|
||||
if ((dbregs->dr7 & mask1) == mask2)
|
||||
if (p == NULL) {
|
||||
load_dr0(dbregs->dr0);
|
||||
load_dr1(dbregs->dr1);
|
||||
load_dr2(dbregs->dr2);
|
||||
load_dr3(dbregs->dr3);
|
||||
load_dr4(dbregs->dr4);
|
||||
load_dr5(dbregs->dr5);
|
||||
load_dr6(dbregs->dr6);
|
||||
load_dr7(dbregs->dr7);
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Don't let an illegal value for dr7 get set. Specifically,
|
||||
* check for undefined settings. Setting these bit patterns
|
||||
* result in undefined behaviour and can lead to an unexpected
|
||||
* TRCTRAP.
|
||||
*/
|
||||
for (i = 0, mask1 = 0x3<<16, mask2 = 0x2<<16; i < 8;
|
||||
i++, mask1 <<= 2, mask2 <<= 2)
|
||||
if ((dbregs->dr7 & mask1) == mask2)
|
||||
return (EINVAL);
|
||||
|
||||
if (dbregs->dr7 & 0x0000fc00)
|
||||
return (EINVAL);
|
||||
|
||||
if (dbregs->dr7 & 0x0000fc00)
|
||||
return (EINVAL);
|
||||
pcb = &p->p_addr->u_pcb;
|
||||
|
||||
/*
|
||||
* Don't let a process set a breakpoint that is not within the
|
||||
* process's address space. If a process could do this, it
|
||||
* could halt the system by setting a breakpoint in the kernel
|
||||
* (if ddb was enabled). Thus, we need to check to make sure
|
||||
* that no breakpoints are being enabled for addresses outside
|
||||
* process's address space, unless, perhaps, we were called by
|
||||
* uid 0.
|
||||
*
|
||||
* XXX - what about when the watched area of the user's
|
||||
* address space is written into from within the kernel
|
||||
* ... wouldn't that still cause a breakpoint to be generated
|
||||
* from within kernel mode?
|
||||
*/
|
||||
|
||||
|
||||
|
||||
pcb = &p->p_addr->u_pcb;
|
||||
|
||||
/*
|
||||
* Don't let a process set a breakpoint that is not within the
|
||||
* process's address space. If a process could do this, it
|
||||
* could halt the system by setting a breakpoint in the kernel
|
||||
* (if ddb was enabled). Thus, we need to check to make sure
|
||||
* that no breakpoints are being enabled for addresses outside
|
||||
* process's address space, unless, perhaps, we were called by
|
||||
* uid 0.
|
||||
*
|
||||
* XXX - what about when the watched area of the user's
|
||||
* address space is written into from within the kernel
|
||||
* ... wouldn't that still cause a breakpoint to be generated
|
||||
* from within kernel mode?
|
||||
*/
|
||||
|
||||
if (suser(p) != 0) {
|
||||
if (dbregs->dr7 & 0x3) {
|
||||
/* dr0 is enabled */
|
||||
if (dbregs->dr0 >= VM_MAXUSER_ADDRESS)
|
||||
return (EINVAL);
|
||||
if (suser(p) != 0) {
|
||||
if (dbregs->dr7 & 0x3) {
|
||||
/* dr0 is enabled */
|
||||
if (dbregs->dr0 >= VM_MAXUSER_ADDRESS)
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
if (dbregs->dr7 & (0x3<<2)) {
|
||||
/* dr1 is enabled */
|
||||
if (dbregs->dr1 >= VM_MAXUSER_ADDRESS)
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
if (dbregs->dr7 & (0x3<<4)) {
|
||||
/* dr2 is enabled */
|
||||
if (dbregs->dr2 >= VM_MAXUSER_ADDRESS)
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
if (dbregs->dr7 & (0x3<<6)) {
|
||||
/* dr3 is enabled */
|
||||
if (dbregs->dr3 >= VM_MAXUSER_ADDRESS)
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
if (dbregs->dr7 & (0x3<<2)) {
|
||||
/* dr1 is enabled */
|
||||
if (dbregs->dr1 >= VM_MAXUSER_ADDRESS)
|
||||
return (EINVAL);
|
||||
}
|
||||
pcb->pcb_dr0 = dbregs->dr0;
|
||||
pcb->pcb_dr1 = dbregs->dr1;
|
||||
pcb->pcb_dr2 = dbregs->dr2;
|
||||
pcb->pcb_dr3 = dbregs->dr3;
|
||||
pcb->pcb_dr6 = dbregs->dr6;
|
||||
pcb->pcb_dr7 = dbregs->dr7;
|
||||
|
||||
if (dbregs->dr7 & (0x3<<4)) {
|
||||
/* dr2 is enabled */
|
||||
if (dbregs->dr2 >= VM_MAXUSER_ADDRESS)
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
if (dbregs->dr7 & (0x3<<6)) {
|
||||
/* dr3 is enabled */
|
||||
if (dbregs->dr3 >= VM_MAXUSER_ADDRESS)
|
||||
return (EINVAL);
|
||||
}
|
||||
pcb->pcb_flags |= PCB_DBREGS;
|
||||
}
|
||||
|
||||
pcb->pcb_dr0 = dbregs->dr0;
|
||||
pcb->pcb_dr1 = dbregs->dr1;
|
||||
pcb->pcb_dr2 = dbregs->dr2;
|
||||
pcb->pcb_dr3 = dbregs->dr3;
|
||||
pcb->pcb_dr6 = dbregs->dr6;
|
||||
pcb->pcb_dr7 = dbregs->dr7;
|
||||
|
||||
pcb->pcb_flags |= PCB_DBREGS;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,12 @@
|
||||
#include <ddb/db_variables.h>
|
||||
#include <ddb/db_output.h>
|
||||
|
||||
|
||||
int db_md_set_watchpoint __P((db_expr_t addr, db_expr_t size));
|
||||
int db_md_clr_watchpoint __P((db_expr_t addr, db_expr_t size));
|
||||
void db_md_list_watchpoints __P((void));
|
||||
|
||||
|
||||
void
|
||||
db_stack_trace_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, char *modif)
|
||||
{
|
||||
@ -99,3 +105,30 @@ db_stack_trace_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, char *m
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
db_md_set_watchpoint(addr, size)
|
||||
db_expr_t addr;
|
||||
db_expr_t size;
|
||||
{
|
||||
return (-1);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
db_md_clr_watchpoint(addr, size)
|
||||
db_expr_t addr;
|
||||
db_expr_t size;
|
||||
{
|
||||
return (-1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
db_md_list_watchpoints()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user