gdb_server: handle stepi/continue packet while target is running with more grace
[fw/openocd] / src / target / arm_dpm.c
index 7cc48abfc36635ae13043950db086745f94b58fa..4bd22ffbf53cc7fa3807a8387d9728d60c015df2 100644 (file)
 #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"
 
 
 /**
@@ -102,7 +103,7 @@ static int dpm_mcr(struct target *target, int cpnum,
 /* 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;
@@ -276,6 +277,51 @@ fail:
        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.
@@ -295,47 +341,30 @@ int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp)
        if (retval != ERROR_OK)
                goto done;
 
+       /* If we're managing hardware breakpoints for this core, enable
+        * or disable them as requested.
+        *
+        * REVISIT We don't yet manage them for ANY cores.  Eventually
+        * we should be able to assume we handle them; but until then,
+        * cope with the hand-crafted breakpoint code.
+        */
+       if (0) {
+               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
@@ -348,7 +377,7 @@ int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp)
         * actually find anything to do...
         */
        do {
-               enum armv4_5_mode mode = ARM_MODE_ANY;
+               enum arm_mode mode = ARM_MODE_ANY;
 
                did_write = false;
 
@@ -370,7 +399,7 @@ int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp)
 
                        /* 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;
@@ -432,10 +461,10 @@ done:
  * 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 == ARM_MODE_SYS)
@@ -473,7 +502,7 @@ static enum armv4_5_mode dpm_mapmode(struct arm *arm,
  */
 
 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;
@@ -513,7 +542,7 @@ fail:
 }
 
 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;
@@ -566,7 +595,7 @@ static int arm_dpm_full_context(struct target *target)
                goto done;
 
        do {
-               enum armv4_5_mode mode = ARM_MODE_ANY;
+               enum arm_mode mode = ARM_MODE_ANY;
 
                did_read = false;
 
@@ -695,8 +724,9 @@ static int dpm_watchpoint_setup(struct arm_dpm *dpm, unsigned index,
         */
 
        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;
@@ -730,7 +760,7 @@ static int dpm_remove_watchpoint(struct target *target, struct watchpoint *wp)
        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;
@@ -819,7 +849,7 @@ int arm_dpm_setup(struct arm_dpm *dpm)
        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;
 
@@ -868,10 +898,14 @@ int arm_dpm_initialize(struct arm_dpm *dpm)
        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));