aarch64: introduce dpm extension for ARMv8 42/3742/5
authorMatthias Welwarsky <matthias.welwarsky@sysgo.com>
Sat, 3 Sep 2016 21:20:58 +0000 (23:20 +0200)
committerMatthias Welwarsky <matthias.welwarsky@sysgo.com>
Fri, 10 Feb 2017 13:01:38 +0000 (14:01 +0100)
Add or move ARMv8 related dpm function to their own source module

Change-Id: Id93d50be0b8635bd40ddb2a74fe8746ff840d736
Signed-off-by: Matthias Welwarsky <matthias.welwarsky@sysgo.com>
src/target/Makefile.am
src/target/aarch64.c
src/target/arm.h
src/target/arm_dpm.c
src/target/arm_dpm.h
src/target/armv8.c
src/target/armv8.h
src/target/armv8_dpm.c [new file with mode: 0644]
src/target/armv8_dpm.h [new file with mode: 0644]

index e23577660950682c519b190c07548ef77e23e725..c20ac0f03a99b1bca8f0c18dfb51c8025cbddf6e 100644 (file)
@@ -74,6 +74,7 @@ ARMV7_SRC = \
        %D%/ls1_sap.c
 
 ARMV8_SRC = \
+       %D%/armv8_dpm.c \
        %D%/aarch64.c \
        %D%/armv8.c
 
@@ -155,6 +156,7 @@ INTEL_IA32_SRC = \
        %D%/armv7m.h \
        %D%/armv7m_trace.h \
        %D%/armv8.h \
+       %D%/armv8_dpm.h \
        %D%/armv8_opcodes.h \
        %D%/avrt.h \
        %D%/dsp563xx.h \
index 78d19731e757a65790fd6d5f736dbca5de7f8c4f..be02eb8509497cb87c01851294998c364e7d537f 100644 (file)
@@ -376,6 +376,18 @@ static int aarch64_dpm_finish(struct arm_dpm *dpm)
        return ERROR_OK;
 }
 
+static int aarch64_instr_execute(struct arm_dpm *dpm,
+       uint32_t opcode)
+{
+       struct aarch64_common *a8 = dpm_to_a8(dpm);
+       uint32_t dscr = DSCR_ITE;
+
+       return aarch64_exec_opcode(
+                       a8->armv8_common.arm.target,
+                       opcode,
+                       &dscr);
+}
+
 static int aarch64_instr_write_data_dcc(struct arm_dpm *dpm,
        uint32_t opcode, uint32_t data)
 {
@@ -638,6 +650,7 @@ static int aarch64_dpm_setup(struct aarch64_common *a8, uint32_t debug)
        dpm->prepare = aarch64_dpm_prepare;
        dpm->finish = aarch64_dpm_finish;
 
+       dpm->instr_execute = aarch64_instr_execute;
        dpm->instr_write_data_dcc = aarch64_instr_write_data_dcc;
        dpm->instr_write_data_dcc_64 = aarch64_instr_write_data_dcc_64;
        dpm->instr_write_data_r0 = aarch64_instr_write_data_r0;
@@ -654,9 +667,9 @@ static int aarch64_dpm_setup(struct aarch64_common *a8, uint32_t debug)
        dpm->bpwp_enable = aarch64_bpwp_enable;
        dpm->bpwp_disable = aarch64_bpwp_disable;
 
-       retval = arm_dpm_setup(dpm);
+       retval = armv8_dpm_setup(dpm);
        if (retval == ERROR_OK)
-               retval = arm_dpm_initialize(dpm);
+               retval = armv8_dpm_initialize(dpm);
 
        return retval;
 }
@@ -879,6 +892,8 @@ static int aarch64_internal_restore(struct target *target, int current,
         */
        switch (arm->core_state) {
                case ARM_STATE_ARM:
+                       resume_pc &= 0xFFFFFFFC;
+                       break;
                case ARM_STATE_AARCH64:
                        resume_pc &= 0xFFFFFFFFFFFFFFFC;
                        break;
@@ -897,10 +912,8 @@ static int aarch64_internal_restore(struct target *target, int current,
        buf_set_u64(arm->pc->value, 0, 64, resume_pc);
        arm->pc->dirty = 1;
        arm->pc->valid = 1;
-#if 0
-       /* restore dpm_mode at system halt */
-       dpm_modeswitch(&armv8->dpm, ARM_MODE_ANY);
-#endif
+       dpmv8_modeswitch(&armv8->dpm, ARM_MODE_ANY);
+
        /* called it now before restoring context because it uses cpu
         * register r0 for restoring system control register */
        retval = aarch64_restore_system_control_reg(target);
@@ -1104,7 +1117,7 @@ static int aarch64_debug_entry(struct target *target)
                arm_dpm_report_wfar(&armv8->dpm, wfar);
        }
 
-       retval = arm_dpm_read_current_registers_64(&armv8->dpm);
+       retval = armv8_dpm_read_current_registers(&armv8->dpm);
 
        if (armv8->post_debug_entry) {
                retval = armv8->post_debug_entry(target);
@@ -1209,9 +1222,8 @@ static int aarch64_restore_context(struct target *target, bool bpwp)
        if (armv8->pre_restore_context)
                armv8->pre_restore_context(target);
 
-       return arm_dpm_write_dirty_registers(&armv8->dpm, bpwp);
+       return armv8_dpm_write_dirty_registers(&armv8->dpm, bpwp);
 
-       return ERROR_OK;
 }
 
 /*
index e4f203838f3b3fee5cae7717e3a7ffd094c6da85..254803873b5e68810ddea63c3f814629769df802 100644 (file)
@@ -66,6 +66,15 @@ enum arm_mode {
        ARM_MODE_USER_THREAD = 1,
        ARM_MODE_HANDLER = 2,
 
+       /* shift left 4 bits for armv8 64 */
+       ARMV8_64_EL0T = 0x0F,
+       ARMV8_64_EL1T = 0x4F,
+       ARMV8_64_EL1H = 0x5F,
+       ARMV8_64_EL2T = 0x8F,
+       ARMV8_64_EL2H = 0x9F,
+       ARMV8_64_EL3T = 0xCF,
+       ARMV8_64_EL3H = 0xDF,
+
        ARM_MODE_ANY = -1
 };
 
@@ -180,6 +189,18 @@ struct arm {
                        uint32_t CRn, uint32_t CRm,
                        uint32_t value);
 
+       /** Read coprocessor register.  */
+       int (*mrs)(struct target *target, uint32_t op0,
+                       uint32_t op1, uint32_t op2,
+                       uint32_t CRn, uint32_t CRm,
+                       uint32_t *value);
+
+       /** Write coprocessor register.  */
+       int (*msr)(struct target *target, uint32_t cpnum,
+                       uint32_t op1, uint32_t op2,
+                       uint32_t CRn, uint32_t CRm,
+                       uint32_t value);
+
        void *arch_info;
 
        /** For targets conforming to ARM Debug Interface v5,
index 55f253e464ff95e730541939b8d44de1d03d2021..00ebebaef89b97ff99f56bf8db5205f015456426 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "arm.h"
 #include "arm_dpm.h"
+#include "armv8_dpm.h"
 #include <jtag/jtag.h>
 #include "register.h"
 #include "breakpoints.h"
@@ -130,11 +131,11 @@ int dpm_modeswitch(struct arm_dpm *dpm, enum arm_mode mode)
        return retval;
 }
 
-
-static int dpm_read_reg32(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
+/* 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)
 {
        uint32_t value;
-       int retval = ERROR_FAIL;
+       int retval;
 
        switch (regnum) {
                case 0 ... 14:
@@ -165,8 +166,8 @@ static int dpm_read_reg32(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
                                        /* core-specific ... ? */
                                        LOG_WARNING("Jazelle PC adjustment unknown");
                                        break;
-                               case ARM_STATE_AARCH64:
-                                       LOG_ERROR("AARCH64: 32bit read requested");
+                               default:
+                                       LOG_WARNING("unknow core state");
                                        break;
                        }
                        break;
