+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Breakpoint and Watchpoint support.
+ *
+ * Hardware {break,watch}points are usually left active, to minimize
+ * debug entry/exit costs. When they are set or cleared, it's done in
+ * batches. Also, DPM-conformant hardware can update debug registers
+ * regardless of whether the CPU is running or halted ... though that
+ * fact isn't currently leveraged.
+ */
+
+static int dpm_bpwp_setup(struct arm_dpm *dpm, struct dpm_bpwp *xp,
+ uint32_t addr, uint32_t length)
+{
+ uint32_t control;
+
+ control = (1 << 0) /* enable */
+ | (3 << 1); /* both user and privileged access */
+
+ /* Match 1, 2, or all 4 byte addresses in this word.
+ *
+ * FIXME: v7 hardware allows lengths up to 2 GB for BP and WP.
+ * Support larger length, when addr is suitably aligned. In
+ * particular, allow watchpoints on 8 byte "double" values.
+ *
+ * REVISIT allow watchpoints on unaligned 2-bit values; and on
+ * v7 hardware, unaligned 4-byte ones too.
+ */
+ switch (length) {
+ case 1:
+ control |= (1 << (addr & 3)) << 5;
+ break;
+ case 2:
+ /* require 2-byte alignment */
+ if (!(addr & 1)) {
+ control |= (3 << (addr & 2)) << 5;
+ break;
+ }
+ /* FALL THROUGH */
+ case 4:
+ /* require 4-byte alignment */
+ if (!(addr & 3)) {
+ control |= 0xf << 5;
+ break;
+ }
+ /* FALL THROUGH */
+ default:
+ LOG_ERROR("unsupported {break,watch}point length/alignment");
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ }
+
+ /* other shared control bits:
+ * bits 15:14 == 0 ... both secure and nonsecure states (v6.1+ only)
+ * bit 20 == 0 ... not linked to a context ID
+ * bit 28:24 == 0 ... not ignoring N LSBs (v7 only)
+ */
+
+ xp->address = addr & ~3;
+ xp->control = control;
+ xp->dirty = true;
+
+ LOG_DEBUG("BPWP: addr %8.8" PRIx32 ", control %" PRIx32 ", number %d",
+ xp->address, control, xp->number);
+
+ /* hardware is updated in write_dirty_registers() */
+ return ERROR_OK;
+}
+
+static int dpm_add_breakpoint(struct target *target, struct breakpoint *bp)
+{
+ struct arm *arm = target_to_arm(target);
+ struct arm_dpm *dpm = arm->dpm;
+ int retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+
+ if (bp->length < 2)
+ return ERROR_COMMAND_SYNTAX_ERROR;
+ if (!dpm->bpwp_enable)
+ return retval;
+
+ /* FIXME we need a generic solution for software breakpoints. */
+ if (bp->type == BKPT_SOFT)
+ LOG_DEBUG("using HW bkpt, not SW...");
+
+ for (unsigned i = 0; i < dpm->nbp; i++) {
+ if (!dpm->dbp[i].bp) {
+ retval = dpm_bpwp_setup(dpm, &dpm->dbp[i].bpwp,
+ bp->address, bp->length);
+ if (retval == ERROR_OK)
+ dpm->dbp[i].bp = bp;
+ break;
+ }
+ }
+
+ return retval;
+}
+
+static int dpm_remove_breakpoint(struct target *target, struct breakpoint *bp)
+{
+ struct arm *arm = target_to_arm(target);
+ struct arm_dpm *dpm = arm->dpm;
+ int retval = ERROR_COMMAND_SYNTAX_ERROR;
+
+ for (unsigned i = 0; i < dpm->nbp; i++) {
+ if (dpm->dbp[i].bp == bp) {
+ dpm->dbp[i].bp = NULL;
+ dpm->dbp[i].bpwp.dirty = true;
+
+ /* hardware is updated in write_dirty_registers() */
+ retval = ERROR_OK;
+ break;
+ }
+ }
+
+ return retval;
+}
+
+static int dpm_watchpoint_setup(struct arm_dpm *dpm, unsigned index_t,
+ struct watchpoint *wp)
+{
+ int retval;
+ struct dpm_wp *dwp = dpm->dwp + index_t;
+ uint32_t control;
+
+ /* this hardware doesn't support data value matching or masking */
+ if (wp->value || wp->mask != ~(uint32_t)0) {
+ LOG_DEBUG("watchpoint values and masking not supported");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ retval = dpm_bpwp_setup(dpm, &dwp->bpwp, wp->address, wp->length);
+ if (retval != ERROR_OK)
+ return retval;
+
+ control = dwp->bpwp.control;
+ switch (wp->rw) {
+ case WPT_READ:
+ control |= 1 << 3;
+ break;
+ case WPT_WRITE:
+ control |= 2 << 3;
+ break;
+ case WPT_ACCESS:
+ control |= 3 << 3;
+ break;
+ }
+ dwp->bpwp.control = control;
+
+ dpm->dwp[index_t].wp = wp;
+
+ return retval;
+}
+
+static int dpm_add_watchpoint(struct target *target, struct watchpoint *wp)
+{
+ struct arm *arm = target_to_arm(target);
+ struct arm_dpm *dpm = arm->dpm;
+ int retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+
+ if (dpm->bpwp_enable) {
+ for (unsigned i = 0; i < dpm->nwp; i++) {
+ if (!dpm->dwp[i].wp) {
+ retval = dpm_watchpoint_setup(dpm, i, wp);
+ break;
+ }
+ }
+ }
+
+ return retval;
+}
+
+static int dpm_remove_watchpoint(struct target *target, struct watchpoint *wp)
+{
+ struct arm *arm = target_to_arm(target);
+ struct arm_dpm *dpm = arm->dpm;
+ int retval = ERROR_COMMAND_SYNTAX_ERROR;
+
+ for (unsigned i = 0; i < dpm->nwp; i++) {
+ if (dpm->dwp[i].wp == wp) {
+ dpm->dwp[i].wp = NULL;
+ dpm->dwp[i].bpwp.dirty = true;
+
+ /* hardware is updated in write_dirty_registers() */
+ retval = ERROR_OK;
+ break;
+ }
+ }
+
+ return retval;
+}
+
+void arm_dpm_report_wfar(struct arm_dpm *dpm, uint32_t addr)
+{
+ switch (dpm->arm->core_state) {
+ case ARM_STATE_ARM:
+ addr -= 8;
+ break;
+ case ARM_STATE_THUMB:
+ case ARM_STATE_THUMB_EE:
+ addr -= 4;
+ break;
+ case ARM_STATE_JAZELLE:
+ case ARM_STATE_AARCH64:
+ /* ?? */
+ break;
+ }
+ dpm->wp_pc = addr;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * 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 DSCR_ENTRY_HALT_REQ: /* HALT request from debugger */
+ case DSCR_ENTRY_EXT_DBG_REQ: /* EDBGRQ */
+ target->debug_reason = DBG_REASON_DBGRQ;
+ break;
+ case DSCR_ENTRY_BREAKPOINT: /* HW breakpoint */
+ case DSCR_ENTRY_BKPT_INSTR: /* vector catch */
+ target->debug_reason = DBG_REASON_BREAKPOINT;
+ break;
+ case DSCR_ENTRY_IMPRECISE_WATCHPT: /* asynch watchpoint */
+ case DSCR_ENTRY_PRECISE_WATCHPT:/* precise watchpoint */
+ target->debug_reason = DBG_REASON_WATCHPOINT;
+ break;
+ default:
+ target->debug_reason = DBG_REASON_UNDEFINED;
+ break;
+ }
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Setup and management support.
+ */
+