68da020a13f8d45d26813bb679b909c40da23d90
[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;
333 unsigned int reg32_id;
334
335 bool is_packed = armv7m_map_reg_packing(num, &reg32_id, &offset);
336 if (!is_packed) {
337 /* We should not get here as all 8-bit or shorter registers
338 * are packed */
339 assert(false);
340 /* assert() does nothing if NDEBUG is defined */
341 return ERROR_FAIL;
342 }
343 struct reg *r32 = &armv7m->arm.core_cache->reg_list[reg32_id];
344
345 /* Read 32-bit container register if not cached */
346 if (!r32->valid) {
347 retval = armv7m_read_core_reg(target, r32, reg32_id, mode);
348 if (retval != ERROR_OK)
349 return retval;
350 }
351
352 /* Copy required bits of 32-bit container register */
353 buf_cpy(r32->value + offset, r->value, r->size);
354
355 } else {
356 assert(r->size == 32 || r->size == 64);
357
358 struct arm_reg *armv7m_core_reg = r->arch_info;
359 uint32_t regsel = armv7m_map_id_to_regsel(armv7m_core_reg->num);
360
361 retval = armv7m->load_core_reg_u32(target, regsel, &reg_value);
362 if (retval != ERROR_OK)
363 return retval;
364 buf_set_u32(r->value, 0, 32, reg_value);
365
366 if (r->size == 64) {
367 retval = armv7m->load_core_reg_u32(target, regsel + 1, &reg_value);
368 if (retval != ERROR_OK) {
369 r->valid = false;
370 return retval;
371 }
372 buf_set_u32(r->value + 4, 0, 32, reg_value);
373
374 uint64_t q = buf_get_u64(r->value, 0, 64);
375 LOG_DEBUG("read %s value 0x%016" PRIx64, r->name, q);
376 } else {
377 LOG_DEBUG("read %s value 0x%08" PRIx32, r->name, reg_value);
378 }
379 }
380
381 r->valid = true;
382
383 return ERROR_OK;
384 }
385
386 static int armv7m_write_core_reg(struct target *target, struct reg *r,
387 int num, enum arm_mode mode, uint8_t *value)
388 {
389 int retval;
390 uint32_t t;
391 struct armv7m_common *armv7m = target_to_armv7m(target);
392
393 assert(num < (int)armv7m->arm.core_cache->num_regs);
394 assert(num == (int)r->number);
395
396 if (value != r->value) {
397 /* If we are not flushing the cache, store the new value to the cache */
398 buf_cpy(value, r->value, r->size);
399 }
400
401 if (r->size <= 8) {
402 /* any 8-bit or shorter register is packed */
403 uint32_t offset;
404 unsigned int reg32_id;
405
406 bool is_packed = armv7m_map_reg_packing(num, &reg32_id, &offset);
407 if (!is_packed) {
408 /* We should not get here as all 8-bit or shorter registers
409 * are packed */
410 assert(false);
411 /* assert() does nothing if NDEBUG is defined */
412 return ERROR_FAIL;
413 }
414 struct reg *r32 = &armv7m->arm.core_cache->reg_list[reg32_id];
415
416 if (!r32->valid) {
417 /* Before merging with other parts ensure the 32-bit register is valid */
418 retval = armv7m_read_core_reg(target, r32, reg32_id, mode);
419 if (retval != ERROR_OK)
420 return retval;
421 }
422
423 /* Write a part to the 32-bit container register */
424 buf_cpy(value, r32->value + offset, r->size);
425 r32->dirty = true;
426
427 } else {
428 assert(r->size == 32 || r->size == 64);
429
430 struct arm_reg *armv7m_core_reg = r->arch_info;
431 uint32_t regsel = armv7m_map_id_to_regsel(armv7m_core_reg->num);
432
433 t = buf_get_u32(value, 0, 32);
434 retval = armv7m->store_core_reg_u32(target, regsel, t);
435 if (retval != ERROR_OK)
436 goto out_error;
437
438 if (r->size == 64) {
439 t = buf_get_u32(value + 4, 0, 32);
440 retval = armv7m->store_core_reg_u32(target, regsel + 1, t);
441 if (retval != ERROR_OK)
442 goto out_error;
443
444 uint64_t q = buf_get_u64(value, 0, 64);
445 LOG_DEBUG("write %s value 0x%016" PRIx64, r->name, q);
446 } else {
447 LOG_DEBUG("write %s value 0x%08" PRIx32, r->name, t);
448 }
449 }
450
451 r->valid = true;
452 r->dirty = false;
453
454 return ERROR_OK;
455
456 out_error:
457 r->dirty = true;
458 LOG_ERROR("Error setting register %s", r->name);
459 return retval;
460 }
461
462 /**
463 * Returns generic ARM userspace registers to GDB.
464 */
465 int armv7m_get_gdb_reg_list(struct target *target, struct reg **reg_list[],
466 int *reg_list_size, enum target_register_class reg_class)
467 {
468 struct armv7m_common *armv7m = target_to_armv7m(target);
469 int i, size;
470
471 if (reg_class == REG_CLASS_ALL)
472 size = armv7m->arm.core_cache->num_regs;
473 else
474 size = ARMV7M_NUM_CORE_REGS;
475
476 *reg_list = malloc(sizeof(struct reg *) * size);
477 if (!*reg_list)
478 return ERROR_FAIL;
479
480 for (i = 0; i < size; i++)
481 (*reg_list)[i] = &armv7m->arm.core_cache->reg_list[i];
482
483 *reg_list_size = size;
484
485 return ERROR_OK;
486 }
487
488 /** Runs a Thumb algorithm in the target. */
489 int armv7m_run_algorithm(struct target *target,
490 int num_mem_params, struct mem_param *mem_params,
491 int num_reg_params, struct reg_param *reg_params,
492 target_addr_t entry_point, target_addr_t exit_point,
493 int timeout_ms, void *arch_info)
494 {
495 int retval;
496
497 retval = armv7m_start_algorithm(target,
498 num_mem_params, mem_params,
499 num_reg_params, reg_params,
500 entry_point, exit_point,
501 arch_info);
502
503 if (retval == ERROR_OK)
504 retval = armv7m_wait_algorithm(target,
505 num_mem_params, mem_params,
506 num_reg_params, reg_params,
507 exit_point, timeout_ms,
508 arch_info);
509
510 return retval;
511 }
512
513 /** Starts a Thumb algorithm in the target. */
514 int armv7m_start_algorithm(struct target *target,
515 int num_mem_params, struct mem_param *mem_params,
516 int num_reg_params, struct reg_param *reg_params,
517 target_addr_t entry_point, target_addr_t exit_point,
518 void *arch_info)
519 {
520 struct armv7m_common *armv7m = target_to_armv7m(target);
521 struct armv7m_algorithm *armv7m_algorithm_info = arch_info;
522 enum arm_mode core_mode = armv7m->arm.core_mode;
523 int retval = ERROR_OK;
524
525 /* NOTE: armv7m_run_algorithm requires that each algorithm uses a software breakpoint
526 * at the exit point */
527
528 if (armv7m_algorithm_info->common_magic != ARMV7M_COMMON_MAGIC) {
529 LOG_ERROR("current target isn't an ARMV7M target");
530 return ERROR_TARGET_INVALID;
531 }
532
533 if (target->state != TARGET_HALTED) {
534 LOG_WARNING("target not halted");
535 return ERROR_TARGET_NOT_HALTED;
536 }
537
538 /* Store all non-debug execution registers to armv7m_algorithm_info context */
539 for (unsigned i = 0; i < armv7m->arm.core_cache->num_regs; i++) {
540
541 armv7m_algorithm_info->context[i] = buf_get_u32(
542 armv7m->arm.core_cache->reg_list[i].value,
543 0,
544 32);
545 }
546
547 for (int i = 0; i < num_mem_params; i++) {
548 if (mem_params[i].direction == PARAM_IN)
549 continue;
550 retval = target_write_buffer(target, mem_params[i].address,
551 mem_params[i].size,
552 mem_params[i].value);
553 if (retval != ERROR_OK)
554 return retval;
555 }
556
557 for (int i = 0; i < num_reg_params; i++) {
558 if (reg_params[i].direction == PARAM_IN)
559 continue;
560
561 struct reg *reg =
562 register_get_by_name(armv7m->arm.core_cache, reg_params[i].reg_name, false);
563 /* uint32_t regvalue; */
564
565 if (!reg) {
566 LOG_ERROR("BUG: register '%s' not found", reg_params[i].reg_name);
567 return ERROR_COMMAND_SYNTAX_ERROR;
568 }
569
570 if (reg->size != reg_params[i].size) {
571 LOG_ERROR("BUG: register '%s' size doesn't match reg_params[i].size",
572 reg_params[i].reg_name);
573 return ERROR_COMMAND_SYNTAX_ERROR;
574 }
575
576 /* regvalue = buf_get_u32(reg_params[i].value, 0, 32); */
577 armv7m_set_core_reg(reg, reg_params[i].value);
578 }
579
580 {
581 /*
582 * Ensure xPSR.T is set to avoid trying to run things in arm
583 * (non-thumb) mode, which armv7m does not support.
584 *
585 * We do this by setting the entirety of xPSR, which should
586 * remove all the unknowns about xPSR state.
587 *
588 * Because xPSR.T is populated on reset from the vector table,
589 * it might be 0 if the vector table has "bad" data in it.
590 */
591 struct reg *reg = &armv7m->arm.core_cache->reg_list[ARMV7M_xPSR];
592 buf_set_u32(reg->value, 0, 32, 0x01000000);
593 reg->valid = true;
594 reg->dirty = true;
595 }
596
597 if (armv7m_algorithm_info->core_mode != ARM_MODE_ANY &&
598 armv7m_algorithm_info->core_mode != core_mode) {
599
600 /* we cannot set ARM_MODE_HANDLER, so use ARM_MODE_THREAD instead */
601 if (armv7m_algorithm_info->core_mode == ARM_MODE_HANDLER) {
602 armv7m_algorithm_info->core_mode = ARM_MODE_THREAD;
603 LOG_INFO("ARM_MODE_HANDLER not currently supported, using ARM_MODE_THREAD instead");
604 }
605
606 LOG_DEBUG("setting core_mode: 0x%2.2x", armv7m_algorithm_info->core_mode);
607 buf_set_u32(armv7m->arm.core_cache->reg_list[ARMV7M_CONTROL].value,
608 0, 1, armv7m_algorithm_info->core_mode);
609 armv7m->arm.core_cache->reg_list[ARMV7M_CONTROL].dirty = true;
610 armv7m->arm.core_cache->reg_list[ARMV7M_CONTROL].valid = true;
611 }
612
613 /* save previous core mode */
614 armv7m_algorithm_info->core_mode = core_mode;
615
616 retval = target_resume(target, 0, entry_point, 1, 1);
617
618 return retval;
619 }
620
621 /** Waits for an algorithm in the target. */
622 int armv7m_wait_algorithm(struct target *target,
623 int num_mem_params, struct mem_param *mem_params,
624 int num_reg_params, struct reg_param *reg_params,
625 target_addr_t exit_point, int timeout_ms,
626 void *arch_info)
627 {
628 struct armv7m_common *armv7m = target_to_armv7m(target);
629 struct armv7m_algorithm *armv7m_algorithm_info = arch_info;
630 int retval = ERROR_OK;
631
632 /* NOTE: armv7m_run_algorithm requires that each algorithm uses a software breakpoint
633 * at the exit point */
634
635 if (armv7m_algorithm_info->common_magic != ARMV7M_COMMON_MAGIC) {
636 LOG_ERROR("current target isn't an ARMV7M target");
637 return ERROR_TARGET_INVALID;
638 }
639
640 retval = target_wait_state(target, TARGET_HALTED, timeout_ms);
641 /* If the target fails to halt due to the breakpoint, force a halt */
642 if (retval != ERROR_OK || target->state != TARGET_HALTED) {
643 retval = target_halt(target);
644 if (retval != ERROR_OK)
645 return retval;
646 retval = target_wait_state(target, TARGET_HALTED, 500);
647 if (retval != ERROR_OK)
648 return retval;
649 return ERROR_TARGET_TIMEOUT;
650 }
651
652 if (exit_point) {
653 /* PC value has been cached in cortex_m_debug_entry() */
654 uint32_t pc = buf_get_u32(armv7m->arm.pc->value, 0, 32);
655 if (pc != exit_point) {
656 LOG_DEBUG("failed algorithm halted at 0x%" PRIx32 ", expected 0x%" TARGET_PRIxADDR,
657 pc, exit_point);
658 return ERROR_TARGET_ALGO_EXIT;
659 }
660 }
661
662 /* Read memory values to mem_params[] */
663 for (int i = 0; i < num_mem_params; i++) {
664 if (mem_params[i].direction != PARAM_OUT) {
665 retval = target_read_buffer(target, mem_params[i].address,
666 mem_params[i].size,
667 mem_params[i].value);
668 if (retval != ERROR_OK)
669 return retval;
670 }
671 }
672
673 /* Copy core register values to reg_params[] */
674 for (int i = 0; i < num_reg_params; i++) {
675 if (reg_params[i].direction != PARAM_OUT) {
676 struct reg *reg = register_get_by_name(armv7m->arm.core_cache,
677 reg_params[i].reg_name,
678 false);
679
680 if (!reg) {
681 LOG_ERROR("BUG: register '%s' not found", reg_params[i].reg_name);
682 return ERROR_COMMAND_SYNTAX_ERROR;
683 }
684
685 if (reg->size != reg_params[i].size) {
686 LOG_ERROR(
687 "BUG: register '%s' size doesn't match reg_params[i].size",
688 reg_params[i].reg_name);
689 return ERROR_COMMAND_SYNTAX_ERROR;
690 }
691
692 buf_set_u32(reg_params[i].value, 0, 32, buf_get_u32(reg->value, 0, 32));
693 }
694 }
695
696 for (int i = armv7m->arm.core_cache->num_regs - 1; i >= 0; i--) {
697 uint32_t regvalue;
698 regvalue = buf_get_u32(armv7m->arm.core_cache->reg_list[i].value, 0, 32);
699 if (regvalue != armv7m_algorithm_info->context[i]) {
700 LOG_DEBUG("restoring register %s with value 0x%8.8" PRIx32,
701 armv7m->arm.core_cache->reg_list[i].name,
702 armv7m_algorithm_info->context[i]);
703 buf_set_u32(armv7m->arm.core_cache->reg_list[i].value,
704 0, 32, armv7m_algorithm_info->context[i]);
705 armv7m->arm.core_cache->reg_list[i].valid = true;
706 armv7m->arm.core_cache->reg_list[i].dirty = true;
707 }
708 }
709
710 /* restore previous core mode */
711 if (armv7m_algorithm_info->core_mode != armv7m->arm.core_mode) {
712 LOG_DEBUG("restoring core_mode: 0x%2.2x", armv7m_algorithm_info->core_mode);
713 buf_set_u32(armv7m->arm.core_cache->reg_list[ARMV7M_CONTROL].value,
714 0, 1, armv7m_algorithm_info->core_mode);
715 armv7m->arm.core_cache->reg_list[ARMV7M_CONTROL].dirty = true;
716 armv7m->arm.core_cache->reg_list[ARMV7M_CONTROL].valid = true;
717 }
718
719 armv7m->arm.core_mode = armv7m_algorithm_info->core_mode;
720
721 return retval;
722 }
723
724 /** Logs summary of ARMv7-M state for a halted target. */
725 int armv7m_arch_state(struct target *target)
726 {
727 struct armv7m_common *armv7m = target_to_armv7m(target);
728 struct arm *arm = &armv7m->arm;
729 uint32_t ctrl, sp;
730
731 /* avoid filling log waiting for fileio reply */
732 if (target->semihosting && target->semihosting->hit_fileio)
733 return ERROR_OK;
734
735 ctrl = buf_get_u32(arm->core_cache->reg_list[ARMV7M_CONTROL].value, 0, 32);
736 sp = buf_get_u32(arm->core_cache->reg_list[ARMV7M_R13].value, 0, 32);
737
738 LOG_USER("target halted due to %s, current mode: %s %s\n"
739 "xPSR: %#8.8" PRIx32 " pc: %#8.8" PRIx32 " %csp: %#8.8" PRIx32 "%s%s",
740 debug_reason_name(target),
741 arm_mode_name(arm->core_mode),
742 armv7m_exception_string(armv7m->exception_number),
743 buf_get_u32(arm->cpsr->value, 0, 32),
744 buf_get_u32(arm->pc->value, 0, 32),
745 (ctrl & 0x02) ? 'p' : 'm',
746 sp,
747 (target->semihosting && target->semihosting->is_active) ? ", semihosting" : "",
748 (target->semihosting && target->semihosting->is_fileio) ? " fileio" : "");
749
750 return ERROR_OK;
751 }
752
753 static const struct reg_arch_type armv7m_reg_type = {
754 .get = armv7m_get_core_reg,
755 .set = armv7m_set_core_reg,
756 };
757
758 /** Builds cache of architecturally defined registers. */
759 struct reg_cache *armv7m_build_reg_cache(struct target *target)
760 {
761 struct armv7m_common *armv7m = target_to_armv7m(target);
762 struct arm *arm = &armv7m->arm;
763 int num_regs = ARMV7M_NUM_REGS;
764 struct reg_cache **cache_p = register_get_last_cache_p(&target->reg_cache);
765 struct reg_cache *cache = malloc(sizeof(struct reg_cache));
766 struct reg *reg_list = calloc(num_regs, sizeof(struct reg));
767 struct arm_reg *arch_info = calloc(num_regs, sizeof(struct arm_reg));
768 struct reg_feature *feature;
769 int i;
770
771 /* Build the process context cache */
772 cache->name = "arm v7m registers";
773 cache->next = NULL;
774 cache->reg_list = reg_list;
775 cache->num_regs = num_regs;
776 (*cache_p) = cache;
777
778 for (i = 0; i < num_regs; i++) {
779 arch_info[i].num = armv7m_regs[i].id;
780 arch_info[i].target = target;
781 arch_info[i].arm = arm;
782
783 reg_list[i].name = armv7m_regs[i].name;
784 reg_list[i].size = armv7m_regs[i].bits;
785 reg_list[i].value = arch_info[i].value;
786 reg_list[i].dirty = false;
787 reg_list[i].valid = false;
788 reg_list[i].hidden = (i == ARMV7M_PMSK_BPRI_FLTMSK_CTRL ||
789 i == ARMV8M_PMSK_BPRI_FLTMSK_CTRL_NS || i == ARMV8M_PMSK_BPRI_FLTMSK_CTRL_S);
790 reg_list[i].type = &armv7m_reg_type;
791 reg_list[i].arch_info = &arch_info[i];
792
793 reg_list[i].group = armv7m_regs[i].group;
794 reg_list[i].number = i;
795 reg_list[i].exist = true;
796 reg_list[i].caller_save = true; /* gdb defaults to true */
797
798 if (reg_list[i].hidden)
799 continue;
800
801 feature = calloc(1, sizeof(struct reg_feature));
802 if (feature) {
803 feature->name = armv7m_regs[i].feature;
804 reg_list[i].feature = feature;
805 } else
806 LOG_ERROR("unable to allocate feature list");
807
808 reg_list[i].reg_data_type = calloc(1, sizeof(struct reg_data_type));
809 if (reg_list[i].reg_data_type)
810 reg_list[i].reg_data_type->type = armv7m_regs[i].type;
811 else
812 LOG_ERROR("unable to allocate reg type list");
813 }
814
815 arm->cpsr = reg_list + ARMV7M_xPSR;
816 arm->pc = reg_list + ARMV7M_PC;
817 arm->core_cache = cache;
818
819 return cache;
820 }
821
822 void armv7m_free_reg_cache(struct target *target)
823 {
824 struct armv7m_common *armv7m = target_to_armv7m(target);
825 struct arm *arm = &armv7m->arm;
826 struct reg_cache *cache;
827 struct reg *reg;
828 unsigned int i;
829
830 cache = arm->core_cache;
831
832 if (!cache)
833 return;
834
835 for (i = 0; i < cache->num_regs; i++) {
836 reg = &cache->reg_list[i];
837
838 free(reg->feature);
839 free(reg->reg_data_type);
840 }
841
842 free(cache->reg_list[0].arch_info);
843 free(cache->reg_list);
844 free(cache);
845
846 arm->core_cache = NULL;
847 }
848
849 static int armv7m_setup_semihosting(struct target *target, int enable)
850 {
851 /* nothing todo for armv7m */
852 return ERROR_OK;
853 }
854
855 /** Sets up target as a generic ARMv7-M core */
856 int armv7m_init_arch_info(struct target *target, struct armv7m_common *armv7m)
857 {
858 struct arm *arm = &armv7m->arm;
859
860 armv7m->common_magic = ARMV7M_COMMON_MAGIC;
861 armv7m->fp_feature = FP_NONE;
862 armv7m->trace_config.trace_bus_id = 1;
863 /* Enable stimulus port #0 by default */
864 armv7m->trace_config.itm_ter[0] = 1;
865
866 arm->core_type = ARM_CORE_TYPE_M_PROFILE;
867 arm->arch_info = armv7m;
868 arm->setup_semihosting = armv7m_setup_semihosting;
869
870 arm->read_core_reg = armv7m_read_core_reg;
871 arm->write_core_reg = armv7m_write_core_reg;
872
873 return arm_init_arch_info(target, arm);
874 }
875
876 /** Generates a CRC32 checksum of a memory region. */
877 int armv7m_checksum_memory(struct target *target,
878 target_addr_t address, uint32_t count, uint32_t *checksum)
879 {
880 struct working_area *crc_algorithm;
881 struct armv7m_algorithm armv7m_info;
882 struct reg_param reg_params[2];
883 int retval;
884
885 static const uint8_t cortex_m_crc_code[] = {
886 #include "../../contrib/loaders/checksum/armv7m_crc.inc"
887 };
888
889 retval = target_alloc_working_area(target, sizeof(cortex_m_crc_code), &crc_algorithm);
890 if (retval != ERROR_OK)
891 return retval;
892
893 retval = target_write_buffer(target, crc_algorithm->address,
894 sizeof(cortex_m_crc_code), (uint8_t *)cortex_m_crc_code);
895 if (retval != ERROR_OK)
896 goto cleanup;
897
898 armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
899 armv7m_info.core_mode = ARM_MODE_THREAD;
900
901 init_reg_param(&reg_params[0], "r0", 32, PARAM_IN_OUT);
902 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
903
904 buf_set_u32(reg_params[0].value, 0, 32, address);
905 buf_set_u32(reg_params[1].value, 0, 32, count);
906
907 int timeout = 20000 * (1 + (count / (1024 * 1024)));
908
909 retval = target_run_algorithm(target, 0, NULL, 2, reg_params, crc_algorithm->address,
910 crc_algorithm->address + (sizeof(cortex_m_crc_code) - 6),
911 timeout, &armv7m_info);
912
913 if (retval == ERROR_OK)
914 *checksum = buf_get_u32(reg_params[0].value, 0, 32);
915 else
916 LOG_ERROR("error executing cortex_m crc algorithm");
917
918 destroy_reg_param(&reg_params[0]);
919 destroy_reg_param(&reg_params[1]);
920
921 cleanup:
922 target_free_working_area(target, crc_algorithm);
923
924 return retval;
925 }
926
927 /** Checks an array of memory regions whether they are erased. */
928 int armv7m_blank_check_memory(struct target *target,
929 struct target_memory_check_block *blocks, int num_blocks, uint8_t erased_value)
930 {
931 struct working_area *erase_check_algorithm;
932 struct working_area *erase_check_params;
933 struct reg_param reg_params[2];
934 struct armv7m_algorithm armv7m_info;
935 int retval;
936
937 static bool timed_out;
938
939 static const uint8_t erase_check_code[] = {
940 #include "../../contrib/loaders/erase_check/armv7m_erase_check.inc"
941 };
942
943 const uint32_t code_size = sizeof(erase_check_code);
944
945 /* make sure we have a working area */
946 if (target_alloc_working_area(target, code_size,
947 &erase_check_algorithm) != ERROR_OK)
948 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
949
950 retval = target_write_buffer(target, erase_check_algorithm->address,
951 code_size, erase_check_code);
952 if (retval != ERROR_OK)
953 goto cleanup1;
954
955 /* prepare blocks array for algo */
956 struct algo_block {
957 union {
958 uint32_t size;
959 uint32_t result;
960 };
961 uint32_t address;
962 };
963
964 uint32_t avail = target_get_working_area_avail(target);
965 int blocks_to_check = avail / sizeof(struct algo_block) - 1;
966 if (num_blocks < blocks_to_check)
967 blocks_to_check = num_blocks;
968
969 struct algo_block *params = malloc((blocks_to_check+1)*sizeof(struct algo_block));
970 if (!params) {
971 retval = ERROR_FAIL;
972 goto cleanup1;
973 }
974
975 int i;
976 uint32_t total_size = 0;
977 for (i = 0; i < blocks_to_check; i++) {
978 total_size += blocks[i].size;
979 target_buffer_set_u32(target, (uint8_t *)&(params[i].size),
980 blocks[i].size / sizeof(uint32_t));
981 target_buffer_set_u32(target, (uint8_t *)&(params[i].address),
982 blocks[i].address);
983 }
984 target_buffer_set_u32(target, (uint8_t *)&(params[blocks_to_check].size), 0);
985
986 uint32_t param_size = (blocks_to_check + 1) * sizeof(struct algo_block);
987 if (target_alloc_working_area(target, param_size,
988 &erase_check_params) != ERROR_OK) {
989 retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
990 goto cleanup2;
991 }
992
993 retval = target_write_buffer(target, erase_check_params->address,
994 param_size, (uint8_t *)params);
995 if (retval != ERROR_OK)
996 goto cleanup3;
997
998 uint32_t erased_word = erased_value | (erased_value << 8)
999 | (erased_value << 16) | (erased_value << 24);
1000
1001 LOG_DEBUG("Starting erase check of %d blocks, parameters@"
1002 TARGET_ADDR_FMT, blocks_to_check, erase_check_params->address);
1003
1004 armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
1005 armv7m_info.core_mode = ARM_MODE_THREAD;
1006
1007 init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
1008 buf_set_u32(reg_params[0].value, 0, 32, erase_check_params->address);
1009
1010 init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
1011 buf_set_u32(reg_params[1].value, 0, 32, erased_word);
1012
1013 /* assume CPU clk at least 1 MHz */
1014 int timeout = (timed_out ? 30000 : 2000) + total_size * 3 / 1000;
1015
1016 retval = target_run_algorithm(target,
1017 0, NULL,
1018 ARRAY_SIZE(reg_params), reg_params,
1019 erase_check_algorithm->address,
1020 erase_check_algorithm->address + (code_size - 2),
1021 timeout,
1022 &armv7m_info);
1023
1024 timed_out = retval == ERROR_TARGET_TIMEOUT;
1025 if (retval != ERROR_OK && !timed_out)
1026 goto cleanup4;
1027
1028 retval = target_read_buffer(target, erase_check_params->address,
1029 param_size, (uint8_t *)params);
1030 if (retval != ERROR_OK)
1031 goto cleanup4;
1032
1033 for (i = 0; i < blocks_to_check; i++) {
1034 uint32_t result = target_buffer_get_u32(target,
1035 (uint8_t *)&(params[i].result));
1036 if (result != 0 && result != 1)
1037 break;
1038
1039 blocks[i].result = result;
1040 }
1041 if (i && timed_out)
1042 LOG_INFO("Slow CPU clock: %d blocks checked, %d remain. Continuing...", i, num_blocks-i);
1043
1044 retval = i; /* return number of blocks really checked */
1045
1046 cleanup4:
1047 destroy_reg_param(&reg_params[0]);
1048 destroy_reg_param(&reg_params[1]);
1049
1050 cleanup3:
1051 target_free_working_area(target, erase_check_params);
1052 cleanup2:
1053 free(params);
1054 cleanup1:
1055 target_free_working_area(target, erase_check_algorithm);
1056
1057 return retval;
1058 }
1059
1060 int armv7m_maybe_skip_bkpt_inst(struct target *target, bool *inst_found)
1061 {
1062 struct armv7m_common *armv7m = target_to_armv7m(target);
1063 struct reg *r = armv7m->arm.pc;
1064 bool result = false;
1065
1066
1067 /* if we halted last time due to a bkpt instruction
1068 * then we have to manually step over it, otherwise
1069 * the core will break again */
1070
1071 if (target->debug_reason == DBG_REASON_BREAKPOINT) {
1072 uint16_t op;
1073 uint32_t pc = buf_get_u32(r->value, 0, 32);
1074
1075 pc &= ~1;
1076 if (target_read_u16(target, pc, &op) == ERROR_OK) {
1077 if ((op & 0xFF00) == 0xBE00) {
1078 pc = buf_get_u32(r->value, 0, 32) + 2;
1079 buf_set_u32(r->value, 0, 32, pc);
1080 r->dirty = true;
1081 r->valid = true;
1082 result = true;
1083 LOG_DEBUG("Skipping over BKPT instruction");
1084 }
1085 }
1086 }
1087
1088 if (inst_found)
1089 *inst_found = result;
1090
1091 return ERROR_OK;
1092 }
1093
1094 const struct command_registration armv7m_command_handlers[] = {
1095 {
1096 .chain = arm_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)