@@ -190,57 +191,10 @@ static int dpm_read_reg32(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
        return retval;
 }
 
-static int dpm_read_reg64(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
-{
-       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)
+/* 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)
 {
-       int retval = ERROR_FAIL;
+       int retval;
        uint32_t value = buf_get_u32(r->value, 0, 32);
 
        switch (regnum) {
@@ -290,51 +244,19 @@ 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);
 }
 
-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_64(dpm, i, value);
-                       break;
-               case 32: /* PC */
-                       i = 0xd51b4520;
-                       retval = dpm->instr_write_data_r0_64(dpm, i, value);
-                       break;
-               default:
-                       LOG_DEBUG("register %s (%16.16llx) not defined", r->name,
-                                 (unsigned long long)value);
-                       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)
+/**
+ * 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)
 {
        struct arm *arm = dpm->arm;
-       uint32_t cpsr, instr, core_regs;
+       uint32_t cpsr;
        int retval;
        struct reg *r;
-       enum arm_state core_state = arm->core_state;
 
        retval = dpm->prepare(dpm);
        if (retval != ERROR_OK)
@@ -349,25 +271,16 @@ static int arm_dpm_read_current_registers_i(struct arm_dpm *dpm)
        }
        r->dirty = true;
 
-       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);
+       retval = dpm->instr_read_data_r0(dpm, ARMV4_5_MRS(0, 0), &cpsr);
        if (retval != ERROR_OK)
                goto fail;
 
        /* update core mode and state, plus shadow mapping for R8..R14 */
        arm_set_cpsr(arm, cpsr);
-       if (core_state == ARM_STATE_AARCH64)
-               /* arm_set_cpsr changes core_state, restore it for now */
-               arm->core_state = ARM_STATE_AARCH64;
-
-       core_regs = arm->core_cache->num_regs;
 
        /* REVISIT we can probably avoid reading R1..R14, saving time... */
