jtag: linuxgpiod: drop extra parenthesis
[openocd.git] / src / target / armv7m.c
index 9d89086eb2d373e54c017c5f226a92309d608579..d508af7bf0c18d47b52a136c0f09427bc36c73d7 100644 (file)
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
 /***************************************************************************
  *   Copyright (C) 2005 by Dominic Rath                                    *
  *   Dominic.Rath@gmx.de                                                   *
  *   Copyright (C) 2007,2008 Ã˜yvind Harboe                                 *
  *   oyvind.harboe@zylin.com                                               *
  *                                                                         *
- *   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.                          *
+ *   Copyright (C) 2018 by Liviu Ionescu                                   *
+ *   <ilg@livius.net>                                                      *
  *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *   Copyright (C) 2019 by Tomas Vanek                                     *
+ *   vanekt@fbl.cz                                                         *
  *                                                                         *
- *     ARMv7-M Architecture, Application Level Reference Manual               *
+ *   ARMv7-M Architecture, Application Level Reference Manual              *
  *              ARM DDI 0405C (September 2008)                             *
  *                                                                         *
  ***************************************************************************/
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 #include "armv7m.h"
 #include "algorithm.h"
 #include "register.h"
-
+#include "semihosting_common.h"
+#include <helper/log.h>
+#include <helper/binarybuffer.h>
 
 #if 0
 #define _DEBUG_INSTRUCTION_EXECUTION_
 #endif
 
