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

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)