return retval;
}
-/* just read the register -- rely on the core mode being right */
-static int dpm_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
+
+static int dpm_read_reg32(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
{
uint32_t value;
- int retval;
+ int retval = ERROR_FAIL;
switch (regnum) {
case 0 ... 14:
return retval;
}
-/* just write the register -- rely on the core mode being right */
-static int dpm_write_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
+static int dpm_read_reg64(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
{
- int retval;
+ uint64_t value;
+ uint32_t i;
+ int retval = ERROR_FAIL;
+
+ switch (regnum) {
+ case 0 ... 30:
+ i = 0xd5130400 + regnum; /* msr dbgdtr_el0,reg */
+ retval = dpm->instr_read_data_dcc_64(dpm, i, &value);
+ break;
+ case 31: /* SP */
+ i = 0x910003e0;
+ retval = dpm->instr_read_data_r0_64(dpm, i, &value);
+ break;
+ case 32: /* PC */
+ i = 0xd53b4520;
+ retval = dpm->instr_read_data_r0_64(dpm, i, &value);
+ break;
+ case 33: /* CPSR */
+ i = 0xd53b4500;
+ retval = dpm->instr_read_data_r0_64(dpm, i, &value);
+ break;
+
+ default:
+ break;
+ }
+
+ if (retval == ERROR_OK) {
+ buf_set_u64(r->value, 0, 64, value);
+ r->valid = true;
+ r->dirty = false;
+ LOG_DEBUG("READ: %s, %16.16llx", r->name, (long long)value);
+ }
+
+ return retval;
+}
+
+
+/* just read the register -- rely on the core mode being right */
+static int dpm_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
+{
+ if (r->size == 64)
+ return dpm_read_reg64(dpm, r, regnum);
+ else
+ return dpm_read_reg32(dpm, r, regnum);
+}
+
+static int dpm_write_reg32(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
+{
+ int retval = ERROR_FAIL;
uint32_t value = buf_get_u32(r->value, 0, 32);
switch (regnum) {
return dpm->instr_write_data_r0(dpm, ARMV4_5_BX(0), value);
}
-/**
- * Read basic registers of the the current context: R0 to R15, and CPSR;
- * sets the core mode (such as USR or IRQ) and state (such as ARM or Thumb).
- * In normal operation this is called on entry to halting debug state,
- * possibly after some other operations supporting restore of debug state
- * or making sure the CPU is fully idle (drain write buffer, etc).
- */
-int arm_dpm_read_current_registers(struct arm_dpm *dpm)
+static int dpm_write_reg64(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
+{
+ int retval = ERROR_FAIL;
+ uint32_t i;
+ uint64_t value = buf_get_u64(r->value, 0, 64);
+
+ switch (regnum) {
+ case 0 ... 30:
+ i = 0xd5330400 + regnum;
+ retval = dpm->instr_write_data_dcc(dpm, i, value);
+ break;
+
+ default:
+ break;
+ }
+
+ if (retval == ERROR_OK) {
+ r->dirty = false;
+ LOG_DEBUG("WRITE: %s, %16.16llx", r->name, (unsigned long long)value);
+ }
+
+ return retval;
+}
+
+/* just write the register -- rely on the core mode being right */
+static int dpm_write_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
+{
+ if (r->size == 64)
+ return dpm_write_reg64(dpm, r, regnum);
+ else
+ return dpm_write_reg32(dpm, r, regnum);
+}
+
+static int arm_dpm_read_current_registers_i(struct arm_dpm *dpm)
{
struct arm *arm = dpm->arm;
- uint32_t cpsr;
+ uint32_t cpsr, instr, core_regs;
int retval;
struct reg *r;
+ enum arm_state core_state = arm->core_state;
retval = dpm->prepare(dpm);
if (retval != ERROR_OK)
}
r->dirty = true;
- retval = dpm->instr_read_data_r0(dpm, ARMV4_5_MRS(0, 0), &cpsr);
+ if (core_state == ARM_STATE_AARCH64)
+ instr = 0xd53b4500; /* mrs x0, dspsr_el0 */
+ else
+ instr = ARMV4_5_MRS(0, 0);
+ retval = dpm->instr_read_data_r0(dpm, instr, &cpsr);
if (retval != ERROR_OK)
goto fail;
/* update core mode and state, plus shadow mapping for R8..R14 */
arm_set_cpsr(arm, cpsr);
+ core_regs = arm->core_cache->num_regs;
+
/* REVISIT we can probably avoid reading R1..R14, saving time... */
- for (unsigned i = 1; i < 16; i++) {
- r = arm_reg_current(arm, i);
+ for (unsigned i = 1; i < core_regs; i++) {
+ r = dpm->arm_reg_current(arm, i);
if (r->valid)
continue;
return retval;
}
+/**
+ * Read basic registers of the the current context: R0 to R15, and CPSR;
+ * sets the core mode (such as USR or IRQ) and state (such as ARM or Thumb).
+ * In normal operation this is called on entry to halting debug state,
+ * possibly after some other operations supporting restore of debug state
+ * or making sure the CPU is fully idle (drain write buffer, etc).
+ */
+int arm_dpm_read_current_registers(struct arm_dpm *dpm)
+{
+ return arm_dpm_read_current_registers_i(dpm);
+}
+
+int arm_dpm_read_current_registers_64(struct arm_dpm *dpm)
+{
+ return arm_dpm_read_current_registers_i(dpm);
+}
+
/* 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_add_breakpoint(struct target *target, struct breakpoint *bp);
-/**
- * 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
- */
-int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp)
+
+static int arm_dpm_write_dirty_registers_32(struct arm_dpm *dpm)
{
struct arm *arm = dpm->arm;
struct reg_cache *cache = arm->core_cache;
int retval;
bool did_write;
- retval = dpm->prepare(dpm);
- 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 (arm->target->type->add_breakpoint == dpm_add_breakpoint) {
- 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);
- if (retval != ERROR_OK)
- goto done;
- }
- }
-
- /* enable/disable watchpoints */
- for (unsigned i = 0; i < dpm->nwp; i++) {
- struct dpm_wp *dwp = dpm->dwp + i;
- struct watchpoint *wp = dwp->wp;
-
- retval = dpm_maybe_update_bpwp(dpm, bpwp, &dwp->bpwp,
- wp ? &wp->set : NULL);
- if (retval != ERROR_OK)
- goto done;
- }
-
- /* NOTE: writes to breakpoint and watchpoint registers might
- * be queued, and need (efficient/batched) flushing later.
- */
-
/* Scan the registers until we find one that's both dirty and
* eligible for flushing. Flush that and everything else that
* shares the same core mode setting. Typically this won't
/* cope with special cases */
switch (regnum) {
- case 8 ... 12:
- /* r8..r12 "anything but FIQ" case;
- * we "know" core mode is accurate
- * since we haven't changed it yet
- */
- if (arm->core_mode == ARM_MODE_FIQ
- && ARM_MODE_ANY
- != mode)
- tmode = ARM_MODE_USR;
- break;
- case 16:
- /* SPSR */
- regnum++;
- break;
+ case 8 ... 12:
+ /* r8..r12 "anything but FIQ" case;
+ * we "know" core mode is accurate
+ * since we haven't changed it yet
+ */
+ if (arm->core_mode == ARM_MODE_FIQ
+ && ARM_MODE_ANY
+ != mode)
+ tmode = ARM_MODE_USR;
+ break;
+ case 16:
+ /* SPSR */
+ regnum++;
+ break;
}
/* REVISIT error checks */
continue;
retval = dpm_write_reg(dpm,
- &cache->reg_list[i],
- regnum);
+ &cache->reg_list[i],
+ regnum);
if (retval != ERROR_OK)
goto done;
}
goto done;
cache->reg_list[0].dirty = false;
+done:
+ return retval;
+}
+
+static int arm_dpm_write_dirty_registers_64(struct arm_dpm *dpm)
+{
+ struct arm *arm = dpm->arm;
+ struct reg_cache *cache = arm->core_cache;
+ int retval;
+
+ /* Scan the registers until we find one that's both dirty and
+ * eligible for flushing. Flush that and everything else that
+ * shares the same core mode setting. Typically this won't
+ * actually find anything to do...
+ */
+
+ /* check everything except our scratch register R0 */
+ for (unsigned i = 1; i < 32; i++) {
+ struct arm_reg *r;
+ unsigned regnum;
+
+ if (!cache->reg_list[i].dirty)
+ continue;
+
+ r = cache->reg_list[i].arch_info;
+ regnum = r->num;
+ retval = dpm_write_reg(dpm,
+ &cache->reg_list[i],
+ regnum);
+ if (retval != ERROR_OK)
+ goto done;
+ }
+
+ /* flush R0 -- it's *very* dirty by now */
+ retval = dpm_write_reg(dpm, &cache->reg_list[0], 0);
+ if (retval != ERROR_OK)
+ goto done;
+ cache->reg_list[0].dirty = false;
+
+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
+ */
+int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp)
+{
+ struct arm *arm = dpm->arm;
+ struct reg_cache *cache = arm->core_cache;
+ int retval;
+
+ retval = dpm->prepare(dpm);
+ 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 (arm->target->type->add_breakpoint == dpm_add_breakpoint) {
+ 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);
+ if (retval != ERROR_OK)
+ goto done;
+ }
+ }
+
+ /* enable/disable watchpoints */
+ for (unsigned i = 0; i < dpm->nwp; i++) {
+ struct dpm_wp *dwp = dpm->dwp + i;
+ struct watchpoint *wp = dwp->wp;
+
+ retval = dpm_maybe_update_bpwp(dpm, bpwp, &dwp->bpwp,
+ wp ? &wp->set : NULL);
+ if (retval != ERROR_OK)
+ goto done;
+ }
+
+ /* NOTE: writes to breakpoint and watchpoint registers might
+ * be queued, and need (efficient/batched) flushing later.
+ */
+
+ if (cache->reg_list[0].size == 64)
+ retval = arm_dpm_write_dirty_registers_64(dpm);
+ else
+ retval = arm_dpm_write_dirty_registers_32(dpm);
+
/* (void) */ dpm->finish(dpm);
done:
return retval;
/* register access setup */
arm->full_context = arm_dpm_full_context;
- arm->read_core_reg = arm_dpm_read_core_reg;
- arm->write_core_reg = arm_dpm_write_core_reg;
+ arm->read_core_reg = arm->read_core_reg ? : arm_dpm_read_core_reg;
+ arm->write_core_reg = arm->write_core_reg ? : arm_dpm_write_core_reg;
/* avoid duplicating the register cache */
if (arm->core_cache == NULL) {
target->type->add_watchpoint = dpm_add_watchpoint;
target->type->remove_watchpoint = dpm_remove_watchpoint;
+
+ if (dpm->arm_reg_current == 0)
+ dpm->arm_reg_current = arm_reg_current;
+
/* FIXME add vector catch support */
- dpm->nbp = 1 + ((dpm->didr >> 24) & 0xf);
- dpm->dbp = calloc(dpm->nbp, sizeof *dpm->dbp);
+ if (arm->core_state == ARM_STATE_AARCH64) {
+ dpm->nbp = 1 + ((dpm->didr >> 24) & 0xf);
+ dpm->nwp = 1 + ((dpm->didr >> 28) & 0xf);
+ } else {
+ dpm->nbp = 1 + ((dpm->didr >> 12) & 0xf);
+ dpm->nwp = 1 + ((dpm->didr >> 20) & 0xf);
+ }
- dpm->nwp = 1 + ((dpm->didr >> 28) & 0xf);
+ dpm->dbp = calloc(dpm->nbp, sizeof *dpm->dbp);
dpm->dwp = calloc(dpm->nwp, sizeof *dpm->dwp);
if (!dpm->dbp || !dpm->dwp) {