x86: consolidate hw watchpoint logic into new file
This is a prerequisite to using these functions outside of ddb, but also provides some cleanup and minor refactoring. This code is almost entirely duplicated between the two implementations, the only significant difference being the lack of dbreg synchronization on i386. Cleanups are: - demote some internal functions to static - use the constant NDBREGS instead of a '4' literal - remove K&R definitions - some added comments Reviewed by: kib, jhb Sponsored by: NetApp, Inc. Sponsored by: Klara, Inc. MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D29153
This commit is contained in:
parent
a1d803c162
commit
c02c04f113
@ -128,11 +128,6 @@ static void db_nextframe(struct amd64_frame **, db_addr_t *, struct thread *);
|
||||
static void db_print_stack_entry(const char *, db_addr_t, void *);
|
||||
static void decode_syscall(int, struct thread *);
|
||||
|
||||
static const char * watchtype_str(int type);
|
||||
int amd64_set_watch(int watchnum, unsigned long watchaddr, int size,
|
||||
int access, struct dbreg *d);
|
||||
int amd64_clr_watch(int watchnum, struct dbreg *d);
|
||||
|
||||
static void
|
||||
db_print_stack_entry(const char *name, db_addr_t callpc, void *frame)
|
||||
{
|
||||
@ -391,223 +386,22 @@ db_trace_thread(struct thread *thr, int count)
|
||||
}
|
||||
|
||||
int
|
||||
amd64_set_watch(watchnum, watchaddr, size, access, d)
|
||||
int watchnum;
|
||||
unsigned long watchaddr;
|
||||
int size;
|
||||
int access;
|
||||
struct dbreg *d;
|
||||
db_md_set_watchpoint(db_expr_t addr, db_expr_t size)
|
||||
{
|
||||
int i, len;
|
||||
|
||||
if (watchnum == -1) {
|
||||
for (i = 0; i < 4; i++)
|
||||
if (!DBREG_DR7_ENABLED(d->dr[7], i))
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* we can watch a 1, 2, 4, or 8 byte sized location
|
||||
*/
|
||||
switch (size) {
|
||||
case 1:
|
||||
len = DBREG_DR7_LEN_1;
|
||||
break;
|
||||
case 2:
|
||||
len = DBREG_DR7_LEN_2;
|
||||
break;
|
||||
case 4:
|
||||
len = DBREG_DR7_LEN_4;
|
||||
break;
|
||||
case 8:
|
||||
len = DBREG_DR7_LEN_8;
|
||||
break;
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* clear the bits we are about to affect */
|
||||
d->dr[7] &= ~DBREG_DR7_MASK(watchnum);
|
||||
|
||||
/* set drN register to the address, N=watchnum */
|
||||
DBREG_DRX(d, watchnum) = watchaddr;
|
||||
|
||||
/* enable the watchpoint */
|
||||
d->dr[7] |= DBREG_DR7_SET(watchnum, len, access,
|
||||
DBREG_DR7_GLOBAL_ENABLE);
|
||||
|
||||
return (watchnum);
|
||||
return (dbreg_set_watchpoint((vm_offset_t)addr, (vm_size_t)size));
|
||||
}
|
||||
|
||||
int
|
||||
amd64_clr_watch(watchnum, d)
|
||||
int watchnum;
|
||||
struct dbreg *d;
|
||||
db_md_clr_watchpoint(db_expr_t addr, db_expr_t size)
|
||||
{
|
||||
|
||||
if (watchnum < 0 || watchnum >= 4)
|
||||
return (-1);
|
||||
|
||||
d->dr[7] &= ~DBREG_DR7_MASK(watchnum);
|
||||
DBREG_DRX(d, watchnum) = 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
db_md_set_watchpoint(addr, size)
|
||||
db_expr_t addr;
|
||||
db_expr_t size;
|
||||
{
|
||||
struct dbreg *d;
|
||||
struct pcpu *pc;
|
||||
int avail, c, cpu, i, wsize;
|
||||
|
||||
d = (struct dbreg *)PCPU_PTR(dbreg);
|
||||
cpu = PCPU_GET(cpuid);
|
||||
fill_dbregs(NULL, d);
|
||||
|
||||
avail = 0;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (!DBREG_DR7_ENABLED(d->dr[7], i))
|
||||
avail++;
|
||||
}
|
||||
|
||||
if (avail * 8 < size)
|
||||
return (-1);
|
||||
|
||||
for (i = 0; i < 4 && size > 0; i++) {
|
||||
if (!DBREG_DR7_ENABLED(d->dr[7], i)) {
|
||||
if (size >= 8 || (avail == 1 && size > 4))
|
||||
wsize = 8;
|
||||
else if (size > 2)
|
||||
wsize = 4;
|
||||
else
|
||||
wsize = size;
|
||||
amd64_set_watch(i, addr, wsize, DBREG_DR7_WRONLY, d);
|
||||
addr += wsize;
|
||||
size -= wsize;
|
||||
avail--;
|
||||
}
|
||||
}
|
||||
|
||||
set_dbregs(NULL, d);
|
||||
CPU_FOREACH(c) {
|
||||
if (c == cpu)
|
||||
continue;
|
||||
pc = pcpu_find(c);
|
||||
memcpy(pc->pc_dbreg, d, sizeof(*d));
|
||||
pc->pc_dbreg_cmd = PC_DBREG_CMD_LOAD;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
db_md_clr_watchpoint(addr, size)
|
||||
db_expr_t addr;
|
||||
db_expr_t size;
|
||||
{
|
||||
struct dbreg *d;
|
||||
struct pcpu *pc;
|
||||
int i, c, cpu;
|
||||
|
||||
d = (struct dbreg *)PCPU_PTR(dbreg);
|
||||
cpu = PCPU_GET(cpuid);
|
||||
fill_dbregs(NULL, d);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (DBREG_DR7_ENABLED(d->dr[7], i)) {
|
||||
if (DBREG_DRX((d), i) >= addr &&
|
||||
DBREG_DRX((d), i) < addr + size)
|
||||
amd64_clr_watch(i, d);
|
||||
}
|
||||
}
|
||||
|
||||
set_dbregs(NULL, d);
|
||||
CPU_FOREACH(c) {
|
||||
if (c == cpu)
|
||||
continue;
|
||||
pc = pcpu_find(c);
|
||||
memcpy(pc->pc_dbreg, d, sizeof(*d));
|
||||
pc->pc_dbreg_cmd = PC_DBREG_CMD_LOAD;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static const 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;
|
||||
}
|
||||
return (dbreg_clr_watchpoint((vm_offset_t)addr, (vm_size_t)size));
|
||||
}
|
||||
|
||||
void
|
||||
db_md_list_watchpoints(void)
|
||||
{
|
||||
struct dbreg d;
|
||||
int i, len, type;
|
||||
|
||||
fill_dbregs(NULL, &d);
|
||||
|
||||
db_printf("\nhardware watchpoints:\n");
|
||||
db_printf(" watch status type len address\n");
|
||||
db_printf(" ----- -------- ---------- --- ------------------\n");
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (DBREG_DR7_ENABLED(d.dr[7], i)) {
|
||||
type = DBREG_DR7_ACCESS(d.dr[7], i);
|
||||
len = DBREG_DR7_LEN(d.dr[7], i);
|
||||
if (len == DBREG_DR7_LEN_8)
|
||||
len = 8;
|
||||
else
|
||||
len++;
|
||||
db_printf(" %-5d %-8s %10s %3d ",
|
||||
i, "enabled", watchtype_str(type), len);
|
||||
db_printsym((db_addr_t)DBREG_DRX(&d, i), DB_STGY_ANY);
|
||||
db_printf("\n");
|
||||
} else {
|
||||
db_printf(" %-5d disabled\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
db_printf("\ndebug register values:\n");
|
||||
for (i = 0; i < 8; i++)
|
||||
if (i != 4 && i != 5)
|
||||
db_printf(" dr%d 0x%016lx\n", i, DBREG_DRX(&d, i));
|
||||
db_printf("\n");
|
||||
}
|
||||
|
||||
void
|
||||
amd64_db_resume_dbreg(void)
|
||||
{
|
||||
struct dbreg *d;
|
||||
|
||||
switch (PCPU_GET(dbreg_cmd)) {
|
||||
case PC_DBREG_CMD_LOAD:
|
||||
d = (struct dbreg *)PCPU_PTR(dbreg);
|
||||
set_dbregs(NULL, d);
|
||||
PCPU_SET(dbreg_cmd, PC_DBREG_CMD_NONE);
|
||||
break;
|
||||
}
|
||||
dbreg_list_watchpoints();
|
||||
}
|
||||
|
@ -320,6 +320,7 @@ x86/x86/bus_machdep.c standard
|
||||
x86/x86/busdma_bounce.c standard
|
||||
x86/x86/busdma_machdep.c standard
|
||||
x86/x86/cpu_machdep.c standard
|
||||
x86/x86/dbreg.c optional ddb
|
||||
x86/x86/dump_machdep.c standard
|
||||
x86/x86/fdt_machdep.c optional fdt
|
||||
x86/x86/identcpu.c standard
|
||||
|
@ -198,11 +198,6 @@ static void db_print_stack_entry(const char *, int, char **, int *, db_addr_t,
|
||||
void *);
|
||||
static void decode_syscall(int, struct thread *);
|
||||
|
||||
static const char * watchtype_str(int type);
|
||||
int i386_set_watch(int watchnum, unsigned int watchaddr, int size, int access,
|
||||
struct dbreg *d);
|
||||
int i386_clr_watch(int watchnum, struct dbreg *d);
|
||||
|
||||
/*
|
||||
* Figure out how many arguments were passed into the frame at "fp".
|
||||
*/
|
||||
@ -618,180 +613,22 @@ db_trace_thread(struct thread *thr, int count)
|
||||
}
|
||||
|
||||
int
|
||||
i386_set_watch(watchnum, watchaddr, size, access, d)
|
||||
int watchnum;
|
||||
unsigned int watchaddr;
|
||||
int size;
|
||||
int access;
|
||||
struct dbreg *d;
|
||||
db_md_set_watchpoint(db_expr_t addr, db_expr_t size)
|
||||
{
|
||||
int i, len;
|
||||
|
||||
if (watchnum == -1) {
|
||||
for (i = 0; i < 4; i++)
|
||||
if (!DBREG_DR7_ENABLED(d->dr[7], i))
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* we can watch a 1, 2, or 4 byte sized location
|
||||
*/
|
||||
switch (size) {
|
||||
case 1:
|
||||
len = DBREG_DR7_LEN_1;
|
||||
break;
|
||||
case 2:
|
||||
len = DBREG_DR7_LEN_2;
|
||||
break;
|
||||
case 4:
|
||||
len = DBREG_DR7_LEN_4;
|
||||
break;
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* clear the bits we are about to affect */
|
||||
d->dr[7] &= ~DBREG_DR7_MASK(watchnum);
|
||||
|
||||
/* set drN register to the address, N=watchnum */
|
||||
DBREG_DRX(d, watchnum) = watchaddr;
|
||||
|
||||
/* enable the watchpoint */
|
||||
d->dr[7] |= DBREG_DR7_SET(watchnum, len, access,
|
||||
DBREG_DR7_GLOBAL_ENABLE);
|
||||
|
||||
return (watchnum);
|
||||
return (dbreg_set_watchpoint((vm_offset_t)addr, (vm_size_t)size));
|
||||
}
|
||||
|
||||
int
|
||||
i386_clr_watch(watchnum, d)
|
||||
int watchnum;
|
||||
struct dbreg *d;
|
||||
db_md_clr_watchpoint(db_expr_t addr, db_expr_t size)
|
||||
{
|
||||
|
||||
if (watchnum < 0 || watchnum >= 4)
|
||||
return (-1);
|
||||
|
||||
d->dr[7] &= ~DBREG_DR7_MASK(watchnum);
|
||||
DBREG_DRX(d, watchnum) = 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
db_md_set_watchpoint(addr, size)
|
||||
db_expr_t addr;
|
||||
db_expr_t size;
|
||||
{
|
||||
struct dbreg d;
|
||||
int avail, i, wsize;
|
||||
|
||||
fill_dbregs(NULL, &d);
|
||||
|
||||
avail = 0;
|
||||
for(i = 0; i < 4; i++) {
|
||||
if (!DBREG_DR7_ENABLED(d.dr[7], i))
|
||||
avail++;
|
||||
}
|
||||
|
||||
if (avail * 4 < size)
|
||||
return (-1);
|
||||
|
||||
for (i = 0; i < 4 && (size > 0); i++) {
|
||||
if (!DBREG_DR7_ENABLED(d.dr[7], i)) {
|
||||
if (size > 2)
|
||||
wsize = 4;
|
||||
else
|
||||
wsize = size;
|
||||
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;
|
||||
{
|
||||
struct dbreg d;
|
||||
int i;
|
||||
|
||||
fill_dbregs(NULL, &d);
|
||||
|
||||
for(i = 0; i < 4; i++) {
|
||||
if (DBREG_DR7_ENABLED(d.dr[7], i)) {
|
||||
if ((DBREG_DRX((&d), i) >= addr) &&
|
||||
(DBREG_DRX((&d), i) < addr+size))
|
||||
i386_clr_watch(i, &d);
|
||||
}
|
||||
}
|
||||
|
||||
set_dbregs(NULL, &d);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
static const 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;
|
||||
}
|
||||
return (dbreg_clr_watchpoint((vm_offset_t)addr, (vm_size_t)size));
|
||||
}
|
||||
|
||||
void
|
||||
db_md_list_watchpoints(void)
|
||||
{
|
||||
struct dbreg d;
|
||||
int i, len, type;
|
||||
|
||||
fill_dbregs(NULL, &d);
|
||||
|
||||
db_printf("\nhardware watchpoints:\n");
|
||||
db_printf(" watch status type len address\n");
|
||||
db_printf(" ----- -------- ---------- --- ----------\n");
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (DBREG_DR7_ENABLED(d.dr[7], i)) {
|
||||
type = DBREG_DR7_ACCESS(d.dr[7], i);
|
||||
len = DBREG_DR7_LEN(d.dr[7], i);
|
||||
db_printf(" %-5d %-8s %10s %3d ",
|
||||
i, "enabled", watchtype_str(type), len + 1);
|
||||
db_printsym((db_addr_t)DBREG_DRX(&d, i), DB_STGY_ANY);
|
||||
db_printf("\n");
|
||||
} else {
|
||||
db_printf(" %-5d disabled\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
db_printf("\ndebug register values:\n");
|
||||
for (i = 0; i < 8; i++)
|
||||
if (i != 4 && i != 5)
|
||||
db_printf(" dr%d 0x%08x\n", i, DBREG_DRX(&d, i));
|
||||
db_printf("\n");
|
||||
dbreg_list_watchpoints();
|
||||
}
|
||||
|
@ -120,6 +120,9 @@ vm_paddr_t cpu_getmaxphyaddr(void);
|
||||
bool cpu_mwait_usable(void);
|
||||
void cpu_probe_amdc1e(void);
|
||||
void cpu_setregs(void);
|
||||
int dbreg_set_watchpoint(vm_offset_t addr, vm_size_t size);
|
||||
int dbreg_clr_watchpoint(vm_offset_t addr, vm_size_t size);
|
||||
void dbreg_list_watchpoints(void);
|
||||
bool disable_wp(void);
|
||||
void restore_wp(bool old_wp);
|
||||
void finishidentcpu(void);
|
||||
|
267
sys/x86/x86/dbreg.c
Normal file
267
sys/x86/x86/dbreg.c
Normal file
@ -0,0 +1,267 @@
|
||||
/*-
|
||||
* Mach Operating System
|
||||
* Copyright (c) 1991,1990 Carnegie Mellon University
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission to use, copy, modify and distribute this software and its
|
||||
* documentation is hereby granted, provided that both the copyright
|
||||
* notice and this permission notice appear in all copies of the
|
||||
* software, derivative works or modified versions, and any portions
|
||||
* thereof, and that both notices appear in supporting documentation.
|
||||
*
|
||||
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
|
||||
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
|
||||
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
|
||||
*
|
||||
* Carnegie Mellon requests users of this software to return to
|
||||
*
|
||||
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
|
||||
* School of Computer Science
|
||||
* Carnegie Mellon University
|
||||
* Pittsburgh PA 15213-3890
|
||||
*
|
||||
* any improvements or extensions that they make and grant Carnegie the
|
||||
* rights to redistribute these changes.
|
||||
*/
|
||||
|
||||
#include "opt_ddb.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/pcpu.h>
|
||||
#include <sys/smp.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <machine/frame.h>
|
||||
#include <machine/md_var.h>
|
||||
|
||||
#include <ddb/ddb.h>
|
||||
#include <ddb/db_sym.h>
|
||||
|
||||
#define NDBREGS 4
|
||||
#ifdef __amd64__
|
||||
#define MAXWATCHSIZE 8
|
||||
#else
|
||||
#define MAXWATCHSIZE 4
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Set a watchpoint in the debug register denoted by 'watchnum'.
|
||||
*/
|
||||
static void
|
||||
dbreg_set_watchreg(int watchnum, vm_offset_t watchaddr, vm_size_t size,
|
||||
int access, struct dbreg *d)
|
||||
{
|
||||
int len;
|
||||
|
||||
MPASS(watchnum >= 0 && watchnum < NDBREGS);
|
||||
|
||||
/* size must be 1 for an execution breakpoint */
|
||||
if (access == DBREG_DR7_EXEC)
|
||||
size = 1;
|
||||
|
||||
/*
|
||||
* we can watch a 1, 2, or 4 byte sized location
|
||||
*/
|
||||
switch (size) {
|
||||
case 1:
|
||||
len = DBREG_DR7_LEN_1;
|
||||
break;
|
||||
case 2:
|
||||
len = DBREG_DR7_LEN_2;
|
||||
break;
|
||||
case 4:
|
||||
len = DBREG_DR7_LEN_4;
|
||||
break;
|
||||
#if MAXWATCHSIZE >= 8
|
||||
case 8:
|
||||
len = DBREG_DR7_LEN_8;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
/* clear the bits we are about to affect */
|
||||
d->dr[7] &= ~DBREG_DR7_MASK(watchnum);
|
||||
|
||||
/* set drN register to the address, N=watchnum */
|
||||
DBREG_DRX(d, watchnum) = watchaddr;
|
||||
|
||||
/* enable the watchpoint */
|
||||
d->dr[7] |= DBREG_DR7_SET(watchnum, len, access,
|
||||
DBREG_DR7_GLOBAL_ENABLE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a watchpoint from the debug register denoted by 'watchnum'.
|
||||
*/
|
||||
static void
|
||||
dbreg_clr_watchreg(int watchnum, struct dbreg *d)
|
||||
{
|
||||
MPASS(watchnum >= 0 && watchnum < NDBREGS);
|
||||
|
||||
d->dr[7] &= ~DBREG_DR7_MASK(watchnum);
|
||||
DBREG_DRX(d, watchnum) = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sync the debug registers. Other cores will read these values from the PCPU
|
||||
* area when they resume. See amd64_db_resume_dbreg() below.
|
||||
*/
|
||||
static void
|
||||
dbreg_sync(struct dbreg *dp)
|
||||
{
|
||||
#ifdef __amd64__
|
||||
struct pcpu *pc;
|
||||
int cpu, c;
|
||||
|
||||
cpu = PCPU_GET(cpuid);
|
||||
CPU_FOREACH(c) {
|
||||
if (c == cpu)
|
||||
continue;
|
||||
pc = pcpu_find(c);
|
||||
memcpy(pc->pc_dbreg, dp, sizeof(*dp));
|
||||
pc->pc_dbreg_cmd = PC_DBREG_CMD_LOAD;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
dbreg_set_watchpoint(vm_offset_t addr, vm_size_t size)
|
||||
{
|
||||
struct dbreg *d;
|
||||
int avail, i, wsize;
|
||||
|
||||
#ifdef __amd64__
|
||||
d = (struct dbreg *)PCPU_PTR(dbreg);
|
||||
#else
|
||||
/* debug registers aren't stored in PCPU on i386. */
|
||||
struct dbreg d_temp;
|
||||
d = &d_temp;
|
||||
#endif
|
||||
|
||||
fill_dbregs(NULL, d);
|
||||
|
||||
/*
|
||||
* Check if there are enough available registers to cover the desired
|
||||
* area.
|
||||
*/
|
||||
avail = 0;
|
||||
for (i = 0; i < NDBREGS; i++) {
|
||||
if (!DBREG_DR7_ENABLED(d->dr[7], i))
|
||||
avail++;
|
||||
}
|
||||
|
||||
if (avail * MAXWATCHSIZE < size)
|
||||
return (-1);
|
||||
|
||||
for (i = 0; i < NDBREGS && size > 0; i++) {
|
||||
if (!DBREG_DR7_ENABLED(d->dr[7], i)) {
|
||||
if ((size >= 8 || (avail == 1 && size > 4)) &&
|
||||
MAXWATCHSIZE == 8)
|
||||
wsize = 8;
|
||||
else if (size > 2)
|
||||
wsize = 4;
|
||||
else
|
||||
wsize = size;
|
||||
dbreg_set_watchreg(i, addr, wsize, DBREG_DR7_WRONLY, d);
|
||||
addr += wsize;
|
||||
size -= wsize;
|
||||
avail--;
|
||||
}
|
||||
}
|
||||
|
||||
set_dbregs(NULL, d);
|
||||
dbreg_sync(d);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
dbreg_clr_watchpoint(vm_offset_t addr, vm_size_t size)
|
||||
{
|
||||
struct dbreg *d;
|
||||
int i;
|
||||
|
||||
#ifdef __amd64__
|
||||
d = (struct dbreg *)PCPU_PTR(dbreg);
|
||||
#else
|
||||
/* debug registers aren't stored in PCPU on i386. */
|
||||
struct dbreg d_temp;
|
||||
d = &d_temp;
|
||||
#endif
|
||||
fill_dbregs(NULL, d);
|
||||
|
||||
for (i = 0; i < NDBREGS; i++) {
|
||||
if (DBREG_DR7_ENABLED(d->dr[7], i)) {
|
||||
if (DBREG_DRX((d), i) >= addr &&
|
||||
DBREG_DRX((d), i) < addr + size)
|
||||
dbreg_clr_watchreg(i, d);
|
||||
}
|
||||
}
|
||||
|
||||
set_dbregs(NULL, d);
|
||||
dbreg_sync(d);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef DDB
|
||||
static const char *
|
||||
watchtype_str(int type)
|
||||
{
|
||||
|
||||
switch (type) {
|
||||
case DBREG_DR7_EXEC:
|
||||
return ("execute");
|
||||
case DBREG_DR7_RDWR:
|
||||
return ("read/write");
|
||||
case DBREG_DR7_WRONLY:
|
||||
return ("write");
|
||||
default:
|
||||
return ("invalid");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dbreg_list_watchpoints(void)
|
||||
{
|
||||
struct dbreg d;
|
||||
int i, len, type;
|
||||
|
||||
fill_dbregs(NULL, &d);
|
||||
|
||||
db_printf("\nhardware watchpoints:\n");
|
||||
db_printf(" watch status type len address\n");
|
||||
db_printf(" ----- -------- ---------- --- ----------\n");
|
||||
for (i = 0; i < NDBREGS; i++) {
|
||||
if (DBREG_DR7_ENABLED(d.dr[7], i)) {
|
||||
type = DBREG_DR7_ACCESS(d.dr[7], i);
|
||||
len = DBREG_DR7_LEN(d.dr[7], i);
|
||||
db_printf(" %-5d %-8s %10s %3d ",
|
||||
i, "enabled", watchtype_str(type), len + 1);
|
||||
db_printsym((db_addr_t)DBREG_DRX(&d, i), DB_STGY_ANY);
|
||||
db_printf("\n");
|
||||
} else {
|
||||
db_printf(" %-5d disabled\n", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __amd64__
|
||||
/* Sync debug registers when resuming from debugger. */
|
||||
void
|
||||
amd64_db_resume_dbreg(void)
|
||||
{
|
||||
struct dbreg *d;
|
||||
|
||||
switch (PCPU_GET(dbreg_cmd)) {
|
||||
case PC_DBREG_CMD_LOAD:
|
||||
d = (struct dbreg *)PCPU_PTR(dbreg);
|
||||
set_dbregs(NULL, d);
|
||||
PCPU_SET(dbreg_cmd, PC_DBREG_CMD_NONE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user