]> git.sur5r.net Git - openocd/commitdiff
arm_dpm: Add 64bit register handling.
authorDavid Ung <davidu@nvidia.com>
Sat, 17 Jan 2015 02:04:06 +0000 (18:04 -0800)
committerMatthias Welwarsky <matthias.welwarsky@sysgo.com>
Fri, 10 Feb 2017 12:54:01 +0000 (13:54 +0100)
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>
src/target/arm_dpm.c
src/target/arm_dpm.h

index 3b18719a502d6b83dbffa97ab04f38f16ab5b1e3..0c84be5440cef355ceeff28207cd3697afa56e90 100644 (file)
@@ -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) {
index bbcae78535dc3fcaaf315d94c0bdb759fbcf5a13..9cc0304a3dc44325bcb8a995b727c6cf8920e85e 100644 (file)
@@ -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);