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

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)