arm_dpm: Add 64bit register handling.
[openocd.git] / src / target / arm_dpm.c
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) {

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)