aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Ung <davidu@nvidia.com>2015-01-16 18:04:06 -0800
committerMatthias Welwarsky <matthias.welwarsky@sysgo.com>2017-02-10 13:54:01 +0100
commit84a0bb4a3c3c883b22f95abc7f1428faef3936f1 (patch)
tree4811a836e84884cdf179566afbb6235dfdc01c8c
parent9cbfc9feb35ce0fcc9bb67a4d83b02100ee65f88 (diff)
arm_dpm: Add 64bit register handling.
Add various function to read/write ARMv8 registers. Change-Id: I16f2829bdd0e87b050a51e414ff675d5c21bcbae Signed-off-by: David Ung <david.ung.42@gmail.com> Signed-off-by: Matthias Welwarsky <matthias.welwarsky@sysgo.com>
-rw-r--r--src/target/arm_dpm.c333
-rw-r--r--src/target/arm_dpm.h10
2 files changed, 257 insertions, 86 deletions
diff --git a/src/target/arm_dpm.c b/src/target/arm_dpm.c
index 3b18719..0c84be5 100644
--- a/src/target/arm_dpm.c
+++ b/src/target/arm_dpm.c
@@ -130,11 +130,11 @@ int dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode)
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:
@@ -190,10 +190,57 @@ static int dpm_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
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) {
@@ -243,19 +290,46 @@ static int dpm_write_pc_core_state(struct arm_dpm *dpm, struct reg *r)
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)
@@ -270,16 +344,22 @@ int arm_dpm_read_current_registers(struct arm_dpm *dpm)
}
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;
@@ -300,6 +380,23 @@ fail:
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.
@@ -348,59 +445,14 @@ done:
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
@@ -436,20 +488,20 @@ int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp)
/* 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 */
@@ -463,8 +515,8 @@ int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp)
continue;
retval = dpm_write_reg(dpm,
- &cache->reg_list[i],
- regnum);
+ &cache->reg_list[i],
+ regnum);
if (retval != ERROR_OK)
goto done;
}
@@ -504,6 +556,106 @@ int arm_dpm_write_dirty_registers(struct arm_dpm *dpm, bool bpwp)
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;
@@ -973,8 +1125,8 @@ int arm_dpm_setup(struct arm_dpm *dpm)
/* 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) {
@@ -999,12 +1151,21 @@ int arm_dpm_setup(struct arm_dpm *dpm)
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) {
diff --git a/src/target/arm_dpm.h b/src/target/arm_dpm.h
index bbcae78..9cc0304 100644
--- a/src/target/arm_dpm.h
+++ b/src/target/arm_dpm.h
@@ -86,10 +86,19 @@ struct arm_dpm {
int (*instr_read_data_dcc)(struct arm_dpm *,
uint32_t opcode, uint32_t *data);
+ int (*instr_read_data_dcc_64)(struct arm_dpm *,
+ uint32_t opcode, uint64_t *data);
+
/** Runs one instruction, reading data from r0 after execution. */
int (*instr_read_data_r0)(struct arm_dpm *,
uint32_t opcode, uint32_t *data);
+ int (*instr_read_data_r0_64)(struct arm_dpm *,
+ uint32_t opcode, uint64_t *data);
+
+ struct reg *(*arm_reg_current)(struct arm *arm,
+ unsigned regnum);
+
/* BREAKPOINT/WATCHPOINT SUPPORT */
/**
@@ -131,6 +140,7 @@ int arm_dpm_setup(struct arm_dpm *dpm);
int arm_dpm_initialize(struct arm_dpm *dpm);
int arm_dpm_read_current_registers(struct arm_dpm *);
+int arm_dpm_read_current_registers_64(struct arm_dpm *);
int dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode);