ARM: reference DPM defn from v6/v7 arch spec
[fw/openocd] / src / target / arm_dpm.c
index 406e30a289f5f4a4c995e60b64a676ef1eadd476..3c18e6334b26326aa8d8974423d339e91a5de427 100644 (file)
@@ -21,7 +21,7 @@
 #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"
  * Implements various ARM DPM operations using architectural debug registers.
  * These routines layer over core-specific communication methods to cope with
  * implementation differences between cores like ARM1136 and Cortex-A8.
+ *
+ * The "Debug Programmers' Model" (DPM) for ARMv6 and ARMv7 is defined by
+ * Part C (Debug Architecture) of the ARM Architecture Reference Manual,
+ * ARMv7-A and ARMv7-R edition (ARM DDI 0406B).  In OpenOCD, DPM operations
+ * are abstracted through internal programming interfaces to share code and
+ * to minimize needless differences in debug behavior between cores.
  */
 
 /*----------------------------------------------------------------------*/
@@ -277,6 +283,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.
@@ -296,47 +347,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 & ~3, 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
@@ -696,8 +730,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;
@@ -731,7 +766,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;
@@ -869,10 +904,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));