d9d01923a25762b40b05d07dacb85ae83765ae07
[openocd.git] / src / target / armv7m.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /***************************************************************************
4 * Copyright (C) 2005 by Dominic Rath *
5 * Dominic.Rath@gmx.de *
6 * *
7 * Copyright (C) 2006 by Magnus Lundin *
8 * lundin@mlu.mine.nu *
9 * *
10 * Copyright (C) 2008 by Spencer Oliver *
11 * spen@spen-soft.co.uk *
12 * *
13 * Copyright (C) 2007,2008 Øyvind Harboe *
14 * oyvind.harboe@zylin.com *
15 * *
16 * Copyright (C) 2018 by Liviu Ionescu *
17 * <ilg@livius.net> *
18 * *
19 * Copyright (C) 2019 by Tomas Vanek *
20 * vanekt@fbl.cz *
21 * *
22 * ARMv7-M Architecture, Application Level Reference Manual *
23 * ARM DDI 0405C (September 2008) *
24 * *
25 ***************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include "breakpoints.h"
32 #include "armv7m.h"
33 #include "algorithm.h"
34 #include "register.h"
35 #include "semihosting_common.h"
36 #include <helper/log.h>
37 #include <helper/binarybuffer.h>
38
39 #if 0
40 #define _DEBUG_INSTRUCTION_EXECUTION_
41 #endif
42
43 static const char * const armv7m_exception_strings[] = {
44 "", "Reset", "NMI", "HardFault",
45 "MemManage", "BusFault", "UsageFault", "SecureFault",
46 "RESERVED", "RESERVED", "RESERVED", "SVCall",
47 "DebugMonitor", "RESERVED", "PendSV", "SysTick"
48 };
49
50 /* PSP is used in some thread modes */
51 const int armv7m_psp_reg_map[ARMV7M_NUM_CORE_REGS] = {
52 ARMV7M_R0, ARMV7M_R1, ARMV7M_R2, ARMV7M_R3,
53 ARMV7M_R4, ARMV7M_R5, ARMV7M_R6, ARMV7M_R7,
54 ARMV7M_R8, ARMV7M_R9, ARMV7M_R10, ARMV7M_R11,
55 ARMV7M_R12, ARMV7M_PSP, ARMV7M_R14, ARMV7M_PC,
56 ARMV7M_XPSR,
57 };
58
59 /* MSP is used in handler and some thread modes */
60 const int armv7m_msp_reg_map[ARMV7M_NUM_CORE_REGS] = {
61 ARMV7M_R0, ARMV7M_R1, ARMV7M_R2, ARMV7M_R3,
62 ARMV7M_R4, ARMV7M_R5, ARMV7M_R6, ARMV7M_R7,
63 ARMV7M_R8, ARMV7M_R9, ARMV7M_R10, ARMV7M_R11,
64 ARMV7M_R12, ARMV7M_MSP, ARMV7M_R14, ARMV7M_PC,
65 ARMV7M_XPSR,
66 };
67
68 /*
69 * These registers are not memory-mapped. The ARMv7-M profile includes
70 * memory mapped registers too, such as for the NVIC (interrupt controller)
71 * and SysTick (timer) modules; those can mostly be treated as peripherals.
72 *
73 * The ARMv6-M profile is almost identical in this respect, except that it
74 * doesn't include basepri or faultmask registers.
75 */
76 static const struct {
77 unsigned id;
78 const char *name;
79 unsigned bits;
80 enum reg_type type;
81 const char *group;
82 const char *feature;
83 } armv7m_regs[] = {
84 { ARMV7M_R0, "r0", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
85 { ARMV7M_R1, "r1", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
86 { ARMV7M_R2, "r2", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
87 { ARMV7M_R3, "r3", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
88 { ARMV7M_R4, "r4", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
89 { ARMV7M_R5, "r5", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
90 { ARMV7M_R6, "r6", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
91 { ARMV7M_R7, "r7", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
92 { ARMV7M_R8, "r8", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
93 { ARMV7M_R9, "r9", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
94 { ARMV7M_R10, "r10", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
95 { ARMV7M_R11, "r11", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
96 { ARMV7M_R12, "r12", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
97 { ARMV7M_R13, "sp", 32, REG_TYPE_DATA_PTR, "general", "org.gnu.gdb.arm.m-profile" },
98 { ARMV7M_R14, "lr", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
99 { ARMV7M_PC, "pc", 32, REG_TYPE_CODE_PTR, "general", "org.gnu.gdb.arm.m-profile" },
100 { ARMV7M_XPSR, "xPSR", 32, REG_TYPE_INT, "general", "org.gnu.gdb.arm.m-profile" },
101
102 { ARMV7M_MSP, "msp", 32, REG_TYPE_DATA_PTR, "system", "org.gnu.gdb.arm.m-system" },
103 { ARMV7M_PSP, "psp", 32, REG_TYPE_DATA_PTR, "system", "org.gnu.gdb.arm.m-system" },
104
105 /* A working register for packing/unpacking special regs, hidden from gdb */
106 { ARMV7M_PMSK_BPRI_FLTMSK_CTRL, "pmsk_bpri_fltmsk_ctrl", 32, REG_TYPE_INT, NULL, NULL },
107
108 /* WARNING: If you use armv7m_write_core_reg() on one of 4 following
109 * special registers, the new data go to ARMV7M_PMSK_BPRI_FLTMSK_CTRL
110 * cache only and are not flushed to CPU HW register.
111 * To trigger write to CPU HW register, add
112 * armv7m_write_core_reg(,,ARMV7M_PMSK_BPRI_FLTMSK_CTRL,);
113 */
114 { ARMV7M_PRIMASK, "primask", 1, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" },
115 { ARMV7M_BASEPRI, "basepri", 8, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" },
116 { ARMV7M_FAULTMASK, "faultmask", 1, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" },
117 { ARMV7M_CONTROL, "control", 3, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" },
118
119 /* ARMv8-M specific registers */
120 { ARMV8M_MSP_NS, "msp_ns", 32, REG_TYPE_DATA_PTR, "stack", "v8-m.sp" },
121 { ARMV8M_PSP_NS, "psp_ns", 32, REG_TYPE_DATA_PTR, "stack", "v8-m.sp" },
122 { ARMV8M_MSP_S, "msp_s", 32, REG_TYPE_DATA_PTR, "stack", "v8-m.sp" },
123 { ARMV8M_PSP_S, "psp_s", 32, REG_TYPE_DATA_PTR, "stack", "v8-m.sp" },
124 { ARMV8M_MSPLIM_S, "msplim_s", 32, REG_TYPE_DATA_PTR, "stack", "v8-m.sp" },
125 { ARMV8M_PSPLIM_S, "psplim_s", 32, REG_TYPE_DATA_PTR, "stack", "v8-m.sp" },
126 { ARMV8M_MSPLIM_NS, "msplim_ns", 32, REG_TYPE_DATA_PTR, "stack", "v8-m.sp" },
127 { ARMV8M_PSPLIM_NS, "psplim_ns", 32, REG_TYPE_DATA_PTR, "stack", "v8-m.sp" },
128
129 { ARMV8M_PMSK_BPRI_FLTMSK_CTRL_S, "pmsk_bpri_fltmsk_ctrl_s", 32, REG_TYPE_INT, NULL, NULL },
130 { ARMV8M_PRIMASK_S, "primask_s", 1, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" },
131 { ARMV8M_BASEPRI_S, "basepri_s", 8, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" },
132 { ARMV8M_FAULTMASK_S, "faultmask_s", 1, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" },
133 { ARMV8M_CONTROL_S, "control_s", 3, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" },
134
135 { ARMV8M_PMSK_BPRI_FLTMSK_CTRL_NS, "pmsk_bpri_fltmsk_ctrl_ns", 32, REG_TYPE_INT, NULL, NULL },
136 { ARMV8M_PRIMASK_NS, "primask_ns", 1, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" },
137 { ARMV8M_BASEPRI_NS, "basepri_ns", 8, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" },
138 { ARMV8M_FAULTMASK_NS, "faultmask_ns", 1, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" },
139 { ARMV8M_CONTROL_NS, "control_ns", 3, REG_TYPE_INT8, "system", "org.gnu.gdb.arm.m-system" },
140
141 /* FPU registers */
142 { ARMV7M_D0, "d0", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
143 { ARMV7M_D1, "d1", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
144 { ARMV7M_D2, "d2", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
145 { ARMV7M_D3, "d3", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
146 { ARMV7M_D4, "d4", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
147 { ARMV7M_D5, "d5", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
148 { ARMV7M_D6, "d6", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
149 { ARMV7M_D7, "d7", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
150 { ARMV7M_D8, "d8", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
151 { ARMV7M_D9, "d9", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
152 { ARMV7M_D10, "d10", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
153 { ARMV7M_D11, "d11", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
154 { ARMV7M_D12, "d12", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
155 { ARMV7M_D13, "d13", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
156 { ARMV7M_D14, "d14", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
157 { ARMV7M_D15, "d15", 64, REG_TYPE_IEEE_DOUBLE, "float", "org.gnu.gdb.arm.vfp" },
158
159 { ARMV7M_FPSCR, "fpscr", 32, REG_TYPE_INT, "float", "org.gnu.gdb.arm.vfp" },
160 };
161
162 #define ARMV7M_NUM_REGS ARRAY_SIZE(armv7m_regs)
163
164 /**
165 * Restores target context using the cache of core registers set up
166 * by armv7m_build_reg_cache(), calling optional core-specific hooks.
167 */
168 int armv7m_restore_context(struct target *target)
169 {
170 int i;
171 struct armv7m_common *armv7m = target_to_armv7m(target);
172 struct reg_cache *cache = armv7m->arm.core_cache;
173
174 LOG_DEBUG(" ");
175
176 if (armv7m->pre_restore_context)
177 armv7m->pre_restore_context(target);
178
179 /* The descending order of register writes is crucial for correct
180 * packing of ARMV7M_PMSK_BPRI_FLTMSK_CTRL!
181 * See also comments in the register table above */
182 for (i = cache->num_regs - 1; i >= 0; i--) {
183 struct reg *r = &cache->reg_list[i];
184
185 if (r->exist && r->dirty)
186 armv7m->arm.write_core_reg(target, r, i, ARM_MODE_ANY, r->value);
187 }
188
189 return ERROR_OK;
190 }
191
192 /* Core state functions */
193
194 /**
195 * Maps ISR number (from xPSR) to name.
196 * Note that while names and meanings for the first sixteen are standardized
197 * (with zero not a true exception), external interrupts are only numbered.
198 * They are assigned by vendors, which generally assign different numbers to
199 * peripherals (such as UART0 or a USB peripheral controller).
200 */
201 const char *armv7m_exception_string(int number)
202 {
203 static char enamebuf[32];
204
205 if ((number < 0) | (number > 511))
206 return "Invalid exception";
207 if (number < 16)
208 return armv7m_exception_strings[number];
209 sprintf(enamebuf, "External Interrupt(%i)", number - 16);
210 return enamebuf;
211 }
212
213 static int armv7m_get_core_reg(struct reg *reg)
214 {
215 int retval;
216 struct arm_reg *armv7m_reg = reg->arch_info;
217 struct target *target = armv7m_reg->target;
218 struct arm *arm = target_to_arm(target);
219
220 if (target->state != TARGET_HALTED)
221 return ERROR_TARGET_NOT_HALTED;
222
223 retval = arm->read_core_reg(target, reg, reg->number, arm->core_mode);
224
225 return retval;
226 }
227
228 static int armv7m_set_core_reg(struct reg *reg, uint8_t *buf)
229 {
230 struct arm_reg *armv7m_reg = reg->arch_info;
231 struct target *target = armv7m_reg->target;
232
233 if (target->state != TARGET_HALTED)
234 return ERROR_TARGET_NOT_HALTED;
235
236 buf_cpy(buf, reg->value, reg->size);
237 reg->dirty = true;
238 reg->valid = true;
239
240 return ERROR_OK;
241 }
242
243 uint32_t armv7m_map_id_to_regsel(unsigned int arm_reg_id)
244 {
245 switch (arm_reg_id) {
246 case ARMV7M_R0 ... ARMV7M_R14:
247 case ARMV7M_PC:
248 case ARMV7M_XPSR:
249 case ARMV7M_MSP:
250 case ARMV7M_PSP:
251 /* NOTE: we "know" here that the register identifiers
252 * match the Cortex-M DCRSR.REGSEL selectors values
253 * for R0..R14, PC, xPSR, MSP, and PSP.
254 */
255 return arm_reg_id;
256
257 case ARMV7M_PMSK_BPRI_FLTMSK_CTRL:
258 return ARMV7M_REGSEL_PMSK_BPRI_FLTMSK_CTRL;
259
260 case ARMV8M_MSP_NS...ARMV8M_PSPLIM_NS:
261 return arm_reg_id - ARMV8M_MSP_NS + ARMV8M_REGSEL_MSP_NS;
262
263 case ARMV8M_PMSK_BPRI_FLTMSK_CTRL_S:
264 return ARMV8M_REGSEL_PMSK_BPRI_FLTMSK_CTRL_S;
265
266 case ARMV8M_PMSK_BPRI_FLTMSK_CTRL_NS:
267 return ARMV8M_REGSEL_PMSK_BPRI_FLTMSK_CTRL_NS;
268
269 case ARMV7M_FPSCR:
270 return ARMV7M_REGSEL_FPSCR;
271
272 case ARMV7M_D0 ... ARMV7M_D15:
273 return ARMV7M_REGSEL_S0 + 2 * (arm_reg_id - ARMV7M_D0);
274
275 default:
276 LOG_ERROR("Bad register ID %u", arm_reg_id);
277 return arm_reg_id;
278 }
279 }
280
281 bool armv7m_map_reg_packing(unsigned int arm_reg_id,
282 unsigned int *reg32_id, uint32_t *offset)
283 {
284
285 switch (arm_reg_id) {
286
287 case ARMV7M_PRIMASK...ARMV7M_CONTROL:
288 *reg32_id = ARMV7M_PMSK_BPRI_FLTMSK_CTRL;
289 *offset = arm_reg_id - ARMV7M_PRIMASK;
290 return true;
291 case ARMV8M_PRIMASK_S...ARMV8M_CONTROL_S:
292 *reg32_id = ARMV8M_PMSK_BPRI_FLTMSK_CTRL_S;
293 *offset = arm_reg_id - ARMV8M_PRIMASK_S;
294 return true;
295 case ARMV8M_PRIMASK_NS...ARMV8M_CONTROL_NS:
296 *reg32_id = ARMV8M_PMSK_BPRI_FLTMSK_CTRL_NS;
297 *offset = arm_reg_id - ARMV8M_PRIMASK_NS;
298 return true;
299
300 default:
301 return false;
302 }
303
304 }
305
306 static int armv7m_read_core_reg(struct target *target, struct reg *r,
307 int num, enum arm_mode mode)
308 {
309 uint32_t reg_value;
310 int retval;
311 struct armv7m_common *armv7m = target_to_armv7m(target);
312
313 assert(num < (int)armv7m->arm.core_cache->num_regs);
314 assert(num == (int)r->number);
315
316 /* If a code calls read_reg, it expects the cache is no more dirty.
317 * Clear the dirty flag regardless of the later read succeeds or not
318 * to prevent unwanted cache flush after a read error */
319 r->dirty = false;
320
321 if (r->size <= 8) {
322 /* any 8-bit or shorter register is packed */
323 uint32_t offset;
324 unsigned int reg32_id;
325
326 bool is_packed = armv7m_map_reg_packing(num, &reg32_id, &offset);
327 if (!is_packed) {
328 /* We should not get here as all 8-bit or shorter registers
329 * are packed */
330 assert(false);
331 /* assert() does nothing if NDEBUG is defined */
332 return ERROR_FAIL;
333 }
334 struct reg *r32 = &armv7m->arm.core_cache->reg_list[reg32_id];
335
336 /* Read 32-bit container register if not cached */
337 if (!r32->valid) {
338 retval = armv7m_read_core_reg(target, r32, reg32_id, mode);
339 if (retval != ERROR_OK)
340 return retval;
341 }
342
343 /* Copy required bits of 32-bit container register */
344 buf_cpy(r32->value + offset, r->value, r->size);
345
346 } else {
347 assert(r->size == 32 || r->size == 64);
348
349 struct arm_reg *armv7m_core_reg = r->arch_info;
350 uint32_t regsel = armv7m_map_id_to_regsel(armv7m_core_reg->num);
351
352 retval = armv7m->load_core_reg_u32(target, regsel, &reg_value);
353 if (retval != ERROR_OK)
354 return retval;
355 buf_set_u32(r->value, 0, 32, reg_value);
356
357 if (r->size == 64) {
358 retval = armv7m->load_core_reg_u32(target, regsel + 1, &reg_value);
359 if (retval != ERROR_OK) {
360 r->valid = false;
361 return retval;
362 }
363 buf_set_u32(r->value + 4, 0, 32, reg_value);
364
365 uint64_t q = buf_get_u64(r->value, 0, 64);
366 LOG_DEBUG("read %s value 0x%016" PRIx64, r->name, q);
367 } else {
368 LOG_DEBUG("read %s value 0x%08" PRIx32, r->name, reg_value);
369 }
370 }
371
372 r->valid = true;
373
374 return ERROR_OK;
375 }
376
377 static int armv7m_write_core_reg(struct target *target, struct reg *r,
378 int num, enum arm_mode mode, uint8_t *value)
379 {
380 int retval;
381 uint32_t t;
382 struct armv7m_common *armv7m = target_to_armv7m(target);
383
384 assert(num < (int)armv7m->arm.core_cache->num_regs);
385 assert(num == (int)r->number);
386
387 if (value != r->value) {
388 /* If we are not flushing the cache, store the new value to the cache */
389 buf_cpy(value, r->value, r->size);
390 }
391
392 if (r->size <= 8) {
393 /* any 8-bit or shorter register is packed */
394 uint32_t offset;
395 unsigned int reg32_id;
396
397 bool is_packed = armv7m_map_reg_packing(num, &reg32_id, &offset);
398 if (!is_packed) {
399 /* We should not get here as all 8-bit or shorter registers
400 * are packed */
401 assert(false);
402 /* assert() does nothing if NDEBUG is defined */
403 return ERROR_FAIL;
404 }
405 struct reg *r32 = &armv7m->arm.core_cache->reg_list[reg32_id];
406
407 if (!r32->valid) {
408 /* Before merging with other parts ensure the 32-bit register is valid */
409 retval = armv7m_read_core_reg(target, r32, reg32_id, mode);
410 if (retval != ERROR_OK)
411 return retval;
412 }
413
414 /* Write a part to the 32-bit container register */
415 buf_cpy(value, r32->value + offset, r->size);
416 r32->dirty = true;
417
418 } else {
419 assert(r->size == 32 || r->size == 64);
420
421 struct arm_reg *armv7m_core_reg = r->arch_info;
422 uint32_t regsel = armv7m_map_id_to_regsel(armv7m_core_reg->num);
423
424 t = buf_get_u32(value, 0, 32);
425 retval = armv7m->store_core_reg_u32(target, regsel, t);
426 if (retval != ERROR_OK)
427 goto out_error;
428
429 if (r->size == 64) {
430 t = buf_get_u32(value + 4, 0, 32);
431 retval = armv7m->store_core_reg_u32(target, regsel + 1, t);
432 if (retval != ERROR_OK)
433 goto out_error;
434
435 uint64_t q = buf_get_u64(value, 0, 64);
436 LOG_DEBUG("write %s value 0x%016" PRIx64, r->name, q);
437 } else {
438 LOG_DEBUG("write %s value 0x%08" PRIx32, r->name, t);
439 }
440 }
441
442 r->valid = true;
443 r->dirty = false;
444
445 return ERROR_OK;
446
447 out_error:
448 r->dirty = true;
449 LOG_ERROR("Error setting register %s", r->name);
450 return retval;
451 }
452
453 /**
454 * Returns generic ARM userspace registers to GDB.
455 */
456 int armv7m_get_gdb_reg_list(struct target *target, struct reg **reg_list[],
457 int *reg_list_size, enum target_register_class reg_class)
458 {
459 struct armv7m_common *armv7m = target_to_armv7m(target);
460 int i, size;
461
462 if (reg_class == REG_CLASS_ALL)
463 size = armv7m->arm.core_cache->num_regs;
464 else
465 size = ARMV7M_NUM_CORE_REGS;
466
467 *reg_list = malloc(sizeof(struct reg *) * size);
468 if (!*reg_list)
469 return ERROR_FAIL;
470
471 for (i = 0; i < size; i++)
472 (*reg_list)[i] = &armv7m->arm.core_cache->reg_list[i];
473
474 *reg_list_size = size;
475
476 return ERROR_OK;
477 }
478
479 /** Runs a Thumb algorithm in the target. */
480 int armv7m_run_algorithm(struct target *target,
481 int num_mem_params, struct mem_param *mem_params,
482 int num_reg_params, struct reg_param *reg_params,
483 target_addr_t entry_point, target_addr_t exit_point,
484 int timeout_ms, void *arch_info)
485 {
486 int retval;
487
488 retval = armv7m_start_algorithm(target,
489 num_mem_params, mem_params,
490 num_reg_params, reg_params,
491 entry_point, exit_point,
492 arch_info);
493
494 if (retval == ERROR_OK)
495 retval = armv7m_wait_algorithm(target,
496 num_mem_params, mem_params,
497 num_reg_params, reg_params,
498 exit_point, timeout_ms,
499 arch_info);
500
501 return retval;
502 }
503
504 /** Starts a Thumb algorithm in the target. */
505 int armv7m_start_algorithm(struct target *target,
506 int num_mem_params, struct mem_param *mem_params,
507 int num_reg_params, struct reg_param *reg_params,
508 target_addr_t entry_point, target_addr_t exit_point,
509 void *arch_info)
510 {
511 struct armv7m_common *armv7m = target_to_armv7m(target);
512 struct armv7m_algorithm *armv7m_algorithm_info = arch_info;
513 enum arm_mode core_mode = armv7m->arm.core_mode;
514 int retval = ERROR_OK;
515
516 /* NOTE: armv7m_run_algorithm requires that each algorithm uses a software breakpoint
517 * at the exit point */
518
519 if (armv7m_algorithm_info->common_magic != ARMV7M_COMMON_MAGIC) {
520 LOG_ERROR("current target isn't an ARMV7M target");
521 return ERROR_TARGET_INVALID;
522 }
523
524 if (target->state != TARGET_HALTED) {
525 LOG_WARNING("target not halted");
526 return ERROR_TARGET_NOT_HALTED;
527 }
528
529 /* Store all non-debug execution registers to armv7m_algorithm_info context */
530 for (unsigned i = 0; i < armv7m->arm.core_cache->num_regs; i++) {
531 struct reg *reg = &armv7m->arm.core_cache->reg_list[i];
532 if (!reg->valid)
533 armv7m_get_core_reg(reg);
534
535 if (!reg->valid)
536 LOG_TARGET_WARNING(target, "Storing invalid register %s", reg->name);
537
538 armv7m_algorithm_info->context[i] = buf_get_u32(reg->value, 0, 32);
539 }
540
541 for (int i = 0; i < num_mem_params; i++) {
542 if (mem_params[i].direction == PARAM_IN)
543 continue;
544 retval = target_write_buffer(target, mem_params[i].address,
545 mem_params[i].size,
546 mem_params[i].value);
547 if (retval != ERROR_OK)
548 return retval;
549 }
550
551 for (int i = 0; i < num_reg_params; i++) {
552 if (reg_params[i].direction == PARAM_IN)
553 continue;
554
555 struct reg *reg =
556 register_get_by_name(armv7m->arm.core_cache, reg_params[i].reg_name, false);
557 /* uint32_t regvalue; */
558
559 if (!reg) {
560 LOG_ERROR("BUG: register '%s' not found", reg_params[i].reg_name);
561 return ERROR_COMMAND_SYNTAX_ERROR;
562 }
563
564 if (reg->size != reg_params[i].size) {
565 LOG_ERROR("BUG: register '%s' size doesn't match reg_params[i].size",
566 reg_params[i].reg_name);
567 return ERROR_COMMAND_SYNTAX_ERROR;
568 }
569
570 /* regvalue = buf_get_u32(reg_params[i].value, 0, 32); */
571 armv7m_set_core_reg(reg, reg_params[i].value);
572 }
573
574 {
575 /*
576 * Ensure xPSR.T is set to avoid trying to run things in arm
577 * (non-thumb) mode, which armv7m does not support.
578 *
579 * We do this by setting the entirety of xPSR, which should
580 * remove all the unknowns about xPSR state.
581 *
582 * Because xPSR.T is populated on reset from the vector table,
583 * it might be 0 if the vector table has "bad" data in it.
584 */
585 struct reg *reg = &armv7m->arm.core_cache->reg_list[ARMV7M_XPSR];
586 buf_set_u32(reg->value, 0, 32, 0x01000000);
587 reg->valid = true;
588 reg->dirty = true;
589 }
590
591 if (armv7m_algorithm_info->core_mode != ARM_MODE_ANY &&
592 armv7m_algorithm_info->core_mode != core_mode) {
593
594 /* we cannot set ARM_MODE_HANDLER, so use ARM_MODE_THREAD instead */
595 if (armv7m_algorithm_info->core_mode == ARM_MODE_HANDLER) {
596 armv7m_algorithm_info->core_mode = ARM_MODE_THREAD;
597 LOG_INFO("ARM_MODE_HANDLER not currently supported, using ARM_MODE_THREAD instead");
598 }
599
600 LOG_DEBUG("setting core_mode: 0x%2.2x", armv7m_algorithm_info->core_mode);
601 buf_set_u32(armv7m->arm.core_cache->reg_list[ARMV7M_CONTROL].value,
602 0, 1, armv7m_algorithm_info->core_mode);
603 armv7m->arm.core_cache->reg_list[ARMV7M_CONTROL].dirty = true;
604 armv7m->arm.core_cache->reg_list[ARMV7M_CONTROL].valid = true;
605 }
606
607 /* save previous core mode */
608 armv7m_algorithm_info->core_mode = core_mode;
609
610 retval = target_resume(target, 0, entry_point, 1, 1);
611
612 return retval;
613 }
614
615 /** Waits for an algorithm in the target. */
616 int armv7m_wait_algorithm(struct target *target,
617 int num_mem_params, struct mem_param *mem_params,
618 int num_reg_params, struct reg_param *reg_params,
619 target_addr_t exit_point, int timeout_ms,
620 void *arch_info)
621 {
622 struct armv7m_common *armv7m = target_to_armv7m(target);
623 struct armv7m_algorithm *armv7m_algorithm_info = arch_info;
624 int retval = ERROR_OK;
625
626 /* NOTE: armv7m_run_algorithm requires that each algorithm uses a software breakpoint
627 * at the exit point */
628
629 if (armv7m_algorithm_info->common_magic != ARMV7M_COMMON_MAGIC) {
630 LOG_ERROR("current target isn't an ARMV7M target");
631 return ERROR_TARGET_INVALID;
632 }
633
634 retval = target_wait_state(target, TARGET_HALTED, timeout_ms);
635 /* If the target fails to halt due to the breakpoint, force a halt */
636 if (retval != ERROR_OK || target->state != TARGET_HALTED) {
637 retval = target_halt(target);
638 if (retval != ERROR_OK)
639 return retval;
640 retval = target_wait_state(target, TARGET_HALTED, 500);
641 if (retval != ERROR_OK)
642 return retval;
643 return ERROR_TARGET_TIMEOUT;
644 }
645
646 if (exit_point) {
647 /* PC value has been cached in cortex_m_debug_entry() */
648 uint32_t pc = buf_get_u32(armv7m->arm.pc->value, 0, 32);
649 if (pc != exit_point) {
650 LOG_DEBUG("failed algorithm halted at 0x%" PRIx32 ", expected 0x%" TARGET_PRIxADDR,
651 pc, exit_point);
652 return ERROR_TARGET_ALGO_EXIT;
653 }
654 }
655
656 /* Read memory values to mem_params[] */
657 for (int i = 0; i < num_mem_params; i++) {
658 if (mem_params[i].direction != PARAM_OUT) {
659 retval = target_read_buffer(target, mem_params[i].address,
660 mem_params[i].size,
661 mem_params[i].value);
662 if (retval != ERROR_OK)
663 return retval;
664 }
665 }
666
667 /* Copy core register values to reg_params[] */
668 for (int i = 0; i < num_reg_params; i++) {
669 if (reg_params[i].direction != PARAM_OUT) {
670 struct reg *reg = register_get_by_name(armv7m->arm.core_cache,
671 reg_params[i].reg_name,
672 false);
673
674 if (!reg) {
675 LOG_ERROR("BUG: register '%s' not found", reg_params[i].reg_name);
676 return ERROR_COMMAND_SYNTAX_ERROR;
677 }
678
679 if (reg->size != reg_params[i].size) {
680 LOG_ERROR(
681 "BUG: register '%s' size doesn't match reg_params[i].size",
682 reg_params[i].reg_name);
683 return ERROR_COMMAND_SYNTAX_ERROR;
684 }
685
686 buf_set_u32(reg_params[i].value, 0, 32, buf_get_u32(reg->value, 0, 32));
687 }
688 }
689
690 for (int i = armv7m->arm.core_cache->num_regs - 1; i >= 0; i--) {
691 uint32_t regvalue;
692 regvalue = buf_get_u32(armv7m->arm.core_cache->reg_list[i].value, 0, 32);
693 if (regvalue != armv7m_algorithm_info->context[i]) {
694 LOG_DEBUG("restoring register %s with value 0x%8.8" PRIx32,
695 armv7m->arm.core_cache->reg_list[i].name,
696 armv7m_algorithm_info->context[i]);
697 buf_set_u32(armv7m->arm.core_cache->reg_list[i].value,
698 0, 32, armv7m_algorithm_info->context[i]);
699 armv7m->arm.core_cache->reg_list[i].valid = true;
700 armv7m->arm.core_cache->reg_list[i].dirty = true;
701 }
702 }
703
704 /* restore previous core mode */
705 if (armv7m_algorithm_info->core_mode != armv7m->arm.core_mode) {
706 LOG_DEBUG("restoring core_mode: 0x%2.2x", armv7m_algorithm_info->core_mode);
707 buf_set_u32(armv7m->arm.core_cache->reg_list[ARMV7M_CONTROL].value,
708 0, 1, armv7m_algorithm_info->core_mode);
709 armv7m->arm.core_cache->reg_list[ARMV7M_CONTROL].dirty = true;
710 armv7m->arm.core_cache->reg_list[ARMV7M_CONTROL].valid = true;
711 }
712
713 armv7m->arm.core_mode = armv7m_algorithm_info->core_mode;
714
715 return retval;
716 }
717
718 /** Logs summary of ARMv7-M state for a halted target. */
719 int armv7m_arch_state(struct target *target)
720 {
721 struct armv7m_common *armv7m = target_to_armv7m(target);
722 struct arm *arm = &armv7m->arm;
723 uint32_t ctrl, sp;
724
725 /* avoid filling log waiting for fileio reply */
726 if (target->semihosting && target->semihosting->hit_fileio)
727 return ERROR_OK;
728
729 ctrl = buf_get_u32(arm->core_cache->reg_list[ARMV7M_CONTROL].value, 0, 32);
730 sp = buf_get_u32(arm->core_cache->reg_list[ARMV7M_R13].value, 0, 32);
731
732 LOG_USER("[%s] halted due to %s, current mode: %s %s\n"
733 "xPSR: %#8.8" PRIx32 " pc: %#8.8" PRIx32 " %csp: %#8.8" PRIx32 "%s%s",
734 target_name(target),
735 debug_reason_name(target),
736 arm_mode_name(arm->core_mode),
737 armv7m_exception_string(armv7m->exception_number),
738 buf_get_u32(arm->cpsr->value, 0, 32),
739 buf_get_u32(arm->pc->value, 0, 32),
740 (ctrl & 0x02) ? 'p' : 'm',
741 sp,
742 (target->semihosting && target->semihosting->is_active) ? ", semihosting" : "",
743 (target->semihosting && target->semihosting->is_fileio) ? " fileio" : "");
744
745 return ERROR_OK;
746 }
747
748 static const struct reg_arch_type armv7m_reg_type = {
749 .get = armv7m_get_core_reg,
750 .set = armv7m_set_core_reg,
751 };
752
753 /** Builds cache of architecturally defined registers. */
754 struct reg_cache *armv7m_build_reg_cache(struct target *target)
755 {
756 struct armv7m_common *armv7m = target_to_armv7m(target);
757 struct arm *arm = &armv7m->arm;
758 int num_regs = ARMV7M_NUM_REGS;
759 struct reg_cache **cache_p = register_get_last_cache_p(&target->reg_cache);
760 struct reg_cache *cache = malloc(sizeof(struct reg_cache));
761 struct reg *reg_list = calloc(num_regs, sizeof(struct reg));
762 struct arm_reg *arch_info = calloc(num_regs, sizeof(struct arm_reg));
763 struct reg_feature *feature;
764 int i;
765
766 /* Build the process context cache */
767 cache->name = "arm v7m registers";
768 cache->next = NULL;
769 cache->reg_list = reg_list;
770 cache->num_regs = num_regs;
771 (*cache_p) = cache;
772
773 for (i = 0; i < num_regs; i++) {
774 arch_info[i].num = armv7m_regs[i].id;
775 arch_info[i].target = target;
776 arch_info[i].arm = arm;
777
778 reg_list[i].name = armv7m_regs[i].name;
779 reg_list[i].size = armv7m_regs[i].bits;
780 reg_list[i].value = arch_info[i].value;
781 reg_list[i].dirty = false;
782 reg_list[i].valid = false;
783 reg_list[i].hidden = (i == ARMV7M_PMSK_BPRI_FLTMSK_CTRL ||
784 i == ARMV8M_PMSK_BPRI_FLTMSK_CTRL_NS || i == ARMV8M_PMSK_BPRI_FLTMSK_CTRL_S);
785 reg_list[i].type = &armv7m_reg_type;
786 reg_list[i].arch_info = &arch_info[i];
787
788 reg_list[i].group = armv7m_regs[i].group;
789 reg_list[i].number = i;
790 reg_list[i].exist = true;
791 reg_list[i].caller_save = true; /* gdb defaults to true */
792
793 if (reg_list[i].hidden)
794 continue;
795
796 feature = calloc(1, sizeof(struct reg_feature));
797 if (feature) {
798 feature->name = armv7m_regs[i].feature;
799 reg_list[i].feature = feature;
800 } else
801 LOG_ERROR("unable to allocate feature list");
802
803 reg_list[i].reg_data_type = calloc(1, sizeof(struct reg_data_type));
804 if (reg_list[i].reg_data_type)
805 reg_list[i].reg_data_type->type = armv7m_regs[i].type;
806 else
807 LOG_ERROR("unable to allocate reg type list");
808 }
809
810 arm->cpsr = reg_list + ARMV7M_XPSR;
811 arm->pc = reg_list + ARMV7M_PC;
812 arm->core_cache = cache;
813
814 return cache;
815 }
816
817 void armv7m_free_reg_cache(struct target *target)
818 {
819 struct armv7m_common *armv7m = target_to_armv7m(target);
820 struct arm *arm = &armv7m->arm;
821 struct reg_cache *cache;
822 struct reg *reg;
823 unsigned int i;
824
825 cache = arm->core_cache;
826
827 if (!cache)
828 return;
829
830 for (i = 0; i < cache->num_regs; i++) {
831 reg = &cache->reg_list[i];
832
833 free(reg->feature);
834 free(reg->reg_data_type);
835 }
836
837 free(cache->reg_list[0].arch_info);
838 free(cache->reg_list);
839 free(cache);
840
841 arm->core_cache = NULL;
842 }
843
844 static int armv7m_setup_semihosting(struct target *target, int enable)
845 {
846 /* nothing todo for armv7m */
847 return ERROR_OK;
848 }
849
850 /** Sets up target as a generic ARMv7-M core */
851 int armv7m_init_arch_info(struct target *target, struct armv7m_common *armv7m)
852 {
853 struct arm *arm = &armv7m->arm;
854
855 armv7m->common_magic = ARMV7M_COMMON_MAGIC;
856 armv7m->fp_feature = FP_NONE;
857 armv7m->trace_config.trace_bus_id = 1;
858 /* Enable stimulus port #0 by default */
859 armv7m->trace_config.itm_ter[0] = 1;
860
861 arm->core_state = ARM_STATE_THUMB;
862 arm->core_type = ARM_CORE_TYPE_M_PROFILE;
863 arm->arch_info = armv7m;
864 arm->setup_semihosting = armv7m_setup_semihosting;
865
866 arm->read_core_reg = armv7m_read_core_reg;
867 arm->write_core_reg = armv7m_write_core_reg;
868
869 return arm_init_arch_info(target, arm);
870 }
871
872 /** Generates a CRC32 checksum of a memory region. */
873 int armv7m_checksum_memory(struct target *target,
874 target_addr_t address, uint32_t count, uint32_t *checksum)
875 {
876 struct working_area *crc_algorithm;
877 struct armv7m_algorithm armv7m_info;
878 struct reg_param reg_params[2];
879 int retval;
880
881 static const uint8_t cortex_m_crc_code[] = {
882 #include "../../contrib/loaders/checksum/armv7m_crc.inc"
883 };
884
885 retval = target_alloc_working_area(target, sizeof(cortex_m_crc_code), &crc_algorithm);
886 if (retval != ERROR_OK)
887 return retval;
888
889 retval = target_write_buffer(target, crc_algorithm->address,
890 sizeof(cortex_m_crc_code), (uint8_t *)cortex_m_crc_code);
891 if (retval != ERROR_OK)
892 goto cleanup;
893
894 armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
895 armv7m_info.core_mode = ARM_MODE_THREAD;
896
897 init_reg_param(&reg_params[0], "r0", 32, PARAM_IN_OUT);
898 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
899
900 buf_set_u32(reg_params[0].value, 0, 32, address);
901 buf_set_u32(reg_params[1].value, 0, 32, count);
902
903 int timeout = 20000 * (1 + (count / (1024 * 1024)));
904
905 retval = target_run_algorithm(target, 0, NULL, 2, reg_params, crc_algorithm->address,
906 crc_algorithm->address + (sizeof(cortex_m_crc_code) - 6),
907 timeout, &armv7m_info);
908
909 if (retval == ERROR_OK)
910 *checksum = buf_get_u32(reg_params[0].value, 0, 32);
911 else
912 LOG_ERROR("error executing cortex_m crc algorithm");
913
914 destroy_reg_param(&reg_params[0]);
915 destroy_reg_param(&reg_params[1]);
916
917 cleanup:
918 target_free_working_area(target, crc_algorithm);
919
920 return retval;
921 }
922
923 /** Checks an array of memory regions whether they are erased. */
924 int armv7m_blank_check_memory(struct target *target,
925 struct target_memory_check_block *blocks, int num_blocks, uint8_t erased_value)
926 {
927 struct working_area *erase_check_algorithm;
928 struct working_area *erase_check_params;
929 struct reg_param reg_params[2];
930 struct armv7m_algorithm armv7m_info;
931 int retval;
932
933 static bool timed_out;
934
935 static const uint8_t erase_check_code[] = {
936 #include "../../contrib/loaders/erase_check/armv7m_erase_check.inc"
937 };
938
939 const uint32_t code_size = sizeof(erase_check_code);
940
941 /* make sure we have a working area */
942 if (target_alloc_working_area(target, code_size,
943 &erase_check_algorithm) != ERROR_OK)
944 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
945
946 retval = target_write_buffer(target, erase_check_algorithm->address,
947 code_size, erase_check_code);
948 if (retval != ERROR_OK)
949 goto cleanup1;
950
951 /* prepare blocks array for algo */
952 struct algo_block {
953 union {
954 uint32_t size;
955 uint32_t result;
956 };
957 uint32_t address;
958 };
959
960 uint32_t avail = target_get_working_area_avail(target);
961 int blocks_to_check = avail / sizeof(struct algo_block) - 1;
962 if (num_blocks < blocks_to_check)
963 blocks_to_check = num_blocks;
964
965 struct algo_block *params = malloc((blocks_to_check+1)*sizeof(struct algo_block));
966 if (!params) {
967 retval = ERROR_FAIL;
968 goto cleanup1;
969 }
970
971 int i;
972 uint32_t total_size = 0;
973 for (i = 0; i < blocks_to_check; i++) {
974 total_size += blocks[i].size;
975 target_buffer_set_u32(target, (uint8_t *)&(params[i].size),
976 blocks[i].size / sizeof(uint32_t));
977 target_buffer_set_u32(target, (uint8_t *)&(params[i].address),
978 blocks[i].address);
979 }
980 target_buffer_set_u32(target, (uint8_t *)&(params[blocks_to_check].size), 0);
981
982 uint32_t param_size = (blocks_to_check + 1) * sizeof(struct algo_block);
983 if (target_alloc_working_area(target, param_size,
984 &erase_check_params) != ERROR_OK) {
985 retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
986 goto cleanup2;
987 }
988
989 retval = target_write_buffer(target, erase_check_params->address,
990 param_size, (uint8_t *)params);
991 if (retval != ERROR_OK)
992 goto cleanup3;
993
994 uint32_t erased_word = erased_value | (erased_value << 8)
995 | (erased_value << 16) | (erased_value << 24);
996
997 LOG_DEBUG("Starting erase check of %d blocks, parameters@"
998 TARGET_ADDR_FMT, blocks_to_check, erase_check_params->address);
999
1000 armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
1001 armv7m_info.core_mode = ARM_MODE_THREAD;
1002
1003 init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
1004 buf_set_u32(reg_params[0].value, 0, 32, erase_check_params->address);
1005
1006 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
1007 buf_set_u32(reg_params[1].value, 0, 32, erased_word);
1008
1009 /* assume CPU clk at least 1 MHz */
1010 int timeout = (timed_out ? 30000 : 2000) + total_size * 3 / 1000;
1011
1012 retval = target_run_algorithm(target,
1013 0, NULL,
1014 ARRAY_SIZE(reg_params), reg_params,
1015 erase_check_algorithm->address,
1016 erase_check_algorithm->address + (code_size - 2),
1017 timeout,
1018 &armv7m_info);
1019
1020 timed_out = retval == ERROR_TARGET_TIMEOUT;
1021 if (retval != ERROR_OK && !timed_out)
1022 goto cleanup4;
1023
1024 retval = target_read_buffer(target, erase_check_params->address,
1025 param_size, (uint8_t *)params);
1026 if (retval != ERROR_OK)
1027 goto cleanup4;
1028
1029 for (i = 0; i < blocks_to_check; i++) {
1030 uint32_t result = target_buffer_get_u32(target,
1031 (uint8_t *)&(params[i].result));
1032 if (result != 0 && result != 1)
1033 break;
1034
1035 blocks[i].result = result;
1036 }
1037 if (i && timed_out)
1038 LOG_INFO("Slow CPU clock: %d blocks checked, %d remain. Continuing...", i, num_blocks-i);
1039
1040 retval = i; /* return number of blocks really checked */
1041
1042 cleanup4:
1043 destroy_reg_param(&reg_params[0]);
1044 destroy_reg_param(&reg_params[1]);
1045
1046 cleanup3:
1047 target_free_working_area(target, erase_check_params);
1048 cleanup2:
1049 free(params);
1050 cleanup1:
1051 target_free_working_area(target, erase_check_algorithm);
1052
1053 return retval;
1054 }
1055
1056 int armv7m_maybe_skip_bkpt_inst(struct target *target, bool *inst_found)
1057 {
1058 struct armv7m_common *armv7m = target_to_armv7m(target);
1059 struct reg *r = armv7m->arm.pc;
1060 bool result = false;
1061
1062
1063 /* if we halted last time due to a bkpt instruction
1064 * then we have to manually step over it, otherwise
1065 * the core will break again */
1066
1067 if (target->debug_reason == DBG_REASON_BREAKPOINT) {
1068 uint16_t op;
1069 uint32_t pc = buf_get_u32(r->value, 0, 32);
1070
1071 pc &= ~1;
1072 if (target_read_u16(target, pc, &op) == ERROR_OK) {
1073 if ((op & 0xFF00) == 0xBE00) {
1074 pc = buf_get_u32(r->value, 0, 32) + 2;
1075 buf_set_u32(r->value, 0, 32, pc);
1076 r->dirty = true;
1077 r->valid = true;
1078 result = true;
1079 LOG_DEBUG("Skipping over BKPT instruction");
1080 }
1081 }
1082 }
1083
1084 if (inst_found)
1085 *inst_found = result;
1086
1087 return ERROR_OK;
1088 }
1089
1090 const struct command_registration armv7m_command_handlers[] = {
1091 {
1092 .name = "arm",
1093 .mode = COMMAND_ANY,
1094 .help = "ARM command group",
1095 .usage = "",
1096 .chain = arm_all_profiles_command_handlers,
1097 },
1098 COMMAND_REGISTRATION_DONE
1099 };

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)