-       for (unsigned i = 1; i < core_regs; i++) {
-               r = dpm->arm_reg_current(arm, i);
+       for (unsigned i = 1; i < 16; i++) {
+               r = arm_reg_current(arm, i);
                if (r->valid)
                        continue;
 
@@ -388,23 +301,6 @@ 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.
@@ -453,14 +349,59 @@ done:
 
 static int dpm_add_breakpoint(struct target *target, struct breakpoint *bp);
 
-
-static int arm_dpm_write_dirty_registers_32(struct arm_dpm *dpm)
+/**
+ * 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;
        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
@@ -564,106 +505,6 @@ static int arm_dpm_write_dirty_registers_32(struct arm_dpm *dpm)
                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;
@@ -1133,20 +974,15 @@ int arm_dpm_setup(struct arm_dpm *dpm)
 
        /* register access setup */
        arm->full_context = arm_dpm_full_context;
-       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;
+       arm->read_core_reg = arm_dpm_read_core_reg;
+       arm->write_core_reg = arm_dpm_write_core_reg;
 
        if (arm->core_cache != NULL) {
-               if (arm->core_state == ARM_STATE_AARCH64) {
-                       cache = armv8_build_reg_cache(target);
-                       target->reg_cache = cache;
-               } else {
-                       cache = arm_build_reg_cache(target, arm);
-                       *register_get_last_cache_p(&target->reg_cache) = cache;
-               }
-
+               cache = arm_build_reg_cache(target, arm);
                if (!cache)
                        return ERROR_FAIL;
+
+               *register_get_last_cache_p(&target->reg_cache) = cache;
        }
 
        /* coprocessor access setup */
@@ -1163,20 +999,10 @@ 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 */
 
-       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->nbp = 1 + ((dpm->didr >> 24) & 0xf);
+       dpm->nwp = 1 + ((dpm->didr >> 28) & 0xf);
        dpm->dbp = calloc(dpm->nbp, sizeof *dpm->dbp);
        dpm->dwp = calloc(dpm->nwp, sizeof *dpm->dwp);
 
index 5b16e6481728992821bb2d35f174e184ef977016..ad49b8c44c5b6c12a004f0726e6ae9585c391fce 100644 (file)
@@ -59,7 +59,7 @@ struct arm_dpm {
        struct arm *arm;
 
        /** Cache of DIDR */
-       uint32_t didr;
+       uint64_t didr;
 
        /** Invoke before a series of instruction operations */
        int (*prepare)(struct arm_dpm *);
@@ -67,6 +67,9 @@ struct arm_dpm {
        /** Invoke after a series of instruction operations */
        int (*finish)(struct arm_dpm *);
 
+       /** Runs one instruction. */
+       int (*instr_execute)(struct arm_dpm *, uint32_t opcode);
+
        /* WRITE TO CPU */
 
        /** Runs one instruction, writing data to DCC before execution. */
index e516518ad4faf5f8eb23cfe21c13991eb9253845..537dadb28e327687671cca7144c6bb4ddbe88aaa 100644 (file)
 #include "target.h"
 #include "target_type.h"
 
+static const char * const armv8_state_strings[] = {
+       "ARM", "Thumb", "Jazelle", "ThumbEE", "ARM64",
+};
+
+static const struct {
+       const char *name;
+       unsigned psr;
+       /* For user and system modes, these list indices for all registers.
+        * otherwise they're just indices for the shadow registers and SPSR.
+        */
+       unsigned short n_indices;
+       const uint8_t *indices;
+} armv8_mode_data[] = {
+       /* These special modes are currently only supported
+        * by ARMv6M and ARMv7M profiles */
+       {
+               .name = "EL0T",
+               .psr = ARMV8_64_EL0T,
+       },
+       {
+               .name = "EL1T",
+               .psr = ARMV8_64_EL1T,
+       },
+       {
+               .name = "EL1H",
+               .psr = ARMV8_64_EL1H,
+       },
+       {
+               .name = "EL2T",
+               .psr = ARMV8_64_EL2T,
+       },
+       {
+               .name = "EL2H",
+               .psr = ARMV8_64_EL2H,
+       },
+       {
+               .name = "EL3T",
+               .psr = ARMV8_64_EL3T,
+       },
+       {
+               .name = "EL3H",
+               .psr = ARMV8_64_EL3H,
+       },
+};
+
+/** Map PSR mode bits to the name of an ARM processor operating mode. */
+const char *armv8_mode_name(unsigned psr_mode)
+{
+       for (unsigned i = 0; i < ARRAY_SIZE(armv8_mode_data); i++) {
+               if (armv8_mode_data[i].psr == psr_mode)
+                       return armv8_mode_data[i].name;
+       }
+       LOG_ERROR("unrecognized psr mode: %#02x", psr_mode);
+       return "UNRECOGNIZED";
+}
+
+int armv8_mode_to_number(enum arm_mode mode)
+{
+       switch (mode) {
+               case ARM_MODE_ANY:
+               /* map MODE_ANY to user mode */
+               case ARM_MODE_USR:
+                       return 0;
+               case ARM_MODE_FIQ:
+                       return 1;
+               case ARM_MODE_IRQ:
+                       return 2;
+               case ARM_MODE_SVC:
+                       return 3;
+               case ARM_MODE_ABT:
+                       return 4;
+               case ARM_MODE_UND:
+                       return 5;
+               case ARM_MODE_SYS:
+                       return 6;
+               case ARM_MODE_MON:
+                       return 7;
+               case ARMV8_64_EL0T:
+                       return 8;
+               case ARMV8_64_EL1T:
+                       return 9;
+               case ARMV8_64_EL1H:
+                       return 10;
+               case ARMV8_64_EL2T:
+                       return 11;
+               case ARMV8_64_EL2H:
+                       return 12;
+               case ARMV8_64_EL3T:
+                       return 13;
+               case ARMV8_64_EL3H:
+                       return 14;
+
+               default:
+                       LOG_ERROR("invalid mode value encountered %d", mode);
+                       return -1;
+       }
+}
+
+
 static int armv8_read_core_reg(struct target *target, struct reg *r,
        int num, enum arm_mode mode)
 {
@@ -86,6 +185,81 @@ static int armv8_write_core_reg(struct target *target, struct reg *r,
        return ERROR_OK;
 }
 #endif
+/**
+ * Configures host-side ARM records to reflect the specified CPSR.
+ * Later, code can use arm_reg_current() to map register numbers
+ * according to how they are exposed by this mode.
+ */
+void armv8_set_cpsr(struct arm *arm, uint32_t cpsr)
+{
+       uint32_t mode = cpsr & 0x1F;
+
+       /* NOTE:  this may be called very early, before the register
+        * cache is set up.  We can't defend against many errors, in
+        * particular against CPSRs that aren't valid *here* ...
+        */
+       if (arm->cpsr) {
+               buf_set_u32(arm->cpsr->value, 0, 32, cpsr);
+               arm->cpsr->valid = 1;
+               arm->cpsr->dirty = 0;
+       }
+
+       /* Older ARMs won't have the J bit */
+       enum arm_state state = 0xFF;
+
+       if (((cpsr & 0x10) >> 4) == 0) {
+               state = ARM_STATE_AARCH64;
+       } else {
+               if (cpsr & (1 << 5)) {  /* T */
+                       if (cpsr & (1 << 24)) { /* J */
+                               LOG_WARNING("ThumbEE -- incomplete support");
+                               state = ARM_STATE_THUMB_EE;
+                       } else
+                               state = ARM_STATE_THUMB;
+               } else {
+                       if (cpsr & (1 << 24)) { /* J */
+                               LOG_ERROR("Jazelle state handling is BROKEN!");
+                               state = ARM_STATE_JAZELLE;
+                       } else
+                               state = ARM_STATE_ARM;
+               }
+       }
+       arm->core_state = state;
+       if (arm->core_state == ARM_STATE_AARCH64) {
+               switch (mode) {
+                       case SYSTEM_AAR64_MODE_EL0t:
+                               arm->core_mode = ARMV8_64_EL0T;
+                       break;
+                       case SYSTEM_AAR64_MODE_EL1t:
+                               arm->core_mode = ARMV8_64_EL0T;
+                       break;
+                       case SYSTEM_AAR64_MODE_EL1h:
+                               arm->core_mode = ARMV8_64_EL1H;
+                       break;
+                       case SYSTEM_AAR64_MODE_EL2t:
+                               arm->core_mode = ARMV8_64_EL2T;
+                       break;
+                       case SYSTEM_AAR64_MODE_EL2h:
+                               arm->core_mode = ARMV8_64_EL2H;
+                       break;
+                       case SYSTEM_AAR64_MODE_EL3t:
+                               arm->core_mode = ARMV8_64_EL3T;
+                       break;
+                       case SYSTEM_AAR64_MODE_EL3h:
+                               arm->core_mode = ARMV8_64_EL3H;
+                       break;
+                       default:
+                               LOG_DEBUG("unknow mode 0x%x", (unsigned) (mode));
+                       break;
+               }
+       } else {
+               arm->core_mode = mode;
+       }
+
+       LOG_DEBUG("set CPSR %#8.8x: %s mode, %s state", (unsigned) cpsr,
+               armv8_mode_name(arm->core_mode),
+               armv8_state_strings[arm->core_state]);
+}
 
 static void armv8_show_fault_registers(struct target *target)
 {
index 47e36680ed159a2a0a4d493acd0c4261e1dfc418..ccb205c5c307bbfdfe2fedfbce27ef4218c4e6e9 100644 (file)
@@ -23,7 +23,7 @@
 #include "arm.h"
 #include "armv4_5_mmu.h"
 #include "armv4_5_cache.h"
-#include "arm_dpm.h"
+#include "armv8_dpm.h"
 
 enum {
        ARMV8_R0,
@@ -198,6 +198,8 @@ int armv8_mmu_translate_va(struct target *target,  uint32_t va, uint32_t *val);
 int armv8_handle_cache_info_command(struct command_context *cmd_ctx,
                struct armv8_cache_common *armv8_cache);
 
+void armv8_set_cpsr(struct arm *arm, uint32_t cpsr);
+
 extern const struct command_registration armv8_command_handlers[];
 
 #endif
diff --git a/src/target/armv8_dpm.c b/src/target/armv8_dpm.c
new file mode 100644 (file)
index 0000000..dc6682b
--- /dev/null
@@ -0,0 +1,985 @@
+/*
+ * Copyright (C) 2009 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "arm.h"
+#include "armv8.h"
+#include "armv8_dpm.h"
+#include <jtag/jtag.h>
+#include "register.h"
+#include "breakpoints.h"
+#include "target_type.h"
+#include "armv8_opcodes.h"
+
+
+/**
+ * @file
+ * Implements various ARM DPM operations using architectural debug registers.
+ * These routines layer over core-specific communication methods to cope with
+ * implementation differences between cores like ARM1136 and Cortex-A8.
+ *
+ * The "Debug Programmers' Model" (DPM) for ARMv6 and ARMv7 is defined by
+ * Part C (Debug Architecture) of the ARM Architecture Reference Manual,
+ * ARMv7-A and ARMv7-R edition (ARM DDI 0406B).  In OpenOCD, DPM operations
+ * are abstracted through internal programming interfaces to share code and
+ * to minimize needless differences in debug behavior between cores.
+ */
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Coprocessor support
+ */
+
+/* Read coprocessor */
+static int dpmv8_mrc(struct target *target, int cpnum,
+       uint32_t op1, uint32_t op2, uint32_t CRn, uint32_t CRm,
+       uint32_t *value)
+{
+       struct arm *arm = target_to_arm(target);
+       struct arm_dpm *dpm = arm->dpm;
+       int retval;
+
+       retval = dpm->prepare(dpm);
+       if (retval != ERROR_OK)
+               return retval;
+
+       LOG_DEBUG("MRC p%d, %d, r0, c%d, c%d, %d", cpnum,
+               (int) op1, (int) CRn,
+               (int) CRm, (int) op2);
+
+       /* read coprocessor register into R0; return via DCC */
+       retval = dpm->instr_read_data_r0(dpm,
+                       T32_FMTITR(ARMV4_5_MRC(cpnum, op1, 0, CRn, CRm, op2)),
+                       value);
+
+       /* (void) */ dpm->finish(dpm);
+       return retval;
+}
+
+static int dpmv8_mcr(struct target *target, int cpnum,
+       uint32_t op1, uint32_t op2, uint32_t CRn, uint32_t CRm,
+       uint32_t value)
+{
+       struct arm *arm = target_to_arm(target);
+       struct arm_dpm *dpm = arm->dpm;
+       int retval;
+
+       retval = dpm->prepare(dpm);
+       if (retval != ERROR_OK)
+               return retval;
+
+       LOG_DEBUG("MCR p%d, %d, r0, c%d, c%d, %d", cpnum,
+               (int) op1, (int) CRn,
+               (int) CRm, (int) op2);
+
+       /* read DCC into r0; then write coprocessor register from R0 */
+       retval = dpm->instr_write_data_r0(dpm,
+                       T32_FMTITR(ARMV4_5_MCR(cpnum, op1, 0, CRn, CRm, op2)),
+                       value);
+
+       /* (void) */ dpm->finish(dpm);
+       return retval;
+}
+
+static int dpmv8_mrs(struct target *target, uint32_t op0,
+       uint32_t op1, uint32_t op2, uint32_t CRn, uint32_t CRm,
+       uint32_t *value)
+{
+       struct arm *arm = target_to_arm(target);
+       struct arm_dpm *dpm = arm->dpm;
+       int retval;
+       uint32_t op_code;
+
+       retval = dpm->prepare(dpm);
+       if (retval != ERROR_OK)
+               return retval;
+       op_code = ((op0 & 0x3) << 19 | (op1 & 0x7) << 16 | (CRn & 0xF) << 12 |\
+                               (CRm & 0xF) << 8 | (op2 & 0x7) << 5);
+       op_code >>= 5;
+       LOG_DEBUG("MRS p%d, %d, r0, c%d, c%d, %d", (int)op0,
+               (int) op1, (int) CRn,
+               (int) CRm, (int) op2);
+       /* read coprocessor register into R0; return via DCC */
+       retval = dpm->instr_read_data_r0(dpm,
+                       ARMV8_MRS(op_code, 0),
+                       value);
+
+       /* (void) */ dpm->finish(dpm);
+       return retval;
+}
+
+static int dpmv8_msr(struct target *target, uint32_t op0,
+       uint32_t op1, uint32_t op2, uint32_t CRn, uint32_t CRm,
+       uint32_t value)
+{
+       struct arm *arm = target_to_arm(target);
+       struct arm_dpm *dpm = arm->dpm;
+       int retval;
+       uint32_t op_code;
+
+       retval = dpm->prepare(dpm);
+       if (retval != ERROR_OK)
+               return retval;
+
+       op_code = ((op0 & 0x3) << 19 | (op1 & 0x7) << 16 | (CRn & 0xF) << 12 |\
+                               (CRm & 0xF) << 8 | (op2 & 0x7) << 5);
+       op_code >>= 5;
+       LOG_DEBUG("MSR p%d, %d, r0, c%d, c%d, %d", (int)op0,
+               (int) op1, (int) CRn,
+               (int) CRm, (int) op2);
+
+       /* read DCC into r0; then write coprocessor register from R0 */
+       retval = dpm->instr_write_data_r0(dpm,
+                       ARMV8_MSR_GP(op_code, 0),
+                       value);
+
+       /* (void) */ dpm->finish(dpm);
+       return retval;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Register access utilities
+ */
+
+/* Toggles between recorded core mode (USR, SVC, etc) and a temporary one.
+ * Routines *must* restore the original mode before returning!!
+ */
+int dpmv8_modeswitch(struct arm_dpm *dpm, enum arm_mode mode)
+{
+       int retval;
+       uint32_t cpsr;
+
+       /* restore previous mode */
+       if (mode == ARM_MODE_ANY)
+               cpsr = buf_get_u32(dpm->arm->cpsr->value, 0, 32);
+
+       /* else force to the specified mode */
+       else
+               cpsr = mode >> 4;
+
+       switch ((cpsr & 0xC) >> 2) {
+                       case SYSTEM_CUREL_EL1:
+                               retval = dpm->instr_execute(dpm, ARMV8_DCPS1(11));
+                               if (retval != ERROR_OK)
+                                       return retval;
+                               break;
+                       case SYSTEM_CUREL_EL2:
+                               retval = dpm->instr_execute(dpm, ARMV8_DCPS2(11));
+                               if (retval != ERROR_OK)
+                                       return retval;
+                               break;
+                       break;
+                       case SYSTEM_CUREL_EL3:
+                               retval = dpm->instr_execute(dpm, ARMV8_DCPS3(11));
+                               if (retval != ERROR_OK)
+                                       return retval;
+                               break;
+                       break;
+                       default:
+                               LOG_DEBUG("unknow mode 0x%x", (unsigned) ((cpsr & 0xC) >> 2));
+                               break;
+       }
+
+
+       retval = dpm->instr_write_data_r0(dpm, ARMV8_MSR_DSPSR(0), cpsr);
+       if (retval != ERROR_OK)
+               return retval;
+
+       if (dpm->instr_cpsr_sync)
+               retval = dpm->instr_cpsr_sync(dpm);
+
+       return retval;
+}
+
+/* just read the register -- rely on the core mode being right */
+static int dpmv8_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
+{
+       uint32_t value;
+       uint64_t value_64;
+       int retval = ERROR_FAIL;
+
+       switch (regnum) {
+               case 0 ... 30:
+                       retval = dpm->instr_read_data_dcc_64(dpm,
+                               ARMV8_MSR_GP(SYSTEM_DBG_DBGDTR_EL0, regnum),
+                               &value_64);
+                       break;
+               case 31:
+                       retval = dpm->instr_read_data_r0_64(dpm,
+                               ARMV8_MOVFSP_64(0),
+                               &value_64);
+                       break;
+               case 32:
+                       retval = dpm->instr_read_data_r0_64(dpm,
+                               ARMV8_MRS_DLR(0),
+                               &value_64);
+                       break;
+               case 33:
+                       retval = dpm->instr_read_data_r0(dpm,
+                               ARMV8_MRS_DSPSR(0),
+                               &value);
+               default:
+                       LOG_DEBUG("READ: %s fail", r->name);
+                       break;
+       }
+
+       if (retval == ERROR_OK) {
+               r->valid = true;
+               r->dirty = false;
+               buf_set_u64(r->value, 0, 32, value_64);
+               if (r->size == 64)
+                       LOG_DEBUG("READ: %s, %16.8llx", r->name, (unsigned long long) value_64);
+               else
+                       LOG_DEBUG("READ: %s, %8.8x", r->name, (unsigned) value);
+       }
+       return retval;
+}
+
+/* just write the register -- rely on the core mode being right */
+static int dpmv8_write_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
+{
+       int retval = ERROR_FAIL;
+       uint32_t value = 0xFFFFFFFF;
+       uint64_t value_64 = 0xFFFFFFFFFFFFFFFF;
+
+       switch (regnum) {
+               case 0 ... 30:
+                       value_64 = buf_get_u64(r->value, 0, 64);
+                       retval = dpm->instr_write_data_dcc_64(dpm,
+                               ARMV8_MRS(SYSTEM_DBG_DBGDTR_EL0, regnum),
+                               value_64);
+                       break;
+               case 31:
+                       value_64 = buf_get_u64(r->value, 0, 64);
+                       retval = dpm->instr_write_data_r0_64(dpm,
+                               ARMV8_MOVTSP_64(0),
+                               value_64);
+                       break;
+               case 32:
+                       value_64 = buf_get_u64(r->value, 0, 64);
+                       retval = dpm->instr_write_data_r0_64(dpm,
+                               ARMV8_MSR_DLR(0),
+                               value_64);
+                       break;
+               case 33:
+                       value = buf_get_u32(r->value, 0, 32);
+                       retval = dpm->instr_write_data_r0(dpm,
+                               ARMV8_MSR_DSPSR(0),
+                               value);
+               default:
+                       LOG_DEBUG("write: %s fail", r->name);
+                       break;
+       }
+
+
+       if (retval == ERROR_OK) {
+               r->dirty = false;
+               if (r->size == 64)
+                       LOG_DEBUG("WRITE: %s, %16.8llx", r->name, (unsigned long long) value_64);
+               else
+                       LOG_DEBUG("WRITE: %s, %8.8x", r->name, (unsigned) value);
+       }
+
+       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 armv8_dpm_read_current_registers(struct arm_dpm *dpm)
+{
+       struct arm *arm = dpm->arm;
+       uint32_t cpsr;
+       int retval;
+       struct reg *r;
+
+       retval = dpm->prepare(dpm);
+       if (retval != ERROR_OK)
+               return retval;
+
+       /* read R0 first (it's used for scratch), then CPSR */
+       r = arm->core_cache->reg_list + 0;
+       if (!r->valid) {
+               retval = dpmv8_read_reg(dpm, r, 0);
+               if (retval != ERROR_OK)
+                       goto fail;
+       }
+       r->dirty = true;
+       /* read cpsr to r0 and get it back */
+       retval = dpm->instr_read_data_r0(dpm, ARMV8_MRS_DSPSR(0), &cpsr);
+       if (retval != ERROR_OK)
+               goto fail;
+
+       /* update core mode and state, plus shadow mapping for R8..R14 */
+       armv8_set_cpsr(arm, cpsr);
+
+       /* REVISIT we can probably avoid reading R1..R14, saving time... */
+       for (unsigned i = 1; i < arm->core_cache->num_regs ; i++) {
+               r = armv8_reg_current(arm, i);
+               if (r->valid)
+                       continue;
+
+               retval = dpmv8_read_reg(dpm, r, i);
+               if (retval != ERROR_OK)
+                       goto fail;
+       }
+
+       /* NOTE: SPSR ignored (if it's even relevant). */
+
+       /* REVISIT the debugger can trigger various exceptions.  See the
+        * ARMv7A architecture spec, section C5.7, for more info about
+        * what defenses are needed; v6 debug has the most issues.
+        */
+
+fail:
+       /* (void) */ dpm->finish(dpm);
+       return retval;
+}
+
+/* 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 dpmv8_maybe_update_bpwp(struct arm_dpm *dpm, bool bpwp,
+       struct dpm_bpwp *xp, int *set_p)
+{
+       int retval = ERROR_OK;
+       bool disable;
+
+       if (!set_p) {
+               if (!xp->dirty)
+                       goto done;
+               xp->dirty = false;
+               /* removed or startup; we must disable it */
+               disable = true;
+       } else if (bpwp) {
+               if (!xp->dirty)
+                       goto done;
+               /* disabled, but we must set it */
+               xp->dirty = disable = false;
+               *set_p = true;
+       } else {
+               if (!*set_p)
+                       goto done;
+               /* set, but we must temporarily disable it */
+               xp->dirty = disable = true;
+               *set_p = false;
+       }
+
+       if (disable)
+               retval = dpm->bpwp_disable(dpm, xp->number);
+       else
+               retval = dpm->bpwp_enable(dpm, xp->number,
+                               xp->address, xp->control);
+
+       if (retval != ERROR_OK)
+               LOG_ERROR("%s: can't %s HW %spoint %d",
+                       disable ? "disable" : "enable",
+                       target_name(dpm->arm->target),
+                       (xp->number < 16) ? "break" : "watch",
+                       xp->number & 0xf);
+done:
+       return retval;
+}
+
+static int dpmv8_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 armv8_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 == dpmv8_add_breakpoint) {
+               for (unsigned i = 0; i < dpm->nbp; i++) {
+                       struct dpm_bp *dbp = dpm->dbp + i;
+                       struct breakpoint *bp = dbp->bp;
+
+                       retval = dpmv8_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 = dpmv8_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
+        * actually find anything to do...
+        */
+
+       /* check everything except our scratch register R0 */
+       for (unsigned i = 1; i < cache->num_regs; i++) {
+               struct arm_reg *r;
+               unsigned regnum;
+
+               /* also skip PC, CPSR, and non-dirty */
+               if (i == (arm->core_cache->num_regs - 2))
+                       continue;
+               if (arm->cpsr == cache->reg_list + i)
+                       continue;
+               if (!cache->reg_list[i].dirty)
+                       continue;
+
+               r = cache->reg_list[i].arch_info;
+               regnum = r->num;
+
+               retval = dpmv8_write_reg(dpm,
+                               &cache->reg_list[i],
+                               regnum);
+               if (retval != ERROR_OK)
+                       goto done;
+       }
+
+
+       /* Restore original CPSR ... assuming either that we changed it,
+        * or it's dirty.  Must write PC to ensure the return address is
+        * defined, and must not write it before CPSR.
+        */
+       retval = dpmv8_modeswitch(dpm, ARM_MODE_ANY);
+       if (retval != ERROR_OK)
+               goto done;
+       arm->cpsr->dirty = false;
+
+       retval = dpmv8_write_reg(dpm, arm->pc, (arm->core_cache->num_regs - 2));
+       if (retval != ERROR_OK)
+               goto done;
+       arm->pc->dirty = false;
+
+       /* flush R0 -- it's *very* dirty by now */
+       retval = dpmv8_write_reg(dpm, &cache->reg_list[0], 0);
+       if (retval != ERROR_OK)
+               goto done;
+       cache->reg_list[0].dirty = false;
+
+       /* (void) */ dpm->finish(dpm);
+done:
+       return retval;
+}
+
+/*
+ * Standard ARM register accessors ... there are three methods
+ * in "struct arm", to support individual read/write and bulk read
+ * of registers.
+ */
+
+static int armv8_dpm_read_core_reg(struct target *target, struct reg *r,
+       int regnum, enum arm_mode mode)
+{
+       struct arm *arm = target_to_arm(target);
+       struct arm_dpm *dpm = target_to_arm(target)->dpm;
+       int retval;
+       int max = arm->core_cache->num_regs;
+
+       if (regnum < 0 || regnum > max)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       /* REVISIT what happens if we try to read SPSR in a core mode
+        * which has no such register?
+        */
+
+       retval = dpm->prepare(dpm);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = dpmv8_read_reg(dpm, r, regnum);
+       if (retval != ERROR_OK)
+               goto fail;
+
+fail:
+       /* (void) */ dpm->finish(dpm);
+       return retval;
+}
+
+static int armv8_dpm_write_core_reg(struct target *target, struct reg *r,
+       int regnum, enum arm_mode mode, uint8_t *value)
+{
+       struct arm *arm = target_to_arm(target);
+       struct arm_dpm *dpm = target_to_arm(target)->dpm;
+       int retval;
+       int max = arm->core_cache->num_regs;
+
+       if (regnum < 0 || regnum > max)
+               return ERROR_COMMAND_SYNTAX_ERROR;
+
+       /* REVISIT what happens if we try to write SPSR in a core mode
+        * which has no such register?
+        */
+
+       retval = dpm->prepare(dpm);
+       if (retval != ERROR_OK)
+               return retval;
+
+       retval = dpmv8_write_reg(dpm, r, regnum);
+       /* always clean up, regardless of error */
+
+       /* (void) */ dpm->finish(dpm);
+       return retval;
+}
+
+static int armv8_dpm_full_context(struct target *target)
+{
+       struct arm *arm = target_to_arm(target);
+       struct arm_dpm *dpm = arm->dpm;
+       struct reg_cache *cache = arm->core_cache;
+       int retval;
+       bool did_read;
+
+       retval = dpm->prepare(dpm);
+       if (retval != ERROR_OK)
+               goto done;
+
+       do {
+               enum arm_mode mode = ARM_MODE_ANY;
+
+               did_read = false;
+
+               /* We "know" arm_dpm_read_current_registers() was called so
+                * the unmapped registers (R0..R7, PC, AND CPSR) and some
+                * view of R8..R14 are current.  We also "know" oddities of
+                * register mapping: special cases for R8..R12 and SPSR.
+                *
+                * Pick some mode with unread registers and read them all.
+                * Repeat until done.
+                */
+               for (unsigned i = 0; i < cache->num_regs; i++) {
+                       struct arm_reg *r;
+
+                       if (cache->reg_list[i].valid)
+                               continue;
+                       r = cache->reg_list[i].arch_info;
+
+                       /* may need to pick a mode and set CPSR */
+                       if (!did_read) {
+                               did_read = true;
+                               mode = r->mode;
+
+                               /* For regular (ARM_MODE_ANY) R8..R12
+                                * in case we've entered debug state
+                                * in FIQ mode we need to patch mode.
+                                */
+                               if (mode != ARM_MODE_ANY)
+                                       retval = dpmv8_modeswitch(dpm, mode);
+                               else
+                                       retval = dpmv8_modeswitch(dpm, ARM_MODE_USR);
+
+                               if (retval != ERROR_OK)
+                                       goto done;
+                       }
+                       if (r->mode != mode)
+                               continue;
+
+                       /* CPSR was read, so "R16" must mean SPSR */
+                       retval = dpmv8_read_reg(dpm,
+                                       &cache->reg_list[i],
+                                       (r->num == 16) ? 17 : r->num);
+                       if (retval != ERROR_OK)
+                               goto done;
+               }
+
+       } while (did_read);
+
+       retval = dpmv8_modeswitch(dpm, ARM_MODE_ANY);
+       /* (void) */ dpm->finish(dpm);
+done:
+       return retval;
+}
+
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * 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 dpmv8_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 dpmv8_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 = dpmv8_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 dpmv8_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 dpmv8_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 = dpmv8_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 dpmv8_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 = dpmv8_watchpoint_setup(dpm, i, wp);
+                               break;
+                       }
+               }
+       }
+
+       return retval;
+}
+
+static int dpmv8_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 armv8_dpm_report_wfar(struct arm_dpm *dpm, uint64_t addr)
+{
+       switch (dpm->arm->core_state) {
+               case ARM_STATE_ARM:
+               case ARM_STATE_AARCH64:
+                       addr -= 8;
+                       break;
+               case ARM_STATE_THUMB:
+               case ARM_STATE_THUMB_EE:
+                       addr -= 4;
+                       break;
+               case ARM_STATE_JAZELLE:
+                       /* ?? */
+                       break;
+               default:
+                       LOG_DEBUG("Unknow core_state");
+                       break;
+       }
+       dpm->wp_pc = addr;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Other debug and support utilities
+ */
+
+void armv8_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)) {
+               /* FALL THROUGH -- assume a v6 core in abort mode */
+               case DSCRV8_ENTRY_HLT:  /* HALT request from debugger */
+               case DSCRV8_ENTRY_EXT_DEBUG:    /* EDBGRQ */
+                       target->debug_reason = DBG_REASON_DBGRQ;
+                       break;
+               case DSCRV8_ENTRY_HALT_STEP_EXECLU:     /* HALT step */
+               case DSCRV8_ENTRY_BKPT: /* SW BKPT */
+               case DSCRV8_ENTRY_RESET_CATCH:  /* Reset catch */
+               case DSCRV8_ENTRY_OS_UNLOCK:  /*OS unlock catch*/
+               case DSCRV8_ENTRY_EXCEPTION_CATCH:  /*exception catch*/
+               case DSCRV8_ENTRY_HALT_STEP_NORMAL: /* Halt step*/
+               case DSCRV8_ENTRY_SW_ACCESS_DBG: /*SW access dbg register*/
+                       target->debug_reason = DBG_REASON_BREAKPOINT;
+                       break;
+               case DSCRV8_ENTRY_WATCHPOINT:   /* asynch watchpoint */
+                       target->debug_reason = DBG_REASON_WATCHPOINT;
+                       break;
+               default:
+                       target->debug_reason = DBG_REASON_UNDEFINED;
+                       break;
+       }
+
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Setup and management support.
+ */
+
+/**
+ * Hooks up this DPM to its associated target; call only once.
+ * Initially this only covers the register cache.
+ *
+ * Oh, and watchpoints.  Yeah.
+ */
+int armv8_dpm_setup(struct arm_dpm *dpm)
+{
+       struct arm *arm = dpm->arm;
+       struct target *target = arm->target;
+       struct reg_cache *cache;
+       arm->dpm = dpm;
+
+       /* register access setup */
+       arm->full_context = armv8_dpm_full_context;
+       arm->read_core_reg = armv8_dpm_read_core_reg;
+       arm->write_core_reg = armv8_dpm_write_core_reg;
+
+       cache = armv8_build_reg_cache(target);
+       if (!cache)
+               return ERROR_FAIL;
+
+       /* coprocessor access setup */
+       arm->mrc = dpmv8_mrc;
+       arm->mcr = dpmv8_mcr;
+       arm->mrs = dpmv8_mrs;
+       arm->msr = dpmv8_msr;
+       /* breakpoint setup -- optional until it works everywhere */
+       if (!target->type->add_breakpoint) {
+               target->type->add_breakpoint = dpmv8_add_breakpoint;
+               target->type->remove_breakpoint = dpmv8_remove_breakpoint;
+       }
+
+       /* watchpoint setup */
+       target->type->add_watchpoint = dpmv8_add_watchpoint;
+       target->type->remove_watchpoint = dpmv8_remove_watchpoint;
+
+       /* FIXME add vector catch support */
+
+       dpm->nbp = 1 + ((dpm->didr >> 12) & 0xf);
+       dpm->dbp = calloc(dpm->nbp, sizeof *dpm->dbp);
+
+       dpm->nwp = 1 + ((dpm->didr >> 20) & 0xf);
+       dpm->dwp = calloc(dpm->nwp, sizeof *dpm->dwp);
+
+       if (!dpm->dbp || !dpm->dwp) {
+               free(dpm->dbp);
+               free(dpm->dwp);
+               return ERROR_FAIL;
+       }
+
+       LOG_INFO("%s: hardware has %d breakpoints, %d watchpoints",
+               target_name(target), dpm->nbp, dpm->nwp);
+
+       /* REVISIT ... and some of those breakpoints could match
+        * execution context IDs...
+        */
+
+       return ERROR_OK;
+}
+
+/**
+ * Reinitializes DPM state at the beginning of a new debug session
+ * or after a reset which may have affected the debug module.
+ */
+int armv8_dpm_initialize(struct arm_dpm *dpm)
+{
+       /* Disable all breakpoints and watchpoints at startup. */
+       if (dpm->bpwp_disable) {
+               unsigned i;
+
+               for (i = 0; i < dpm->nbp; i++) {
+                       dpm->dbp[i].bpwp.number = i;
+                       (void) dpm->bpwp_disable(dpm, i);
+               }
+               for (i = 0; i < dpm->nwp; i++) {
+                       dpm->dwp[i].bpwp.number = 16 + i;
+                       (void) dpm->bpwp_disable(dpm, 16 + i);
+               }
+       } else
+               LOG_WARNING("%s: can't disable breakpoints and watchpoints",
+                       target_name(dpm->arm->target));
+
+       return ERROR_OK;
+}
diff --git a/src/target/armv8_dpm.h b/src/target/armv8_dpm.h
new file mode 100644 (file)
index 0000000..416f4df
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2009 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef OPENOCD_TARGET_ARMV8_DPM_H
+#define OPENOCD_TARGET_ARMV8_DPM_H
+
+#include "arm_dpm.h"
+
+/* forward-declare struct armv8_common */
+struct armv8_common;
+
+/**
+ * This wraps an implementation of DPM primitives.  Each interface
+ * provider supplies a structure like this, which is the glue between
+ * upper level code and the lower level hardware access.
+ *
+ * It is a PRELIMINARY AND INCOMPLETE set of primitives, starting with
+ * support for CPU register access.
+ */
+int armv8_dpm_setup(struct arm_dpm *dpm);
+int armv8_dpm_initialize(struct arm_dpm *dpm);
+
+int armv8_dpm_read_current_registers(struct arm_dpm *);
+int dpmv8_modeswitch(struct arm_dpm *dpm, enum arm_mode mode);
+
+
+int armv8_dpm_write_dirty_registers(struct arm_dpm *, bool bpwp);
+
+void armv8_dpm_report_wfar(struct arm_dpm *, uint64_t wfar);
+
+/* DSCR bits; see ARMv7a arch spec section C10.3.1.
+ * Not all v7 bits are valid in v6.
+ */
+#define DSCR_DEBUG_STATUS_MASK         (0x1F <<  0)
+#define DSCR_ERR                                       (0x1 <<  6)
+#define DSCR_SYS_ERROR_PEND                    (0x1 <<  7)
+#define DSCR_CUR_EL                                    (0x3 <<  8)
+#define DSCR_EL_STATUS_MASK                    (0xF << 10)
+#define DSCR_HDE                                       (0x1 << 14)
+#define DSCR_SDD                                       (0x1 << 16)
+#define DSCR_NON_SECURE             (0x1 << 18)
+#define DSCR_MA                                                (0x1 << 20)
+#define DSCR_TDA                                       (0x1 << 21)
+#define DSCR_INTDIS_MASK                       (0x3 << 22)
+#define DSCR_ITE                                       (0x1 << 24)
+#define DSCR_PIPE_ADVANCE           (0x1 << 25)
+#define DSCR_TXU                                       (0x1 << 26)
+#define DSCR_RTO                                       (0x1 << 27) /* bit 28 is reserved */
+#define DSCR_ITO                                       (0x1 << 28)
+#define DSCR_DTR_TX_FULL            (0x1 << 29)
+#define DSCR_DTR_RX_FULL            (0x1 << 30) /* bit 31 is reserved */
+
+
+
+/* Methods of entry into debug mode */
+#define DSCRV8_ENTRY_NON_DEBUG                 (0x2)
+#define DSCRV8_ENTRY_RESTARTING                        (0x1)
+#define DSCRV8_ENTRY_BKPT                              (0x7)
+#define DSCRV8_ENTRY_EXT_DEBUG                 (0x13)
+#define DSCRV8_ENTRY_HALT_STEP_NORMAL  (0x1B)
+#define DSCRV8_ENTRY_HALT_STEP_EXECLU  (0x1F)
+#define DSCRV8_ENTRY_OS_UNLOCK                 (0x23)
+#define DSCRV8_ENTRY_RESET_CATCH               (0x27)
+#define DSCRV8_ENTRY_WATCHPOINT                        (0x2B)
+#define DSCRV8_ENTRY_HLT                               (0x2F)
+#define DSCRV8_ENTRY_SW_ACCESS_DBG             (0x33)
+#define DSCRV8_ENTRY_EXCEPTION_CATCH   (0x37)
+#define DSCRV8_ENTRY_HALT_STEP                 (0x3B)
+#define DSCRV8_HALT_MASK                       (0x3C)
+
+/*DRCR registers*/
+#define DRCR_CSE                               (1 << 2)
+#define DRCR_CSPA                              (1 << 3)
+#define DRCR_CBRRQ                             (1 << 4)
+
+
+/* DTR modes */
+#define DSCR_EXT_DCC_NON_BLOCKING     (0x0 << 20)
+#define DSCR_EXT_DCC_STALL_MODE       (0x1 << 20)
+#define DSCR_EXT_DCC_FAST_MODE        (0x2 << 20)  /* bits 22, 23 are reserved */
+
+
+/* DRCR (debug run control register) bits */
+#define DRCR_HALT                              (1 << 0)
+#define DRCR_RESTART                   (1 << 1)
+#define DRCR_CLEAR_EXCEPTIONS  (1 << 2)
+
+/* PRCR (processor debug status register) bits */
+#define PRSR_PU                                        (1 << 0)
+#define PRSR_SPD                               (1 << 1)
+#define PRSR_RESET                             (1 << 2)
+#define PRSR_SR                                        (1 << 3)
+#define PRSR_HALT                              (1 << 4)
+#define PRSR_OSLK                              (1 << 5)
+#define PRSR_DLK                               (1 << 6)
+#define PRSR_EDAD                              (1 << 7)
+#define PRSR_SDAD                              (1 << 8)
+#define PRSR_EPMAD                             (1 << 9)
+#define PRSR_SPMAD                             (1 << 10)
+#define PRSR_SDR                               (1 << 11)
+
+void armv8_dpm_report_dscr(struct arm_dpm *dpm, uint32_t dcsr);
+
+#endif /* OPENOCD_TARGET_ARM_DPM_H */

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)