ARM DPM: support updating HW breakpoints

Abstract the DPM breakpoint and watchpoint data structures to
have a shared core for housekeeping.

Abstract the code updating the watchpoint registers so that it
can be used to update breakpoint registers.  Then do so, when
something has set up the breakpoint state used by this code.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
__archive__
David Brownell 2009-12-11 15:26:10 -08:00
parent 838d41af29
commit 75c706cc04
2 changed files with 75 additions and 52 deletions

View File

@ -277,6 +277,51 @@ fail:
return retval; return retval;
} }
/* Avoid needless I/O ... leave breakpoints and watchpoints alone
* unless they're removed, or need updating because of single-stepping
* or running debugger code.
*/
static int dpm_maybe_update_bpwp(struct arm_dpm *dpm, bool bpwp,
struct dpm_bpwp *xp, int *set_p)
{
int retval = ERROR_OK;
bool disable;
if (!set_p) {
if (!xp->dirty)
goto done;
xp->dirty = false;
/* removed or startup; we must disable it */
disable = true;
} else if (bpwp) {
if (!xp->dirty)
goto done;
/* disabled, but we must set it */
xp->dirty = disable = false;
*set_p = true;
} else {
if (!*set_p)
goto done;
/* set, but we must temporarily disable it */
xp->dirty = disable = true;
*set_p = false;
}
if (disable)
retval = dpm->bpwp_disable(dpm, xp->number);
else
retval = dpm->bpwp_enable(dpm, xp->number,
xp->address, xp->control);
if (retval != ERROR_OK)
LOG_ERROR("%s: can't %s HW bp/wp %d",
disable ? "disable" : "enable",
target_name(dpm->arm->target),
xp->number);
done:
return retval;
}
/** /**
* Writes all modified core registers for all processor modes. In normal * Writes all modified core registers for all processor modes. In normal
* operation this is called on exit from halting debug state. * operation this is called on exit from halting debug state.
@ -296,47 +341,22 @@ int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp)
if (retval != ERROR_OK) if (retval != ERROR_OK)
goto done; goto done;
/* enable/disable hardware breakpoints */
for (unsigned i = 0; i < dpm->nbp; i++) {
struct dpm_bp *dbp = dpm->dbp + i;
struct breakpoint *bp = dbp->bp;
retval = dpm_maybe_update_bpwp(dpm, bpwp, &dbp->bpwp,
bp ? &bp->set : NULL);
}
/* enable/disable watchpoints */ /* enable/disable watchpoints */
for (unsigned i = 0; i < dpm->nwp; i++) { for (unsigned i = 0; i < dpm->nwp; i++) {
struct dpm_wp *dwp = dpm->dwp + i; struct dpm_wp *dwp = dpm->dwp + i;
struct watchpoint *wp = dwp->wp; struct watchpoint *wp = dwp->wp;
bool disable;
/* Avoid needless I/O ... leave watchpoints alone retval = dpm_maybe_update_bpwp(dpm, bpwp, &dwp->bpwp,
* unless they're removed, or need updating because wp ? &wp->set : NULL);
* of single-stepping or running debugger code.
*/
if (!wp) {
if (!dwp->dirty)
continue;
dwp->dirty = false;
/* removed or startup; we must disable it */
disable = true;
} else if (bpwp) {
if (!dwp->dirty)
continue;
/* disabled, but we must set it */
dwp->dirty = disable = false;
wp->set = true;
} else {
if (!wp->set)
continue;
/* set, but we must temporarily disable it */
dwp->dirty = disable = true;
wp->set = false;
}
if (disable)
retval = dpm->bpwp_disable(dpm, 16 + i);
else
retval = dpm->bpwp_enable(dpm, 16 + i,
wp->address & ~3, dwp->control);
if (retval != ERROR_OK)
LOG_ERROR("%s: can't %s HW watchpoint %d",
target_name(arm->target),
disable ? "disable" : "enable",
i);
} }
/* NOTE: writes to breakpoint and watchpoint registers might /* NOTE: writes to breakpoint and watchpoint registers might
@ -696,8 +716,9 @@ static int dpm_watchpoint_setup(struct arm_dpm *dpm, unsigned index,
*/ */
dpm->dwp[index].wp = wp; dpm->dwp[index].wp = wp;
dpm->dwp[index].control = control; dpm->dwp[index].bpwp.address = addr & ~3;
dpm->dwp[index].dirty = true; dpm->dwp[index].bpwp.control = control;
dpm->dwp[index].bpwp.dirty = true;
/* hardware is updated in write_dirty_registers() */ /* hardware is updated in write_dirty_registers() */
return ERROR_OK; return ERROR_OK;
@ -731,7 +752,7 @@ static int dpm_remove_watchpoint(struct target *target, struct watchpoint *wp)
for (unsigned i = 0; i < dpm->nwp; i++) { for (unsigned i = 0; i < dpm->nwp; i++) {
if (dpm->dwp[i].wp == wp) { if (dpm->dwp[i].wp == wp) {
dpm->dwp[i].wp = NULL; dpm->dwp[i].wp = NULL;
dpm->dwp[i].dirty = true; dpm->dwp[i].bpwp.dirty = true;
/* hardware is updated in write_dirty_registers() */ /* hardware is updated in write_dirty_registers() */
retval = ERROR_OK; retval = ERROR_OK;
@ -869,10 +890,14 @@ int arm_dpm_initialize(struct arm_dpm *dpm)
if (dpm->bpwp_disable) { if (dpm->bpwp_disable) {
unsigned i; unsigned i;
for (i = 0; i < dpm->nbp; i++) for (i = 0; i < dpm->nbp; i++) {
dpm->dbp[i].bpwp.number = i;
(void) dpm->bpwp_disable(dpm, i); (void) dpm->bpwp_disable(dpm, i);
for (i = 0; i < dpm->nwp; i++) }
for (i = 0; i < dpm->nwp; i++) {
dpm->dwp[i].bpwp.number = 16 + i;
(void) dpm->bpwp_disable(dpm, 16 + i); (void) dpm->bpwp_disable(dpm, 16 + i);
}
} else } else
LOG_WARNING("%s: can't disable breakpoints and watchpoints", LOG_WARNING("%s: can't disable breakpoints and watchpoints",
target_name(dpm->arm->target)); target_name(dpm->arm->target));

View File

@ -31,24 +31,22 @@
* registers are compatible. * registers are compatible.
*/ */
struct dpm_bp { struct dpm_bpwp {
struct breakpoint *bp; unsigned number;
/* bp->address == breakpoint value register uint32_t address;
* control == breakpoint control register
*/
uint32_t control; uint32_t control;
/* true if hardware state needs flushing */ /* true if hardware state needs flushing */
bool dirty; bool dirty;
}; };
struct dpm_bp {
struct breakpoint *bp;
struct dpm_bpwp bpwp;
};
struct dpm_wp { struct dpm_wp {
struct watchpoint *wp; struct watchpoint *wp;
/* wp->address == watchpoint value register struct dpm_bpwp bpwp;
* control == watchpoint control register
*/
uint32_t control;
/* true if hardware state needs flushing */
bool dirty;
}; };
/** /**