-/** Maps from enum armv7m_mode (except ARMV7M_MODE_ANY) to name. */
-char *armv7m_mode_strings[] =
-{
-       "Thread", "Thread (User)", "Handler",
-};
-
-static char *armv7m_exception_strings[] =
-{
+static const char * const armv7m_exception_strings[] = {
        "", "Reset", "NMI", "HardFault",
-       "MemManage", "BusFault", "UsageFault", "RESERVED",
+       "MemManage", "BusFault", "UsageFault", "SecureFault",
        "RESERVED", "RESERVED", "RESERVED", "SVCall",
        "DebugMonitor", "RESERVED", "PendSV", "SysTick"
 };
 
-#ifdef ARMV7_GDB_HACKS
-uint8_t armv7m_gdb_dummy_cpsr_value[] = {0, 0, 0, 0};
+/* PSP is used in some thread modes */
+const int armv7m_psp_reg_map[ARMV7M_NUM_CORE_REGS] = {
+       ARMV7M_R0, ARMV7M_R1, ARMV7M_R2, ARMV7M_R3,
+       ARMV7M_R4, ARMV7M_R5, ARMV7M_R6, ARMV7M_R7,
+       ARMV7M_R8, ARMV7M_R9, ARMV7M_R10, ARMV7M_R11,
+       ARMV7M_R12, ARMV7M_PSP, ARMV7M_R14, ARMV7M_PC,
+       ARMV7M_XPSR,
+};
 
-struct reg armv7m_gdb_dummy_cpsr_reg =
-{
-       .name = "GDB dummy cpsr register",
-       .value = armv7m_gdb_dummy_cpsr_value,
-       .dirty = 0,
-       .valid = 1,
-       .size = 32,
-       .arch_info = NULL,
+/* MSP is used in handler and some thread modes */
+const int armv7m_msp_reg_map[ARMV7M_NUM_CORE_REGS] = {
+       ARMV7M_R0, ARMV7M_R1, ARMV7M_R2, ARMV7M_R3,
+       ARMV7M_R4, ARMV7M_R5, ARMV7M_R6, ARMV7M_R7,
+       ARMV7M_R8, ARMV7M_R9, ARMV7M_R10, ARMV7M_R11,
+       ARMV7M_R12, ARMV7M_MSP, ARMV7M_R14, ARMV7M_PC,
+       ARMV7M_XPSR,
 };
-#endif
 
 /*
  * These registers are not memory-mapped.  The ARMv7-M profile includes
@@ -82,40 +75,91 @@ struct reg armv7m_gdb_dummy_cpsr_reg =
  */
 static const struct {
        unsigned id;
-       char *name;
+       const char *name;
        unsigned bits;
+       enum reg_type type;
+       const char *group;
+       const char *feature;
 } armv7m_regs[] = {
-       { ARMV7M_R0, "r0", 32 },
-       { ARMV7M_R1, "r1", 32 },
-       { ARMV7M_R2, "r2", 32 },
-       { ARMV7M_R3, "r3", 32 },
-
-       { ARMV7M_R4, "r4", 32 },
-       { ARMV7M_R5, "r5", 32 },
-       { ARMV7M_R6, "r6", 32 },
-       { ARMV7M_R7, "r7", 32 },
-
-       { ARMV7M_R8, "r8", 32 },
-       { ARMV7M_R9, "r9", 32 },
-       { ARMV7M_R10, "r10", 32 },
-       { ARMV7M_R11, "r11", 32 },
-
-       { ARMV7M_R12, "r12", 32 },
-       { ARMV7M_R13, "sp", 32 },
-       { ARMV7M_R14, "lr", 32 },
-       { ARMV7M_PC, "pc", 32 },
-
-       { ARMV7M_xPSR, "xPSR", 32 },
-       { ARMV7M_MSP, "msp", 32 },
-       { ARMV7M_PSP, "psp", 32 },
-
-       { ARMV7M_PRIMASK, "primask", 1 },
-       { ARMV7M_BASEPRI, "basepri", 8 },
-       { ARMV7M_FAULTMASK, "faultmask", 1 },
-       { ARMV7M_CONTROL, "control", 2 },
+       { ARMV7M_R0, "r0", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
+       { ARMV7M_R1, "r1", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
+       { ARMV7M_R2, "r2", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
+       { ARMV7M_R3, "r3", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
+       { ARMV7M_R4, "r4", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
+       { ARMV7M_R5, "r5", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
+       { ARMV7M_R6, "r6", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
+       { ARMV7M_R7, "r7", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
+       { ARMV7M_R8, "r8", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
+       { ARMV7M_R9, "r9", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
+       { ARMV7M_R10, "r10", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
+       { ARMV7M_R11, "r11", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
+       { ARMV7M_R12, "r12", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
+       { ARMV7M_R13, "sp", 32, REG_TYPE_DATA_PTR, "general", "org.gnu.gdb.arm.m-profile" },
+       { ARMV7M_R14, "lr", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
+       { ARMV7M_PC, "pc", 32, REG_TYPE_CODE_PTR, "general", "org.gnu.gdb.arm.m-profile" },
+       { ARMV7M_XPSR, "xpsr", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
+
+       { ARMV7M_MSP, "msp", 32, REG_TYPE_DATA_PTR, "system", "org.gnu.gdb.arm.m-system" },
+       { ARMV7M_PSP, "psp", 32, REG_TYPE_DATA_PTR, "system", "org.gnu.gdb.arm.m-system" },
+
+       /* A working register for packing/unpacking special regs, hidden from gdb */
+       { ARMV7M_PMSK_BPRI_FLTMSK_CTRL, "pmsk_bpri_fltmsk_ctrl", 32, REG_TYPE_INT, NULL, NULL },
+
+       /* WARNING: If you use armv7m_write_core_reg() on one of 4 following
+        * special registers, the new data go to ARMV7M_PMSK_BPRI_FLTMSK_CTRL
+        * cache only and are not flushed to CPU HW register.
+        * To trigger write to CPU HW register, add
+        *              armv7m_write_core_reg(,,ARMV7M_PMSK_BPRI_FLTMSK_CTRL,);
+        */
+       { ARMV7M_PRIMASK, "primask", 1, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" },
+       { ARMV7M_BASEPRI, "basepri", 8, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" },
+       { ARMV7M_FAULTMASK, "faultmask", 1, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" },
+       { ARMV7M_CONTROL, "control", 3, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" },
+
+       /* ARMv8-M security extension (TrustZone) specific registers */
+       { ARMV8M_MSP_NS, "msp_ns", 32, REG_TYPE_DATA_PTR, "stack", "org.gnu.gdb.arm.secext" },
+       { ARMV8M_PSP_NS, "psp_ns", 32, REG_TYPE_DATA_PTR, "stack", "org.gnu.gdb.arm.secext" },
+       { ARMV8M_MSP_S, "msp_s", 32, REG_TYPE_DATA_PTR, "stack", "org.gnu.gdb.arm.secext" },
+       { ARMV8M_PSP_S, "psp_s", 32, REG_TYPE_DATA_PTR, "stack", "org.gnu.gdb.arm.secext" },
+       { ARMV8M_MSPLIM_S, "msplim_s", 32, REG_TYPE_DATA_PTR, "stack", "org.gnu.gdb.arm.secext" },
+       { ARMV8M_PSPLIM_S, "psplim_s", 32, REG_TYPE_DATA_PTR, "stack", "org.gnu.gdb.arm.secext" },
+       { ARMV8M_MSPLIM_NS, "msplim_ns", 32, REG_TYPE_DATA_PTR, "stack", "org.gnu.gdb.arm.secext" },
+       { ARMV8M_PSPLIM_NS, "psplim_ns", 32, REG_TYPE_DATA_PTR, "stack", "org.gnu.gdb.arm.secext" },
+
+       { ARMV8M_PMSK_BPRI_FLTMSK_CTRL_S, "pmsk_bpri_fltmsk_ctrl_s", 32, REG_TYPE_INT, NULL, NULL },
+       { ARMV8M_PRIMASK_S, "primask_s", 1, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.secext" },
+       { ARMV8M_BASEPRI_S, "basepri_s", 8, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.secext" },
+       { ARMV8M_FAULTMASK_S, "faultmask_s", 1, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.secext" },
+       { ARMV8M_CONTROL_S, "control_s", 3, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.secext" },
+
+       { ARMV8M_PMSK_BPRI_FLTMSK_CTRL_NS, "pmsk_bpri_fltmsk_ctrl_ns", 32, REG_TYPE_INT, NULL, NULL },
+       { ARMV8M_PRIMASK_NS, "primask_ns", 1, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.secext" },
+       { ARMV8M_BASEPRI_NS, "basepri_ns", 8, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.secext" },
+       { ARMV8M_FAULTMASK_NS, "faultmask_ns", 1, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.secext" },
+       { ARMV8M_CONTROL_NS, "control_ns", 3, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.secext" },
+
+       /* FPU registers */
+       { ARMV7M_D0, "d0", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
+       { ARMV7M_D1, "d1", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
+       { ARMV7M_D2, "d2", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
+       { ARMV7M_D3, "d3", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
+       { ARMV7M_D4, "d4", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
+       { ARMV7M_D5, "d5", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
+       { ARMV7M_D6, "d6", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
+       { ARMV7M_D7, "d7", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
+       { ARMV7M_D8, "d8", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
+       { ARMV7M_D9, "d9", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
+       { ARMV7M_D10, "d10", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
+       { ARMV7M_D11, "d11", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
+       { ARMV7M_D12, "d12", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
+       { ARMV7M_D13, "d13", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
+       { ARMV7M_D14, "d14", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
+       { ARMV7M_D15, "d15", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
+
+       { ARMV7M_FPSCR, "fpscr", 32, REG_TYPE_INT, "float", "org.gnu.gdb.arm.vfp" },
 };
 
-#define ARMV7M_NUM_REGS        ARRAY_SIZE(armv7m_regs)
+#define ARMV7M_NUM_REGS ARRAY_SIZE(armv7m_regs)
 
 /**
  * Restores target context using the cache of core registers set up
@@ -125,23 +169,26 @@ int armv7m_restore_context(struct target *target)
 {
        int i;
        struct armv7m_common *armv7m = target_to_armv7m(target);
+       struct reg_cache *cache = armv7m->arm.core_cache;
 
        LOG_DEBUG(" ");
 
        if (armv7m->pre_restore_context)
                armv7m->pre_restore_context(target);
 
-       for (i = ARMV7M_NUM_REGS - 1; i >= 0; i--)
-       {
-               if (armv7m->core_cache->reg_list[i].dirty)
-               {
-                       armv7m->write_core_reg(target, i);
+       /* The descending order of register writes is crucial for correct
+        * packing of ARMV7M_PMSK_BPRI_FLTMSK_CTRL!
+        * See also comments in the register table above */
+       for (i = cache->num_regs - 1; i >= 0; i--) {
+               struct reg *r = &cache->reg_list[i];
+
+               if (r->exist && r->dirty) {
+                       int retval = armv7m->arm.write_core_reg(target, r, i, ARM_MODE_ANY, r->value);
+                       if (retval != ERROR_OK)
+                               return retval;
                }
        }
 
-       if (armv7m->post_restore_context)
-               armv7m->post_restore_context(target);
-
        return ERROR_OK;
 }
 
@@ -154,7 +201,7 @@ int armv7m_restore_context(struct target *target)
  * They are assigned by vendors, which generally assign different numbers to
  * peripherals (such as UART0 or a USB peripheral controller).
  */
-char *armv7m_exception_string(int number)
+const char *armv7m_exception_string(int number)
 {
        static char enamebuf[32];
 
@@ -169,302 +216,510 @@ char *armv7m_exception_string(int number)
 static int armv7m_get_core_reg(struct reg *reg)
 {
        int retval;
-       struct armv7m_core_reg *armv7m_reg = reg->arch_info;
+       struct arm_reg *armv7m_reg = reg->arch_info;
        struct target *target = armv7m_reg->target;
-       struct armv7m_common *armv7m = target_to_armv7m(target);
+       struct arm *arm = target_to_arm(target);
 
        if (target->state != TARGET_HALTED)
-       {
                return ERROR_TARGET_NOT_HALTED;
-       }
 
-       retval = armv7m->read_core_reg(target, armv7m_reg->num);
+       retval = arm->read_core_reg(target, reg, reg->number, arm->core_mode);
 
        return retval;
 }
 
 static int armv7m_set_core_reg(struct reg *reg, uint8_t *buf)
 {
-       struct armv7m_core_reg *armv7m_reg = reg->arch_info;
+       struct arm_reg *armv7m_reg = reg->arch_info;
        struct target *target = armv7m_reg->target;
-       uint32_t value = buf_get_u32(buf, 0, 32);
 
        if (target->state != TARGET_HALTED)
-       {
                return ERROR_TARGET_NOT_HALTED;
-       }
 
-       buf_set_u32(reg->value, 0, 32, value);
-       reg->dirty = 1;
-       reg->valid = 1;
+       buf_cpy(buf, reg->value, reg->size);
+       reg->dirty = true;
+       reg->valid = true;
 
        return ERROR_OK;
 }
 
-static int armv7m_read_core_reg(struct target *target, unsigned num)
+uint32_t armv7m_map_id_to_regsel(unsigned int arm_reg_id)
+{
+       switch (arm_reg_id) {
+       case ARMV7M_R0 ... ARMV7M_R14:
+       case ARMV7M_PC:
+       case ARMV7M_XPSR:
+       case ARMV7M_MSP:
+       case ARMV7M_PSP:
+               /* NOTE:  we "know" here that the register identifiers
+                * match the Cortex-M DCRSR.REGSEL selectors values
+                * for R0..R14, PC, xPSR, MSP, and PSP.
+                */
+               return arm_reg_id;
+
+       case ARMV7M_PMSK_BPRI_FLTMSK_CTRL:
+               return ARMV7M_REGSEL_PMSK_BPRI_FLTMSK_CTRL;
+
+       case ARMV8M_MSP_NS...ARMV8M_PSPLIM_NS:
+               return arm_reg_id - ARMV8M_MSP_NS + ARMV8M_REGSEL_MSP_NS;
+
+       case ARMV8M_PMSK_BPRI_FLTMSK_CTRL_S:
+               return ARMV8M_REGSEL_PMSK_BPRI_FLTMSK_CTRL_S;
+
+       case ARMV8M_PMSK_BPRI_FLTMSK_CTRL_NS:
+               return ARMV8M_REGSEL_PMSK_BPRI_FLTMSK_CTRL_NS;
+
+       case ARMV7M_FPSCR:
+               return ARMV7M_REGSEL_FPSCR;
+
+       case ARMV7M_D0 ... ARMV7M_D15:
+               return ARMV7M_REGSEL_S0 + 2 * (arm_reg_id - ARMV7M_D0);
+
+       default:
+               LOG_ERROR("Bad register ID %u", arm_reg_id);
+               return arm_reg_id;
+       }
+}
+
+bool armv7m_map_reg_packing(unsigned int arm_reg_id,
+                                       unsigned int *reg32_id, uint32_t *offset)
+{
+
+       switch (arm_reg_id) {
+
+       case ARMV7M_PRIMASK...ARMV7M_CONTROL:
+               *reg32_id = ARMV7M_PMSK_BPRI_FLTMSK_CTRL;
+               *offset = arm_reg_id - ARMV7M_PRIMASK;
+               return true;
+       case ARMV8M_PRIMASK_S...ARMV8M_CONTROL_S:
+               *reg32_id = ARMV8M_PMSK_BPRI_FLTMSK_CTRL_S;
+               *offset = arm_reg_id - ARMV8M_PRIMASK_S;
+               return true;
+       case ARMV8M_PRIMASK_NS...ARMV8M_CONTROL_NS:
+               *reg32_id = ARMV8M_PMSK_BPRI_FLTMSK_CTRL_NS;
+               *offset = arm_reg_id - ARMV8M_PRIMASK_NS;
+               return true;
+
+       default:
+               return false;
+       }
+
+}
+
+static int armv7m_read_core_reg(struct target *target, struct reg *r,
+       int num, enum arm_mode mode)
 {
        uint32_t reg_value;
        int retval;
-       struct armv7m_core_reg * armv7m_core_reg;
        struct armv7m_common *armv7m = target_to_armv7m(target);
 
-       if (num >= ARMV7M_NUM_REGS)
-               return ERROR_INVALID_ARGUMENTS;
+       assert(num < (int)armv7m->arm.core_cache->num_regs);
+       assert(num == (int)r->number);
+
+       /* If a code calls read_reg, it expects the cache is no more dirty.
+        * Clear the dirty flag regardless of the later read succeeds or not
+        * to prevent unwanted cache flush after a read error */
+       r->dirty = false;
+
+       if (r->size <= 8) {
+               /* any 8-bit or shorter register is packed */
+               uint32_t offset;
+               unsigned int reg32_id;
+
+               bool is_packed = armv7m_map_reg_packing(num, &reg32_id, &offset);
+               if (!is_packed) {
+                       /* We should not get here as all 8-bit or shorter registers
+                        * are packed */
+                       assert(false);
+                       /* assert() does nothing if NDEBUG is defined */
+                       return ERROR_FAIL;
+               }
+               struct reg *r32 = &armv7m->arm.core_cache->reg_list[reg32_id];
 
-       armv7m_core_reg = armv7m->core_cache->reg_list[num].arch_info;
-       retval = armv7m->load_core_reg_u32(target, armv7m_core_reg->type, armv7m_core_reg->num, &reg_value);
-       buf_set_u32(armv7m->core_cache->reg_list[num].value, 0, 32, reg_value);
-       armv7m->core_cache->reg_list[num].valid = 1;
-       armv7m->core_cache->reg_list[num].dirty = 0;
+               /* Read 32-bit container register if not cached */
+               if (!r32->valid) {
+                       retval = armv7m_read_core_reg(target, r32, reg32_id, mode);
+                       if (retval != ERROR_OK)
+                               return retval;
+               }
 
-       return retval;
+               /* Copy required bits of 32-bit container register */
+               buf_cpy(r32->value + offset, r->value, r->size);
+
+       } else {
+               assert(r->size == 32 || r->size == 64);
+
+               struct arm_reg *armv7m_core_reg = r->arch_info;
+               uint32_t regsel = armv7m_map_id_to_regsel(armv7m_core_reg->num);
+
+               retval = armv7m->load_core_reg_u32(target, regsel, &reg_value);
+               if (retval != ERROR_OK)
+                       return retval;
+               buf_set_u32(r->value, 0, 32, reg_value);
+
+               if (r->size == 64) {
+                       retval = armv7m->load_core_reg_u32(target, regsel + 1, &reg_value);
+                       if (retval != ERROR_OK) {
+                               r->valid = false;
+                               return retval;
+                       }
+                       buf_set_u32(r->value + 4, 0, 32, reg_value);
+
+                       uint64_t q = buf_get_u64(r->value, 0, 64);
+                       LOG_DEBUG("read %s value 0x%016" PRIx64, r->name, q);
+               } else {
+                       LOG_DEBUG("read %s value 0x%08" PRIx32, r->name, reg_value);
+               }
+       }
+
+       r->valid = true;
+
+       return ERROR_OK;
 }
 
-static int armv7m_write_core_reg(struct target *target, unsigned num)
+static int armv7m_write_core_reg(struct target *target, struct reg *r,
+       int num, enum arm_mode mode, uint8_t *value)
 {
        int retval;
-       uint32_t reg_value;
-       struct armv7m_core_reg *armv7m_core_reg;
+       uint32_t t;
        struct armv7m_common *armv7m = target_to_armv7m(target);
 
-       if (num >= ARMV7M_NUM_REGS)
-               return ERROR_INVALID_ARGUMENTS;
+       assert(num < (int)armv7m->arm.core_cache->num_regs);
+       assert(num == (int)r->number);
 
-       reg_value = buf_get_u32(armv7m->core_cache->reg_list[num].value, 0, 32);
-       armv7m_core_reg = armv7m->core_cache->reg_list[num].arch_info;
-       retval = armv7m->store_core_reg_u32(target, armv7m_core_reg->type, armv7m_core_reg->num, reg_value);
-       if (retval != ERROR_OK)
-       {
-               LOG_ERROR("JTAG failure");
-               armv7m->core_cache->reg_list[num].dirty = armv7m->core_cache->reg_list[num].valid;
-               return ERROR_JTAG_DEVICE_ERROR;
+       if (value != r->value) {
+               /* If we are not flushing the cache, store the new value to the cache */
+               buf_cpy(value, r->value, r->size);
        }
-       LOG_DEBUG("write core reg %i value 0x%" PRIx32 "", num , reg_value);
-       armv7m->core_cache->reg_list[num].valid = 1;
-       armv7m->core_cache->reg_list[num].dirty = 0;
+
+       if (r->size <= 8) {
+               /* any 8-bit or shorter register is packed */
+               uint32_t offset;
+               unsigned int reg32_id;
+
+               bool is_packed = armv7m_map_reg_packing(num, &reg32_id, &offset);
+               if (!is_packed) {
+                       /* We should not get here as all 8-bit or shorter registers
+                        * are packed */
+                       assert(false);
+                       /* assert() does nothing if NDEBUG is defined */
+                       return ERROR_FAIL;
+               }
+               struct reg *r32 = &armv7m->arm.core_cache->reg_list[reg32_id];
+
+               if (!r32->valid) {
+                       /* Before merging with other parts ensure the 32-bit register is valid */
+                       retval = armv7m_read_core_reg(target, r32, reg32_id, mode);
+                       if (retval != ERROR_OK)
+                               return retval;
+               }
+
+               /* Write a part to the 32-bit container register */
+               buf_cpy(value, r32->value + offset, r->size);
+               r32->dirty = true;
+
+       } else {
+               assert(r->size == 32 || r->size == 64);
+
+               struct arm_reg *armv7m_core_reg = r->arch_info;
+               uint32_t regsel = armv7m_map_id_to_regsel(armv7m_core_reg->num);
+
+               t = buf_get_u32(value, 0, 32);
+               retval = armv7m->store_core_reg_u32(target, regsel, t);
+               if (retval != ERROR_OK)
+                       goto out_error;
+
+               if (r->size == 64) {
+                       t = buf_get_u32(value + 4, 0, 32);
+                       retval = armv7m->store_core_reg_u32(target, regsel + 1, t);
+                       if (retval != ERROR_OK)
+                               goto out_error;
+
+                       uint64_t q = buf_get_u64(value, 0, 64);
+                       LOG_DEBUG("write %s value 0x%016" PRIx64, r->name, q);
+               } else {
+                       LOG_DEBUG("write %s value 0x%08" PRIx32, r->name, t);
+               }
+       }
+
+       r->valid = true;
+       r->dirty = false;
 
        return ERROR_OK;
+
+out_error:
+       r->dirty = true;
+       LOG_ERROR("Error setting register %s", r->name);
+       return retval;
 }
 
 /**
  * Returns generic ARM userspace registers to GDB.
- * GDB doesn't quite understand that most ARMs don't have floating point
- * hardware, so this also fakes a set of long-obsolete FPA registers that
- * are not used in EABI based software stacks.
  */
-int armv7m_get_gdb_reg_list(struct target *target, struct reg **reg_list[], int *reg_list_size)
+int armv7m_get_gdb_reg_list(struct target *target, struct reg **reg_list[],
+               int *reg_list_size, enum target_register_class reg_class)
 {
        struct armv7m_common *armv7m = target_to_armv7m(target);
-       int i;
+       int i, size;
 
-       *reg_list_size = 26;
-       *reg_list = malloc(sizeof(struct reg*) * (*reg_list_size));
+       if (reg_class == REG_CLASS_ALL)
+               size = armv7m->arm.core_cache->num_regs;
+       else
+               size = ARMV7M_NUM_CORE_REGS;
 
-       /*
-        * GDB register packet format for ARM:
-        *  - the first 16 registers are r0..r15
-        *  - (obsolete) 8 FPA registers
-        *  - (obsolete) FPA status
-        *  - CPSR
-        */
-       for (i = 0; i < 16; i++)
-       {
-               (*reg_list)[i] = &armv7m->core_cache->reg_list[i];
-       }
-
-       for (i = 16; i < 24; i++)
-               (*reg_list)[i] = &arm_gdb_dummy_fp_reg;
-       (*reg_list)[24] = &arm_gdb_dummy_fps_reg;
+       *reg_list = malloc(sizeof(struct reg *) * size);
+       if (!*reg_list)
+               return ERROR_FAIL;
 
-#ifdef ARMV7_GDB_HACKS
-       /* use dummy cpsr reg otherwise gdb may try and set the thumb bit */
-       (*reg_list)[25] = &armv7m_gdb_dummy_cpsr_reg;
+       for (i = 0; i < size; i++)
+               (*reg_list)[i] = &armv7m->arm.core_cache->reg_list[i];
 
-       /* ARMV7M is always in thumb mode, try to make GDB understand this
-        * if it does not support this arch */
-       *((char*)armv7m->core_cache->reg_list[15].value) |= 1;
-#else
-       (*reg_list)[25] = &armv7m->core_cache->reg_list[ARMV7M_xPSR];
-#endif
+       *reg_list_size = size;
 
        return ERROR_OK;
 }
 
-/* run to exit point. return error if exit point was not reached. */
-static int armv7m_run_and_wait(struct target *target, uint32_t entry_point, int timeout_ms, uint32_t exit_point, struct armv7m_common *armv7m)
+/** Runs a Thumb algorithm in the target. */
+int armv7m_run_algorithm(struct target *target,
+       int num_mem_params, struct mem_param *mem_params,
+       int num_reg_params, struct reg_param *reg_params,
+       target_addr_t entry_point, target_addr_t exit_point,
+       unsigned int timeout_ms, void *arch_info)
 {
-       uint32_t pc;
        int retval;
-       /* This code relies on the target specific  resume() and  poll()->debug_entry()
-        * sequence to write register values to the processor and the read them back */
-       if ((retval = target_resume(target, 0, entry_point, 1, 1)) != ERROR_OK)
-       {
-               return retval;
-       }
 
-       retval = target_wait_state(target, TARGET_HALTED, timeout_ms);
-       /* If the target fails to halt due to the breakpoint, force a halt */
-       if (retval != ERROR_OK || target->state != TARGET_HALTED)
-       {
-               if ((retval = target_halt(target)) != ERROR_OK)
-                       return retval;
-               if ((retval = target_wait_state(target, TARGET_HALTED, 500)) != ERROR_OK)
-               {
-                       return retval;
-               }
-               return ERROR_TARGET_TIMEOUT;
-       }
+       retval = armv7m_start_algorithm(target,
+                       num_mem_params, mem_params,
+                       num_reg_params, reg_params,
+                       entry_point, exit_point,
+                       arch_info);
 
-       armv7m->load_core_reg_u32(target, ARMV7M_REGISTER_CORE_GP, 15, &pc);
-       if (pc != exit_point)
-       {
-               LOG_DEBUG("failed algoritm halted at 0x%" PRIx32 " ", pc);
-               return ERROR_TARGET_TIMEOUT;
-       }
+       if (retval == ERROR_OK)
+               retval = armv7m_wait_algorithm(target,
+                               num_mem_params, mem_params,
+                               num_reg_params, reg_params,
+                               exit_point, timeout_ms,
+                               arch_info);
 
-       return ERROR_OK;
+       return retval;
 }
 
-/** Runs a Thumb algorithm in the target. */
-int armv7m_run_algorithm(struct target *target,
+/** Starts a Thumb algorithm in the target. */
+int armv7m_start_algorithm(struct target *target,
        int num_mem_params, struct mem_param *mem_params,
        int num_reg_params, struct reg_param *reg_params,
-       uint32_t entry_point, uint32_t exit_point,
-       int timeout_ms, void *arch_info)
+       target_addr_t entry_point, target_addr_t exit_point,
+       void *arch_info)
 {
        struct armv7m_common *armv7m = target_to_armv7m(target);
        struct armv7m_algorithm *armv7m_algorithm_info = arch_info;
-       enum armv7m_mode core_mode = armv7m->core_mode;
+       enum arm_mode core_mode = armv7m->arm.core_mode;
        int retval = ERROR_OK;
-       uint32_t context[ARMV7M_NUM_REGS];
 
-       if (armv7m_algorithm_info->common_magic != ARMV7M_COMMON_MAGIC)
-       {
+       /* NOTE: armv7m_run_algorithm requires that each algorithm uses a software breakpoint
+        * at the exit point */
+
+       if (armv7m_algorithm_info->common_magic != ARMV7M_COMMON_MAGIC) {
                LOG_ERROR("current target isn't an ARMV7M target");
                return ERROR_TARGET_INVALID;
        }
 
-       if (target->state != TARGET_HALTED)
-       {
-               LOG_WARNING("target not halted");
+       if (target->state != TARGET_HALTED) {
+               LOG_TARGET_ERROR(target, "not halted (start target algo)");
                return ERROR_TARGET_NOT_HALTED;
        }
 
-       /* refresh core register cache */
-       /* Not needed if core register cache is always consistent with target process state */
-       for (unsigned i = 0; i < ARMV7M_NUM_REGS; i++)
-       {
-               if (!armv7m->core_cache->reg_list[i].valid)
-                       armv7m->read_core_reg(target, i);
-               context[i] = buf_get_u32(armv7m->core_cache->reg_list[i].value, 0, 32);
+       /* Store all non-debug execution registers to armv7m_algorithm_info context */
+       for (unsigned i = 0; i < armv7m->arm.core_cache->num_regs; i++) {
+               struct reg *reg = &armv7m->arm.core_cache->reg_list[i];
+               if (!reg->exist)
+                       continue;
+
+               if (!reg->valid)
+                       armv7m_get_core_reg(reg);
+
+               if (!reg->valid)
+                       LOG_TARGET_WARNING(target, "Storing invalid register %s", reg->name);
+
+               armv7m_algorithm_info->context[i] = buf_get_u32(reg->value, 0, 32);
        }
 
-       for (int i = 0; i < num_mem_params; i++)
-       {
-               if ((retval = target_write_buffer(target, mem_params[i].address, mem_params[i].size, mem_params[i].value)) != ERROR_OK)
+       for (int i = 0; i < num_mem_params; i++) {
+               if (mem_params[i].direction == PARAM_IN)
+                       continue;
+               retval = target_write_buffer(target, mem_params[i].address,
+                               mem_params[i].size,
+                               mem_params[i].value);
+               if (retval != ERROR_OK)
                        return retval;
        }
 
-       for (int i = 0; i < num_reg_params; i++)
-       {
-               struct reg *reg = register_get_by_name(armv7m->core_cache, reg_params[i].reg_name, 0);
-//             uint32_t regvalue;
+       for (int i = 0; i < num_reg_params; i++) {
+               if (reg_params[i].direction == PARAM_IN)
+                       continue;
+
+               struct reg *reg =
+                       register_get_by_name(armv7m->arm.core_cache, reg_params[i].reg_name, false);
+/*             uint32_t regvalue; */
 
-               if (!reg)
-               {
+               if (!reg) {
                        LOG_ERROR("BUG: register '%s' not found", reg_params[i].reg_name);
-                       return ERROR_INVALID_ARGUMENTS;
+                       return ERROR_COMMAND_SYNTAX_ERROR;
                }
 
-               if (reg->size != reg_params[i].size)
-               {
-                       LOG_ERROR("BUG: register '%s' size doesn't match reg_params[i].size", reg_params[i].reg_name);
-                       return ERROR_INVALID_ARGUMENTS;
+               if (reg->size != reg_params[i].size) {
+                       LOG_ERROR("BUG: register '%s' size doesn't match reg_params[i].size",
+                               reg_params[i].reg_name);
+                       return ERROR_COMMAND_SYNTAX_ERROR;
                }
 
-//             regvalue = buf_get_u32(reg_params[i].value, 0, 32);
+/*             regvalue = buf_get_u32(reg_params[i].value, 0, 32); */
                armv7m_set_core_reg(reg, reg_params[i].value);
        }
 
-       if (armv7m_algorithm_info->core_mode != ARMV7M_MODE_ANY)
        {
-               LOG_DEBUG("setting core_mode: 0x%2.2x", armv7m_algorithm_info->core_mode);
-               buf_set_u32(armv7m->core_cache->reg_list[ARMV7M_CONTROL].value,
-                               0, 1, armv7m_algorithm_info->core_mode);
-               armv7m->core_cache->reg_list[ARMV7M_CONTROL].dirty = 1;
-               armv7m->core_cache->reg_list[ARMV7M_CONTROL].valid = 1;
+               /*
+                * Ensure xPSR.T is set to avoid trying to run things in arm
+                * (non-thumb) mode, which armv7m does not support.
+                *
+                * We do this by setting the entirety of xPSR, which should
+                * remove all the unknowns about xPSR state.
+                *
+                * Because xPSR.T is populated on reset from the vector table,
+                * it might be 0 if the vector table has "bad" data in it.
+                */
+               struct reg *reg = &armv7m->arm.core_cache->reg_list[ARMV7M_XPSR];
+               buf_set_u32(reg->value, 0, 32, 0x01000000);
+               reg->valid = true;
+               reg->dirty = true;
        }
 
-       /* REVISIT speed things up (3% or so in one case) by requiring
-        * algorithms to include a BKPT instruction at each exit point.
-        * This eliminates overheads of adding/removing a breakpoint.
-        */
+       if (armv7m_algorithm_info->core_mode != ARM_MODE_ANY &&
+                       armv7m_algorithm_info->core_mode != core_mode) {
 
-       /* ARMV7M always runs in Thumb state */
-       if ((retval = breakpoint_add(target, exit_point, 2, BKPT_SOFT)) != ERROR_OK)
-       {
-               LOG_ERROR("can't add breakpoint to finish algorithm execution");
-               return ERROR_TARGET_FAILURE;
+               /* we cannot set ARM_MODE_HANDLER, so use ARM_MODE_THREAD instead */
+               if (armv7m_algorithm_info->core_mode == ARM_MODE_HANDLER) {
+                       armv7m_algorithm_info->core_mode = ARM_MODE_THREAD;
+                       LOG_INFO("ARM_MODE_HANDLER not currently supported, using ARM_MODE_THREAD instead");
+               }
+
+               LOG_DEBUG("setting core_mode: 0x%2.2x", armv7m_algorithm_info->core_mode);
+               buf_set_u32(armv7m->arm.core_cache->reg_list[ARMV7M_CONTROL].value,
+                       0, 1, armv7m_algorithm_info->core_mode);
+               armv7m->arm.core_cache->reg_list[ARMV7M_CONTROL].dirty = true;
+               armv7m->arm.core_cache->reg_list[ARMV7M_CONTROL].valid = true;
        }
 
-       retval = armv7m_run_and_wait(target, entry_point, timeout_ms, exit_point, armv7m);
+       /* save previous core mode */
+       armv7m_algorithm_info->core_mode = core_mode;
 
-       breakpoint_remove(target, exit_point);
+       retval = target_resume(target, 0, entry_point, 1, 1);
 
-       if (retval != ERROR_OK)
-       {
-               return retval;
+       return retval;
+}
+
+/** Waits for an algorithm in the target. */
+int armv7m_wait_algorithm(struct target *target,
+       int num_mem_params, struct mem_param *mem_params,
+       int num_reg_params, struct reg_param *reg_params,
+       target_addr_t exit_point, unsigned int timeout_ms,
+       void *arch_info)
+{
+       struct armv7m_common *armv7m = target_to_armv7m(target);
+       struct armv7m_algorithm *armv7m_algorithm_info = arch_info;
+       int retval = ERROR_OK;
+
+       /* NOTE: armv7m_run_algorithm requires that each algorithm uses a software breakpoint
+        * at the exit point */
+
+       if (armv7m_algorithm_info->common_magic != ARMV7M_COMMON_MAGIC) {
+               LOG_ERROR("current target isn't an ARMV7M target");
+               return ERROR_TARGET_INVALID;
+       }
+
+       retval = target_wait_state(target, TARGET_HALTED, timeout_ms);
+       /* If the target fails to halt due to the breakpoint, force a halt */
+       if (retval != ERROR_OK || target->state != TARGET_HALTED) {
+               retval = target_halt(target);
+               if (retval != ERROR_OK)
+                       return retval;
+               retval = target_wait_state(target, TARGET_HALTED, 500);
+               if (retval != ERROR_OK)
+                       return retval;
+               return ERROR_TARGET_TIMEOUT;
+       }
+
+       if (exit_point) {
+               /* PC value has been cached in cortex_m_debug_entry() */
+               uint32_t pc = buf_get_u32(armv7m->arm.pc->value, 0, 32);
+               if (pc != exit_point) {
+                       LOG_DEBUG("failed algorithm halted at 0x%" PRIx32 ", expected 0x%" TARGET_PRIxADDR,
+                                         pc, exit_point);
+                       return ERROR_TARGET_ALGO_EXIT;
+               }
        }
 
        /* Read memory values to mem_params[] */
-       for (int i = 0; i < num_mem_params; i++)
-       {
-               if (mem_params[i].direction != PARAM_OUT)
-                       if ((retval = target_read_buffer(target, mem_params[i].address, mem_params[i].size, mem_params[i].value)) != ERROR_OK)
-                       {
+       for (int i = 0; i < num_mem_params; i++) {
+               if (mem_params[i].direction != PARAM_OUT) {
+                       retval = target_read_buffer(target, mem_params[i].address,
+                                       mem_params[i].size,
+                                       mem_params[i].value);
+                       if (retval != ERROR_OK)
                                return retval;
-                       }
+               }
        }
 
        /* Copy core register values to reg_params[] */
-       for (int i = 0; i < num_reg_params; i++)
-       {
-               if (reg_params[i].direction != PARAM_OUT)
-               {
-                       struct reg *reg = register_get_by_name(armv7m->core_cache, reg_params[i].reg_name, 0);
+       for (int i = 0; i < num_reg_params; i++) {
+               if (reg_params[i].direction != PARAM_OUT) {
+                       struct reg *reg = register_get_by_name(armv7m->arm.core_cache,
+                                       reg_params[i].reg_name,
+                                       false);
 
-                       if (!reg)
-                       {
+                       if (!reg) {
                                LOG_ERROR("BUG: register '%s' not found", reg_params[i].reg_name);
-                               return ERROR_INVALID_ARGUMENTS;
+                               return ERROR_COMMAND_SYNTAX_ERROR;
                        }
 
-                       if (reg->size != reg_params[i].size)
-                       {
-                               LOG_ERROR("BUG: register '%s' size doesn't match reg_params[i].size", reg_params[i].reg_name);
-                               return ERROR_INVALID_ARGUMENTS;
+                       if (reg->size != reg_params[i].size) {
+                               LOG_ERROR(
+                                       "BUG: register '%s' size doesn't match reg_params[i].size",
+                                       reg_params[i].reg_name);
+                               return ERROR_COMMAND_SYNTAX_ERROR;
                        }
 
                        buf_set_u32(reg_params[i].value, 0, 32, buf_get_u32(reg->value, 0, 32));
                }
        }
 
-       for (int i = ARMV7M_NUM_REGS - 1; i >= 0; i--)
-       {
+       for (int i = armv7m->arm.core_cache->num_regs - 1; i >= 0; i--) {
+               struct reg *reg = &armv7m->arm.core_cache->reg_list[i];
+               if (!reg->exist)
+                       continue;
+
                uint32_t regvalue;
-               regvalue = buf_get_u32(armv7m->core_cache->reg_list[i].value, 0, 32);
-               if (regvalue != context[i])
-               {
+               regvalue = buf_get_u32(reg->value, 0, 32);
+               if (regvalue != armv7m_algorithm_info->context[i]) {
                        LOG_DEBUG("restoring register %s with value 0x%8.8" PRIx32,
-                               armv7m->core_cache->reg_list[i].name, context[i]);
-                       buf_set_u32(armv7m->core_cache->reg_list[i].value,
-                                       0, 32, context[i]);
-                       armv7m->core_cache->reg_list[i].valid = 1;
-                       armv7m->core_cache->reg_list[i].dirty = 1;
+                                         reg->name, armv7m_algorithm_info->context[i]);
+                       buf_set_u32(reg->value,
+                               0, 32, armv7m_algorithm_info->context[i]);
+                       reg->valid = true;
+                       reg->dirty = true;
                }
        }
 
-       armv7m->core_mode = core_mode;
+       /* restore previous core mode */
+       if (armv7m_algorithm_info->core_mode != armv7m->arm.core_mode) {
+               LOG_DEBUG("restoring core_mode: 0x%2.2x", armv7m_algorithm_info->core_mode);
+               buf_set_u32(armv7m->arm.core_cache->reg_list[ARMV7M_CONTROL].value,
+                       0, 1, armv7m_algorithm_info->core_mode);
+               armv7m->arm.core_cache->reg_list[ARMV7M_CONTROL].dirty = true;
+               armv7m->arm.core_cache->reg_list[ARMV7M_CONTROL].valid = true;
+       }
+
+       armv7m->arm.core_mode = armv7m_algorithm_info->core_mode;
 
        return retval;
 }
@@ -473,24 +728,32 @@ int armv7m_run_algorithm(struct target *target,
 int armv7m_arch_state(struct target *target)
 {
        struct armv7m_common *armv7m = target_to_armv7m(target);
+       struct arm *arm = &armv7m->arm;
        uint32_t ctrl, sp;
 
-       ctrl = buf_get_u32(armv7m->core_cache->reg_list[ARMV7M_CONTROL].value, 0, 32);
-       sp = buf_get_u32(armv7m->core_cache->reg_list[ARMV7M_R13].value, 0, 32);
+       /* avoid filling log waiting for fileio reply */
+       if (target->semihosting && target->semihosting->hit_fileio)
+               return ERROR_OK;
+
+       ctrl = buf_get_u32(arm->core_cache->reg_list[ARMV7M_CONTROL].value, 0, 32);
+       sp = buf_get_u32(arm->core_cache->reg_list[ARMV7M_R13].value, 0, 32);
 
-       LOG_USER("target halted due to %s, current mode: %s %s\n"
-               "xPSR: %#8.8" PRIx32 " pc: %#8.8" PRIx32 " %csp: %#8.8" PRIx32,
-               Jim_Nvp_value2name_simple(nvp_target_debug_reason,
-                               target->debug_reason)->name,
-               armv7m_mode_strings[armv7m->core_mode],
+       LOG_USER("[%s] halted due to %s, current mode: %s %s\n"
+               "xPSR: %#8.8" PRIx32 " pc: %#8.8" PRIx32 " %csp: %#8.8" PRIx32 "%s%s",
+               target_name(target),
+               debug_reason_name(target),
+               arm_mode_name(arm->core_mode),
                armv7m_exception_string(armv7m->exception_number),
-               buf_get_u32(armv7m->core_cache->reg_list[ARMV7M_xPSR].value, 0, 32),
-               buf_get_u32(armv7m->core_cache->reg_list[ARMV7M_PC].value, 0, 32),
+               buf_get_u32(arm->cpsr->value, 0, 32),
+               buf_get_u32(arm->pc->value, 0, 32),
                (ctrl & 0x02) ? 'p' : 'm',
-               sp);
+               sp,
+               (target->semihosting && target->semihosting->is_active) ? ", semihosting" : "",
+               (target->semihosting && target->semihosting->is_fileio) ? " fileio" : "");
 
        return ERROR_OK;
 }
+
 static const struct reg_arch_type armv7m_reg_type = {
        .get = armv7m_get_core_reg,
        .set = armv7m_set_core_reg,
@@ -500,110 +763,145 @@ static const struct reg_arch_type armv7m_reg_type = {
 struct reg_cache *armv7m_build_reg_cache(struct target *target)
 {
        struct armv7m_common *armv7m = target_to_armv7m(target);
+       struct arm *arm = &armv7m->arm;
        int num_regs = ARMV7M_NUM_REGS;
        struct reg_cache **cache_p = register_get_last_cache_p(&target->reg_cache);
        struct reg_cache *cache = malloc(sizeof(struct reg_cache));
        struct reg *reg_list = calloc(num_regs, sizeof(struct reg));
-       struct armv7m_core_reg *arch_info = calloc(num_regs, sizeof(struct armv7m_core_reg));
+       struct arm_reg *arch_info = calloc(num_regs, sizeof(struct arm_reg));
+       struct reg_feature *feature;
        int i;
 
-#ifdef ARMV7_GDB_HACKS
-       register_init_dummy(&armv7m_gdb_dummy_cpsr_reg);
-#endif
-
        /* Build the process context cache */
        cache->name = "arm v7m registers";
        cache->next = NULL;
        cache->reg_list = reg_list;
        cache->num_regs = num_regs;
        (*cache_p) = cache;
-       armv7m->core_cache = cache;
 
-       for (i = 0; i < num_regs; i++)
-       {
+       for (i = 0; i < num_regs; i++) {
                arch_info[i].num = armv7m_regs[i].id;
                arch_info[i].target = target;
-               arch_info[i].armv7m_common = armv7m;
+               arch_info[i].arm = arm;
+
                reg_list[i].name = armv7m_regs[i].name;
                reg_list[i].size = armv7m_regs[i].bits;
-               reg_list[i].value = calloc(1, 4);
-               reg_list[i].dirty = 0;
-               reg_list[i].valid = 0;
+               reg_list[i].value = arch_info[i].value;
+               reg_list[i].dirty = false;
+               reg_list[i].valid = false;
+               reg_list[i].hidden = (i == ARMV7M_PMSK_BPRI_FLTMSK_CTRL ||
+                               i == ARMV8M_PMSK_BPRI_FLTMSK_CTRL_NS || i == ARMV8M_PMSK_BPRI_FLTMSK_CTRL_S);
                reg_list[i].type = &armv7m_reg_type;
                reg_list[i].arch_info = &arch_info[i];
+
+               reg_list[i].group = armv7m_regs[i].group;
+               reg_list[i].number = i;
+               reg_list[i].exist = true;
+               reg_list[i].caller_save = true; /* gdb defaults to true */
+
+               if (reg_list[i].hidden)
+                       continue;
+
+               feature = calloc(1, sizeof(struct reg_feature));
+               if (feature) {
+                       feature->name = armv7m_regs[i].feature;
+                       reg_list[i].feature = feature;
+               } else
+                       LOG_ERROR("unable to allocate feature list");
+
+               reg_list[i].reg_data_type = calloc(1, sizeof(struct reg_data_type));
+               if (reg_list[i].reg_data_type)
+                       reg_list[i].reg_data_type->type = armv7m_regs[i].type;
+               else
+                       LOG_ERROR("unable to allocate reg type list");
        }
 
+       arm->cpsr = reg_list + ARMV7M_XPSR;
+       arm->pc = reg_list + ARMV7M_PC;
+       arm->core_cache = cache;
+
        return cache;
 }
 
+void armv7m_free_reg_cache(struct target *target)
+{
+       struct armv7m_common *armv7m = target_to_armv7m(target);
+       struct arm *arm = &armv7m->arm;
+       struct reg_cache *cache;
+       struct reg *reg;
+       unsigned int i;
+
+       cache = arm->core_cache;
+
+       if (!cache)
+               return;
+
+       for (i = 0; i < cache->num_regs; i++) {
+               reg = &cache->reg_list[i];
+
+               free(reg->feature);
+               free(reg->reg_data_type);
+       }
+
+       free(cache->reg_list[0].arch_info);
+       free(cache->reg_list);
+       free(cache);
+
+       arm->core_cache = NULL;
+}
+
+static int armv7m_setup_semihosting(struct target *target, int enable)
+{
+       /* nothing todo for armv7m */
+       return ERROR_OK;
+}
+
 /** Sets up target as a generic ARMv7-M core */
 int armv7m_init_arch_info(struct target *target, struct armv7m_common *armv7m)
 {
-       /* register arch-specific functions */
+       struct arm *arm = &armv7m->arm;
 
-       target->arch_info = armv7m;
-       armv7m->read_core_reg = armv7m_read_core_reg;
-       armv7m->write_core_reg = armv7m_write_core_reg;
+       armv7m->common_magic = ARMV7M_COMMON_MAGIC;
+       armv7m->fp_feature = FP_NONE;
+       armv7m->trace_config.trace_bus_id = 1;
+       /* Enable stimulus port #0 by default */
+       armv7m->trace_config.itm_ter[0] = 1;
 
-       return ERROR_OK;
+       arm->core_state = ARM_STATE_THUMB;
+       arm->core_type = ARM_CORE_TYPE_M_PROFILE;
+       arm->arch_info = armv7m;
+       arm->setup_semihosting = armv7m_setup_semihosting;
+
+       arm->read_core_reg = armv7m_read_core_reg;
+       arm->write_core_reg = armv7m_write_core_reg;
+
+       return arm_init_arch_info(target, arm);
 }
 
 /** Generates a CRC32 checksum of a memory region. */
 int armv7m_checksum_memory(struct target *target,
-               uint32_t address, uint32_t count, uint32_t* checksum)
+       target_addr_t address, uint32_t count, uint32_t *checksum)
 {
        struct working_area *crc_algorithm;
        struct armv7m_algorithm armv7m_info;
        struct reg_param reg_params[2];
        int retval;
 
-       static const uint16_t cortex_m3_crc_code[] = {
-               0x4602,                                 /* mov  r2, r0 */
-               0xF04F, 0x30FF,                 /* mov  r0, #0xffffffff */
-               0x460B,                                 /* mov  r3, r1 */
-               0xF04F, 0x0400,                 /* mov  r4, #0 */
-               0xE013,                                 /* b    ncomp */
-                                                               /* nbyte: */
-               0x5D11,                                 /* ldrb r1, [r2, r4] */
-               0xF8DF, 0x7028,                 /* ldr          r7, CRC32XOR */
-               0xEA80, 0x6001,                 /* eor          r0, r0, r1, asl #24 */
-
-               0xF04F, 0x0500,                 /* mov          r5, #0 */
-                                                               /* loop: */
-               0x2800,                                 /* cmp          r0, #0 */
-               0xEA4F, 0x0640,                 /* mov          r6, r0, asl #1 */
-               0xF105, 0x0501,                 /* add          r5, r5, #1 */
-               0x4630,                                 /* mov          r0, r6 */
-               0xBFB8,                                 /* it           lt */
-               0xEA86, 0x0007,                 /* eor          r0, r6, r7 */
-               0x2D08,                                 /* cmp          r5, #8 */
-               0xD1F4,                                 /* bne          loop */
-
-               0xF104, 0x0401,                 /* add  r4, r4, #1 */
-                                                               /* ncomp: */
-               0x429C,                                 /* cmp  r4, r3 */
-               0xD1E9,                                 /* bne  nbyte */
-                                                               /* end: */
-               0xE7FE,                                 /* b    end */
-               0x1DB7, 0x04C1                  /* CRC32XOR:    .word 0x04C11DB7 */
+       static const uint8_t cortex_m_crc_code[] = {
+#include "../../contrib/loaders/checksum/armv7m_crc.inc"
        };
 
-       uint32_t i;
-
-       if (target_alloc_working_area(target, sizeof(cortex_m3_crc_code), &crc_algorithm) != ERROR_OK)
-       {
-               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
-       }
+       retval = target_alloc_working_area(target, sizeof(cortex_m_crc_code), &crc_algorithm);
+       if (retval != ERROR_OK)
+               return retval;
 
-       /* convert flash writing code into a buffer in target endianness */
-       for (i = 0; i < (sizeof(cortex_m3_crc_code)/sizeof(uint16_t)); i++)
-               if ((retval = target_write_u16(target, crc_algorithm->address + i*sizeof(uint16_t), cortex_m3_crc_code[i])) != ERROR_OK)
-               {
-                       return retval;
-               }
+       retval = target_write_buffer(target, crc_algorithm->address,
+                       sizeof(cortex_m_crc_code), (uint8_t *)cortex_m_crc_code);
+       if (retval != ERROR_OK)
+               goto cleanup;
 
        armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
-       armv7m_info.core_mode = ARMV7M_MODE_ANY;
+       armv7m_info.core_mode = ARM_MODE_THREAD;
 
        init_reg_param(&reg_params[0], "r0", 32, PARAM_IN_OUT);
        init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
@@ -611,233 +909,200 @@ int armv7m_checksum_memory(struct target *target,
        buf_set_u32(reg_params[0].value, 0, 32, address);
        buf_set_u32(reg_params[1].value, 0, 32, count);
 
-       if ((retval = target_run_algorithm(target, 0, NULL, 2, reg_params,
-               crc_algorithm->address, crc_algorithm->address + (sizeof(cortex_m3_crc_code)-6), 20000, &armv7m_info)) != ERROR_OK)
-       {
-               LOG_ERROR("error executing cortex_m3 crc algorithm");
-               destroy_reg_param(&reg_params[0]);
-               destroy_reg_param(&reg_params[1]);
-               target_free_working_area(target, crc_algorithm);
-               return retval;
-       }
+       unsigned int timeout = 20000 * (1 + (count / (1024 * 1024)));
+
+       retval = target_run_algorithm(target, 0, NULL, 2, reg_params, crc_algorithm->address,
+                       crc_algorithm->address + (sizeof(cortex_m_crc_code) - 6),
+                       timeout, &armv7m_info);
 
-       *checksum = buf_get_u32(reg_params[0].value, 0, 32);
+       if (retval == ERROR_OK)
+               *checksum = buf_get_u32(reg_params[0].value, 0, 32);
+       else
+               LOG_ERROR("error executing cortex_m crc algorithm");
 
        destroy_reg_param(&reg_params[0]);
        destroy_reg_param(&reg_params[1]);
 
+cleanup:
        target_free_working_area(target, crc_algorithm);
 
-       return ERROR_OK;
+       return retval;
 }
 
-/** Checks whether a memory region is zeroed. */
+/** Checks an array of memory regions whether they are erased. */
 int armv7m_blank_check_memory(struct target *target,
-               uint32_t address, uint32_t count, uint32_t* blank)
+       struct target_memory_check_block *blocks, int num_blocks, uint8_t erased_value)
 {
        struct working_area *erase_check_algorithm;
-       struct reg_param reg_params[3];
+       struct working_area *erase_check_params;
+       struct reg_param reg_params[2];
        struct armv7m_algorithm armv7m_info;
        int retval;
-       uint32_t i;
 
-       static const uint16_t erase_check_code[] =
-       {
-               /* loop: */
-               0xF810, 0x3B01,         /* ldrb r3, [r0], #1 */
-               0xEA02, 0x0203,         /* and  r2, r2, r3 */
-               0x3901,                         /* subs r1, r1, #1 */
-               0xD1F9,                         /* bne  loop */
-               /* end: */
-               0xE7FE,                         /* b    end */
+       static bool timed_out;
+
+       static const uint8_t erase_check_code[] = {
+#include "../../contrib/loaders/erase_check/armv7m_erase_check.inc"
        };
 
+       const uint32_t code_size = sizeof(erase_check_code);
+
        /* make sure we have a working area */
-       if (target_alloc_working_area(target, sizeof(erase_check_code), &erase_check_algorithm) != ERROR_OK)
-       {
+       if (target_alloc_working_area(target, code_size,
+               &erase_check_algorithm) != ERROR_OK)
                return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
-       }
-
-       /* convert flash writing code into a buffer in target endianness */
-       for (i = 0; i < (sizeof(erase_check_code)/sizeof(uint16_t)); i++)
-               target_write_u16(target, erase_check_algorithm->address + i*sizeof(uint16_t), erase_check_code[i]);
-
-       armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
-       armv7m_info.core_mode = ARMV7M_MODE_ANY;
 
-       init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
-       buf_set_u32(reg_params[0].value, 0, 32, address);
-
-       init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
-       buf_set_u32(reg_params[1].value, 0, 32, count);
+       retval = target_write_buffer(target, erase_check_algorithm->address,
+                       code_size, erase_check_code);
+       if (retval != ERROR_OK)
+               goto cleanup1;
+
+       /* prepare blocks array for algo */
+       struct algo_block {
+               union {
+                       uint32_t size;
+                       uint32_t result;
+               };
+               uint32_t address;
+       };
 
-       init_reg_param(&reg_params[2], "r2", 32, PARAM_IN_OUT);
-       buf_set_u32(reg_params[2].value, 0, 32, 0xff);
+       uint32_t avail = target_get_working_area_avail(target);
+       int blocks_to_check = avail / sizeof(struct algo_block) - 1;
+       if (num_blocks < blocks_to_check)
+               blocks_to_check = num_blocks;
 
-       if ((retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
-                       erase_check_algorithm->address, erase_check_algorithm->address + (sizeof(erase_check_code)-2), 10000, &armv7m_info)) != ERROR_OK)
-       {
-               destroy_reg_param(&reg_params[0]);
-               destroy_reg_param(&reg_params[1]);
-               destroy_reg_param(&reg_params[2]);
-               target_free_working_area(target, erase_check_algorithm);
-               return 0;
+       struct algo_block *params = malloc((blocks_to_check+1)*sizeof(struct algo_block));
+       if (!params) {
+               retval = ERROR_FAIL;
+               goto cleanup1;
        }
 
-       *blank = buf_get_u32(reg_params[2].value, 0, 32);
-
-       destroy_reg_param(&reg_params[0]);
-       destroy_reg_param(&reg_params[1]);
-       destroy_reg_param(&reg_params[2]);
+       int i;
+       uint32_t total_size = 0;
+       for (i = 0; i < blocks_to_check; i++) {
+               total_size += blocks[i].size;
+               target_buffer_set_u32(target, (uint8_t *)&(params[i].size),
+                                               blocks[i].size / sizeof(uint32_t));
+               target_buffer_set_u32(target, (uint8_t *)&(params[i].address),
+                                               blocks[i].address);
+       }
+       target_buffer_set_u32(target, (uint8_t *)&(params[blocks_to_check].size), 0);
 
-       target_free_working_area(target, erase_check_algorithm);
+       uint32_t param_size = (blocks_to_check + 1) * sizeof(struct algo_block);
+       if (target_alloc_working_area(target, param_size,
+                       &erase_check_params) != ERROR_OK) {
+               retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+               goto cleanup2;
+       }
 
-       return ERROR_OK;
-}
+       retval = target_write_buffer(target, erase_check_params->address,
+                               param_size, (uint8_t *)params);
+       if (retval != ERROR_OK)
+               goto cleanup3;
 
-/*--------------------------------------------------------------------------*/
+       uint32_t erased_word = erased_value | (erased_value << 8)
+                              | (erased_value << 16) | (erased_value << 24);
 
-/*
- * Only stuff below this line should need to verify that its target
- * is an ARMv7-M node.
- *
- * FIXME yet none of it _does_ verify target types yet!
- */
+       LOG_DEBUG("Starting erase check of %d blocks, parameters@"
+                TARGET_ADDR_FMT, blocks_to_check, erase_check_params->address);
 
+       armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+       armv7m_info.core_mode = ARM_MODE_THREAD;
 
-/*
- * Return the debug ap baseaddress in hexadecimal;
- * no extra output to simplify script processing
- */
-COMMAND_HANDLER(handle_dap_baseaddr_command)
-{
-       struct target *target = get_current_target(CMD_CTX);
-       struct armv7m_common *armv7m = target_to_armv7m(target);
-       struct swjdp_common *swjdp = &armv7m->swjdp_info;
-       uint32_t apsel, apselsave, baseaddr;
-       int retval;
+       init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
+       buf_set_u32(reg_params[0].value, 0, 32, erase_check_params->address);
 
-       apselsave = swjdp->apsel;
-       switch (CMD_ARGC) {
-       case 0:
-               apsel = swjdp->apsel;
-               break;
-       case 1:
-               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel);
-               break;
-       default:
-               return ERROR_COMMAND_SYNTAX_ERROR;
-       }
+       init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
+       buf_set_u32(reg_params[1].value, 0, 32, erased_word);
 
-       if (apselsave != apsel)
-               dap_ap_select(swjdp, apsel);
+       /* assume CPU clk at least 1 MHz */
+       unsigned int timeout = (timed_out ? 30000 : 2000) + total_size * 3 / 1000;
 
-       dap_ap_read_reg_u32(swjdp, 0xF8, &baseaddr);
-       retval = swjdp_transaction_endcheck(swjdp);
-       command_print(CMD_CTX, "0x%8.8" PRIx32 "", baseaddr);
+       retval = target_run_algorithm(target,
+                               0, NULL,
+                               ARRAY_SIZE(reg_params), reg_params,
+                               erase_check_algorithm->address,
+                               erase_check_algorithm->address + (code_size - 2),
+                               timeout,
+                               &armv7m_info);
 
-       if (apselsave != apsel)
-               dap_ap_select(swjdp, apselsave);
+       timed_out = retval == ERROR_TARGET_TIMEOUT;
+       if (retval != ERROR_OK && !timed_out)
+               goto cleanup4;
 
-       return retval;
-}
+       retval = target_read_buffer(target, erase_check_params->address,
+                               param_size, (uint8_t *)params);
+       if (retval != ERROR_OK)
+               goto cleanup4;
 
-/*
- * Return the debug ap id in hexadecimal;
- * no extra output to simplify script processing
- */
-COMMAND_HANDLER(handle_dap_apid_command)
-{
-       struct target *target = get_current_target(CMD_CTX);
-       struct armv7m_common *armv7m = target_to_armv7m(target);
-       struct swjdp_common *swjdp = &armv7m->swjdp_info;
+       for (i = 0; i < blocks_to_check; i++) {
+               uint32_t result = target_buffer_get_u32(target,
+                                       (uint8_t *)&(params[i].result));
+               if (result != 0 && result != 1)
+                       break;
 
-       return CALL_COMMAND_HANDLER(dap_apid_command, swjdp);
-}
+               blocks[i].result = result;
+       }
+       if (i && timed_out)
+               LOG_INFO("Slow CPU clock: %d blocks checked, %d remain. Continuing...", i, num_blocks-i);
 
-COMMAND_HANDLER(handle_dap_apsel_command)
-{
-       struct target *target = get_current_target(CMD_CTX);
-       struct armv7m_common *armv7m = target_to_armv7m(target);
-       struct swjdp_common *swjdp = &armv7m->swjdp_info;
+       retval = i;             /* return number of blocks really checked */
 
-       return CALL_COMMAND_HANDLER(dap_apsel_command, swjdp);
-}
+cleanup4:
+       destroy_reg_param(&reg_params[0]);
+       destroy_reg_param(&reg_params[1]);
 
-COMMAND_HANDLER(handle_dap_memaccess_command)
-{
-       struct target *target = get_current_target(CMD_CTX);
-       struct armv7m_common *armv7m = target_to_armv7m(target);
-       struct swjdp_common *swjdp = &armv7m->swjdp_info;
+cleanup3:
+       target_free_working_area(target, erase_check_params);
+cleanup2:
+       free(params);
+cleanup1:
+       target_free_working_area(target, erase_check_algorithm);
 
-       return CALL_COMMAND_HANDLER(dap_memaccess_command, swjdp);
+       return retval;
 }
 
-
-COMMAND_HANDLER(handle_dap_info_command)
+int armv7m_maybe_skip_bkpt_inst(struct target *target, bool *inst_found)
 {
-       struct target *target = get_current_target(CMD_CTX);
        struct armv7m_common *armv7m = target_to_armv7m(target);
-       struct swjdp_common *swjdp = &armv7m->swjdp_info;
-       uint32_t apsel;
-
-       switch (CMD_ARGC) {
-       case 0:
-               apsel = swjdp->apsel;
-               break;
-       case 1:
-               COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], apsel);
-               break;
-       default:
-               return ERROR_COMMAND_SYNTAX_ERROR;
+       struct reg *r = armv7m->arm.pc;
+       bool result = false;
+
+
+       /* if we halted last time due to a bkpt instruction
+        * then we have to manually step over it, otherwise
+        * the core will break again */
+
+       if (target->debug_reason == DBG_REASON_BREAKPOINT) {
+               uint16_t op;
+               uint32_t pc = buf_get_u32(r->value, 0, 32);
+
+               pc &= ~1;
+               if (target_read_u16(target, pc, &op) == ERROR_OK) {
+                       if ((op & 0xFF00) == 0xBE00) {
+                               pc = buf_get_u32(r->value, 0, 32) + 2;
+                               buf_set_u32(r->value, 0, 32, pc);
+                               r->dirty = true;
+                               r->valid = true;
+                               result = true;
+                               LOG_DEBUG("Skipping over BKPT instruction");
+                       }
+               }
        }
 
-       return dap_info_command(CMD_CTX, swjdp, apsel);
+       if (inst_found)
+               *inst_found = result;
+
+       return ERROR_OK;
 }
 
-static const struct command_registration armv7m_exec_command_handlers[] = {
-       {
-               .name = "info",
-               .handler = &handle_dap_info_command,
-               .mode = COMMAND_EXEC,
-               .help = "dap info for ap [num], "
-                       "default currently selected AP",
-       },
-       {
-               .name = "apsel",
-               .handler = &handle_dap_apsel_command,
-               .mode = COMMAND_EXEC,
-               .help = "select a different AP [num] (default 0)",
-       },
-       {
-               .name = "apid",
-               .handler = &handle_dap_apid_command,
-               .mode = COMMAND_EXEC,
-               .help = "return id reg from AP [num], "
-                       "default currently selected AP",
-       },
-       {
-               .name = "baseaddr",
-               .handler = &handle_dap_baseaddr_command,
-               .mode = COMMAND_EXEC,
-               .help = "return debug base address from AP [num], "
-                       "default currently selected AP",
-       },
-       {
-               .name = "memaccess",
-               .handler = &handle_dap_memaccess_command,
-               .mode = COMMAND_EXEC,
-               .help = "set/get number of extra tck for mem-ap memory "
-                       "bus access [0-255]",
-       },
-       COMMAND_REGISTRATION_DONE
-};
 const struct command_registration armv7m_command_handlers[] = {
        {
-               .name = "dap",
+               .name = "arm",
                .mode = COMMAND_ANY,
-               .help = "Cortex DAP command group",
-               .chain = armv7m_exec_command_handlers,
+               .help = "ARM command group",
+               .usage = "",
+               .chain = arm_all_profiles_command_handlers,
        },
        COMMAND_REGISTRATION_DONE
 };

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)