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

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)