#include "config.h"
#endif
-#include "armv4_5.h" /* REVISIT to become arm.h */
+#include "arm.h"
#include "arm_dpm.h"
#include <jtag/jtag.h>
#include "register.h"
#include "breakpoints.h"
#include "target_type.h"
+#include "arm_opcodes.h"
/**
if (retval != ERROR_OK)
return retval;
- LOG_DEBUG("MRC p%d, %d, r0, c%d, c%d, %d", cpnum, op1, CRn, CRm, op2);
+ LOG_DEBUG("MRC p%d, %d, r0, c%d, c%d, %d", cpnum,
+ (int) op1, (int) CRn,
+ (int) CRm, (int) op2);
/* read coprocessor register into R0; return via DCC */
retval = dpm->instr_read_data_r0(dpm,
if (retval != ERROR_OK)
return retval;
- LOG_DEBUG("MCR p%d, %d, r0, c%d, c%d, %d", cpnum, op1, CRn, CRm, op2);
+ LOG_DEBUG("MCR p%d, %d, r0, c%d, c%d, %d", cpnum,
+ (int) op1, (int) CRn,
+ (int) CRm, (int) op2);
/* read DCC into r0; then write coprocessor register from R0 */
retval = dpm->instr_write_data_r0(dpm,
/* Toggles between recorded core mode (USR, SVC, etc) and a temporary one.
* Routines *must* restore the original mode before returning!!
*/
-static int dpm_modeswitch(struct arm_dpm *dpm, enum armv4_5_mode mode)
+static int dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode)
{
int retval;
uint32_t cpsr;
/* restore previous mode */
- if (mode == ARMV4_5_MODE_ANY)
+ if (mode == ARM_MODE_ANY)
cpsr = buf_get_u32(dpm->arm->cpsr->value, 0, 32);
/* else force to the specified mode */
* is always right except in those broken-by-intent cases.
*/
switch (dpm->arm->core_state) {
- case ARMV4_5_STATE_ARM:
+ case ARM_STATE_ARM:
value -= 8;
break;
- case ARMV4_5_STATE_THUMB:
+ case ARM_STATE_THUMB:
case ARM_STATE_THUMB_EE:
value -= 4;
break;
- case ARMV4_5_STATE_JAZELLE:
+ case ARM_STATE_JAZELLE:
/* core-specific ... ? */
LOG_WARNING("Jazelle PC adjustment unknown");
break;
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
* operation this is called on exit from halting debug state.
*
+ * @param dpm: represents the processor
* @param bpwp: true ensures breakpoints and watchpoints are set,
* false ensures they are cleared
*/
if (retval != ERROR_OK)
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 */
for (unsigned i = 0; i < dpm->nwp; i++) {
struct dpm_wp *dwp = dpm->dwp + i;
struct watchpoint *wp = dwp->wp;
- bool disable;
-
- /* Avoid needless I/O ... leave watchpoints alone
- * unless they're removed, or need updating because
- * 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, dwp->control);
- if (retval != ERROR_OK)
- LOG_ERROR("%s: can't %s HW watchpoint %d",
- target_name(arm->target),
- disable ? "disable" : "enable",
- i);
+ retval = dpm_maybe_update_bpwp(dpm, bpwp, &dwp->bpwp,
+ wp ? &wp->set : NULL);
}
/* NOTE: writes to breakpoint and watchpoint registers might
* actually find anything to do...
*/
do {
- enum armv4_5_mode mode = ARMV4_5_MODE_ANY;
+ enum arm_mode mode = ARM_MODE_ANY;
did_write = false;
/* may need to pick and set a mode */
if (!did_write) {
- enum armv4_5_mode tmode;
+ enum arm_mode tmode;
did_write = true;
mode = tmode = r->mode;
* we "know" core mode is accurate
* since we haven't changed it yet
*/
- if (arm->core_mode == ARMV4_5_MODE_FIQ
- && ARMV4_5_MODE_ANY
+ if (arm->core_mode == ARM_MODE_FIQ
+ && ARM_MODE_ANY
!= mode)
- tmode = ARMV4_5_MODE_USR;
+ tmode = ARM_MODE_USR;
break;
case 16:
/* SPSR */
}
/* REVISIT error checks */
- if (tmode != ARMV4_5_MODE_ANY)
+ if (tmode != ARM_MODE_ANY)
retval = dpm_modeswitch(dpm, tmode);
}
if (r->mode != mode)
* or it's dirty. Must write PC to ensure the return address is
* defined, and must not write it before CPSR.
*/
- retval = dpm_modeswitch(dpm, ARMV4_5_MODE_ANY);
+ retval = dpm_modeswitch(dpm, ARM_MODE_ANY);
arm->cpsr->dirty = false;
retval = dpm_write_reg(dpm, &cache->reg_list[15], 15);
return retval;
}
-/* Returns ARMV4_5_MODE_ANY or temporary mode to use while reading the
+/* Returns ARM_MODE_ANY or temporary mode to use while reading the
* specified register ... works around flakiness from ARM core calls.
* Caller already filtered out SPSR access; mode is never MODE_SYS
* or MODE_ANY.
*/
-static enum armv4_5_mode dpm_mapmode(struct arm *arm,
- unsigned num, enum armv4_5_mode mode)
+static enum arm_mode dpm_mapmode(struct arm *arm,
+ unsigned num, enum arm_mode mode)
{
- enum armv4_5_mode amode = arm->core_mode;
+ enum arm_mode amode = arm->core_mode;
/* don't switch if the mode is already correct */
- if (amode == ARMV4_5_MODE_SYS)
- amode = ARMV4_5_MODE_USR;
+ if (amode == ARM_MODE_SYS)
+ amode = ARM_MODE_USR;
if (mode == amode)
- return ARMV4_5_MODE_ANY;
+ return ARM_MODE_ANY;
switch (num) {
/* don't switch for non-shadowed registers (r0..r7, r15/pc, cpsr) */
break;
/* r8..r12 aren't shadowed for anything except FIQ */
case 8 ... 12:
- if (mode == ARMV4_5_MODE_FIQ)
+ if (mode == ARM_MODE_FIQ)
return mode;
break;
/* r13/sp, and r14/lr are always shadowed */
LOG_WARNING("invalid register #%u", num);
break;
}
- return ARMV4_5_MODE_ANY;
+ return ARM_MODE_ANY;
}
*/
static int arm_dpm_read_core_reg(struct target *target, struct reg *r,
- int regnum, enum armv4_5_mode mode)
+ int regnum, enum arm_mode mode)
{
struct arm_dpm *dpm = target_to_arm(target)->dpm;
int retval;
return ERROR_INVALID_ARGUMENTS;
if (regnum == 16) {
- if (mode != ARMV4_5_MODE_ANY)
+ if (mode != ARM_MODE_ANY)
regnum = 17;
} else
mode = dpm_mapmode(dpm->arm, regnum, mode);
if (retval != ERROR_OK)
return retval;
- if (mode != ARMV4_5_MODE_ANY) {
+ if (mode != ARM_MODE_ANY) {
retval = dpm_modeswitch(dpm, mode);
if (retval != ERROR_OK)
goto fail;
retval = dpm_read_reg(dpm, r, regnum);
/* always clean up, regardless of error */
- if (mode != ARMV4_5_MODE_ANY)
- /* (void) */ dpm_modeswitch(dpm, ARMV4_5_MODE_ANY);
+ if (mode != ARM_MODE_ANY)
+ /* (void) */ dpm_modeswitch(dpm, ARM_MODE_ANY);
fail:
/* (void) */ dpm->finish(dpm);
}
static int arm_dpm_write_core_reg(struct target *target, struct reg *r,
- int regnum, enum armv4_5_mode mode, uint32_t value)
+ int regnum, enum arm_mode mode, uint32_t value)
{
struct arm_dpm *dpm = target_to_arm(target)->dpm;
int retval;
return ERROR_INVALID_ARGUMENTS;
if (regnum == 16) {
- if (mode != ARMV4_5_MODE_ANY)
+ if (mode != ARM_MODE_ANY)
regnum = 17;
} else
mode = dpm_mapmode(dpm->arm, regnum, mode);
if (retval != ERROR_OK)
return retval;
- if (mode != ARMV4_5_MODE_ANY) {
+ if (mode != ARM_MODE_ANY) {
retval = dpm_modeswitch(dpm, mode);
if (retval != ERROR_OK)
goto fail;
retval = dpm_write_reg(dpm, r, regnum);
/* always clean up, regardless of error */
- if (mode != ARMV4_5_MODE_ANY)
- /* (void) */ dpm_modeswitch(dpm, ARMV4_5_MODE_ANY);
+ if (mode != ARM_MODE_ANY)
+ /* (void) */ dpm_modeswitch(dpm, ARM_MODE_ANY);
fail:
/* (void) */ dpm->finish(dpm);
goto done;
do {
- enum armv4_5_mode mode = ARMV4_5_MODE_ANY;
+ enum arm_mode mode = ARM_MODE_ANY;
did_read = false;
/* For R8..R12 when we've entered debug
* state in FIQ mode... patch mode.
*/
- if (mode == ARMV4_5_MODE_ANY)
- mode = ARMV4_5_MODE_USR;
+ if (mode == ARM_MODE_ANY)
+ mode = ARM_MODE_USR;
/* REVISIT error checks */
retval = dpm_modeswitch(dpm, mode);
} while (did_read);
- retval = dpm_modeswitch(dpm, ARMV4_5_MODE_ANY);
+ retval = dpm_modeswitch(dpm, ARM_MODE_ANY);
/* (void) */ dpm->finish(dpm);
done:
return retval;
*/
dpm->dwp[index].wp = wp;
- dpm->dwp[index].control = control;
- dpm->dwp[index].dirty = true;
+ dpm->dwp[index].bpwp.address = addr & ~3;
+ dpm->dwp[index].bpwp.control = control;
+ dpm->dwp[index].bpwp.dirty = true;
/* hardware is updated in write_dirty_registers() */
return ERROR_OK;
for (unsigned i = 0; i < dpm->nwp; i++) {
if (dpm->dwp[i].wp == wp) {
dpm->dwp[i].wp = NULL;
- dpm->dwp[i].dirty = true;
+ dpm->dwp[i].bpwp.dirty = true;
/* hardware is updated in write_dirty_registers() */
retval = ERROR_OK;
void arm_dpm_report_wfar(struct arm_dpm *dpm, uint32_t addr)
{
switch (dpm->arm->core_state) {
- case ARMV4_5_STATE_ARM:
+ case ARM_STATE_ARM:
addr -= 8;
break;
- case ARMV4_5_STATE_THUMB:
+ case ARM_STATE_THUMB:
case ARM_STATE_THUMB_EE:
addr -= 4;
break;
- case ARMV4_5_STATE_JAZELLE:
+ case ARM_STATE_JAZELLE:
/* ?? */
break;
}
/*----------------------------------------------------------------------*/
+/*
+ * Other debug and support utilities
+ */
+
+void arm_dpm_report_dscr(struct arm_dpm *dpm, uint32_t dscr)
+{
+ struct target *target = dpm->arm->target;
+
+ dpm->dscr = dscr;
+
+ /* Examine debug reason */
+ switch (DSCR_ENTRY(dscr)) {
+ case 6: /* Data abort (v6 only) */
+ case 7: /* Prefetch abort (v6 only) */
+ /* FALL THROUGH -- assume a v6 core in abort mode */
+ case 0: /* HALT request from debugger */
+ case 4: /* EDBGRQ */
+ target->debug_reason = DBG_REASON_DBGRQ;
+ break;
+ case 1: /* HW breakpoint */
+ case 3: /* SW BKPT */
+ case 5: /* vector catch */
+ target->debug_reason = DBG_REASON_BREAKPOINT;
+ break;
+ case 2: /* asynch watchpoint */
+ case 10: /* precise watchpoint */
+ target->debug_reason = DBG_REASON_WATCHPOINT;
+ break;
+ default:
+ target->debug_reason = DBG_REASON_UNDEFINED;
+ break;
+ }
+}
+
+/*----------------------------------------------------------------------*/
+
/*
* Setup and management support.
*/
arm->read_core_reg = arm_dpm_read_core_reg;
arm->write_core_reg = arm_dpm_write_core_reg;
- cache = armv4_5_build_reg_cache(target, arm);
+ cache = arm_build_reg_cache(target, arm);
if (!cache)
return ERROR_FAIL;
if (dpm->bpwp_disable) {
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);
- 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);
+ }
} else
LOG_WARNING("%s: can't disable breakpoints and watchpoints",
target_name(dpm->arm->target));