d00852c201301ebd6ffa1165ec9e290a98c8f642
[openocd.git] / src / target / stm32_stlink.c
1 /***************************************************************************
2 * Copyright (C) 2011 by Mathias Kuester *
3 * Mathias Kuester <kesmtp@freenet.de> *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "jtag/jtag.h"
25 #include "jtag/stlink/stlink_interface.h"
26 #include "jtag/stlink/stlink_layout.h"
27 #include "register.h"
28 #include "algorithm.h"
29 #include "target.h"
30 #include "breakpoints.h"
31 #include "target_type.h"
32 #include "armv7m.h"
33 #include "cortex_m.h"
34
35 static inline struct stlink_interface_s *target_to_stlink(struct target *target)
36 {
37 return target->tap->priv;
38 }
39
40 static int stm32_stlink_load_core_reg_u32(struct target *target,
41 enum armv7m_regtype type,
42 uint32_t num, uint32_t *value)
43 {
44 int retval;
45 struct stlink_interface_s *stlink_if = target_to_stlink(target);
46
47 LOG_DEBUG("%s", __func__);
48
49 /* NOTE: we "know" here that the register identifiers used
50 * in the v7m header match the Cortex-M3 Debug Core Register
51 * Selector values for R0..R15, xPSR, MSP, and PSP.
52 */
53 switch (num) {
54 case 0 ... 18:
55 /* read a normal core register */
56 retval =
57 stlink_if->layout->api->read_reg(stlink_if->fd, num, value);
58
59 if (retval != ERROR_OK) {
60 LOG_ERROR("JTAG failure %i", retval);
61 return ERROR_JTAG_DEVICE_ERROR;
62 }
63 LOG_DEBUG("load from core reg %i value 0x%" PRIx32 "",
64 (int)num, *value);
65 break;
66
67 case ARMV7M_PRIMASK:
68 case ARMV7M_BASEPRI:
69 case ARMV7M_FAULTMASK:
70 case ARMV7M_CONTROL:
71 /* Cortex-M3 packages these four registers as bitfields
72 * in one Debug Core register. So say r0 and r2 docs;
73 * it was removed from r1 docs, but still works.
74 */
75 retval =
76 stlink_if->layout->api->read_reg(stlink_if->fd, 20, value);
77
78 switch (num) {
79 case ARMV7M_PRIMASK:
80 *value = buf_get_u32((uint8_t *) value, 0, 1);
81 break;
82
83 case ARMV7M_BASEPRI:
84 *value = buf_get_u32((uint8_t *) value, 8, 8);
85 break;
86
87 case ARMV7M_FAULTMASK:
88 *value = buf_get_u32((uint8_t *) value, 16, 1);
89 break;
90
91 case ARMV7M_CONTROL:
92 *value = buf_get_u32((uint8_t *) value, 24, 2);
93 break;
94 }
95
96 LOG_DEBUG("load from special reg %i value 0x%" PRIx32 "",
97 (int)num, *value);
98 break;
99
100 default:
101 return ERROR_COMMAND_SYNTAX_ERROR;
102 }
103
104 return ERROR_OK;
105 }
106
107 static int stm32_stlink_store_core_reg_u32(struct target *target,
108 enum armv7m_regtype type,
109 uint32_t num, uint32_t value)
110 {
111 int retval;
112 uint32_t reg;
113 struct armv7m_common *armv7m = target_to_armv7m(target);
114 struct stlink_interface_s *stlink_if = target_to_stlink(target);
115
116 LOG_DEBUG("%s", __func__);
117
118 #ifdef ARMV7_GDB_HACKS
119 /* If the LR register is being modified, make sure it will put us
120 * in "thumb" mode, or an INVSTATE exception will occur. This is a
121 * hack to deal with the fact that gdb will sometimes "forge"
122 * return addresses, and doesn't set the LSB correctly (i.e., when
123 * printing expressions containing function calls, it sets LR = 0.)
124 * Valid exception return codes have bit 0 set too.
125 */
126 if (num == ARMV7M_R14)
127 value |= 0x01;
128 #endif
129
130 /* NOTE: we "know" here that the register identifiers used
131 * in the v7m header match the Cortex-M3 Debug Core Register
132 * Selector values for R0..R15, xPSR, MSP, and PSP.
133 */
134 switch (num) {
135 case 0 ... 18:
136 retval = stlink_if->layout->api->write_reg(stlink_if->fd, num, value);
137
138 if (retval != ERROR_OK) {
139 struct reg *r;
140
141 LOG_ERROR("JTAG failure");
142 r = armv7m->core_cache->reg_list + num;
143 r->dirty = r->valid;
144 return ERROR_JTAG_DEVICE_ERROR;
145 }
146 LOG_DEBUG("write core reg %i value 0x%" PRIx32 "", (int)num, value);
147 break;
148
149 case ARMV7M_PRIMASK:
150 case ARMV7M_BASEPRI:
151 case ARMV7M_FAULTMASK:
152 case ARMV7M_CONTROL:
153 /* Cortex-M3 packages these four registers as bitfields
154 * in one Debug Core register. So say r0 and r2 docs;
155 * it was removed from r1 docs, but still works.
156 */
157
158 stlink_if->layout->api->read_reg(stlink_if->fd, 20, &reg);
159
160 switch (num) {
161 case ARMV7M_PRIMASK:
162 buf_set_u32((uint8_t *) &reg, 0, 1, value);
163 break;
164
165 case ARMV7M_BASEPRI:
166 buf_set_u32((uint8_t *) &reg, 8, 8, value);
167 break;
168
169 case ARMV7M_FAULTMASK:
170 buf_set_u32((uint8_t *) &reg, 16, 1, value);
171 break;
172
173 case ARMV7M_CONTROL:
174 buf_set_u32((uint8_t *) &reg, 24, 2, value);
175 break;
176 }
177
178 stlink_if->layout->api->write_reg(stlink_if->fd, 20, reg);
179
180 LOG_DEBUG("write special reg %i value 0x%" PRIx32 " ", (int)num, value);
181 break;
182
183 default:
184 return ERROR_COMMAND_SYNTAX_ERROR;
185 }
186
187 return ERROR_OK;
188 }
189
190 static int stm32_stlink_init_arch_info(struct target *target,
191 struct cortex_m3_common *cortex_m3,
192 struct jtag_tap *tap)
193 {
194 struct armv7m_common *armv7m;
195
196 LOG_DEBUG("%s", __func__);
197
198 armv7m = &cortex_m3->armv7m;
199 armv7m_init_arch_info(target, armv7m);
200
201 armv7m->load_core_reg_u32 = stm32_stlink_load_core_reg_u32;
202 armv7m->store_core_reg_u32 = stm32_stlink_store_core_reg_u32;
203
204 return ERROR_OK;
205 }
206
207 static int stm32_stlink_init_target(struct command_context *cmd_ctx,
208 struct target *target)
209 {
210 LOG_DEBUG("%s", __func__);
211
212 armv7m_build_reg_cache(target);
213
214 return ERROR_OK;
215 }
216
217 static int stm32_stlink_target_create(struct target *target,
218 Jim_Interp *interp)
219 {
220 LOG_DEBUG("%s", __func__);
221
222 struct cortex_m3_common *cortex_m3 = calloc(1, sizeof(struct cortex_m3_common));
223
224 if (!cortex_m3)
225 return ERROR_COMMAND_SYNTAX_ERROR;
226
227 stm32_stlink_init_arch_info(target, cortex_m3, target->tap);
228
229 return ERROR_OK;
230 }
231
232 static int stm32_stlink_poll(struct target *target);
233
234 static int stm32_stlink_examine(struct target *target)
235 {
236 int retval, i;
237 uint32_t cpuid, fpcr;
238 struct cortex_m3_common *cortex_m3 = target_to_cm3(target);
239
240 LOG_DEBUG("%s", __func__);
241
242 if (target->tap->hasidcode == false) {
243 LOG_ERROR("no IDCODE present on device");
244
245 return ERROR_COMMAND_SYNTAX_ERROR;
246 }
247
248 if (!target_was_examined(target)) {
249 target_set_examined(target);
250
251 stm32_stlink_poll(target);
252
253 LOG_INFO("IDCODE %x", target->tap->idcode);
254
255 /* Read from Device Identification Registers */
256 retval = target_read_u32(target, CPUID, &cpuid);
257 if (retval != ERROR_OK)
258 return retval;
259
260 if (((cpuid >> 4) & 0xc3f) == 0xc23)
261 LOG_DEBUG("Cortex-M3 r%" PRId8 "p%" PRId8 " processor detected",
262 (uint8_t)((cpuid >> 20) & 0xf), (uint8_t)((cpuid >> 0) & 0xf));
263 LOG_DEBUG("cpuid: 0x%8.8" PRIx32 "", cpuid);
264
265 /* Setup FPB */
266 target_read_u32(target, FP_CTRL, &fpcr);
267 cortex_m3->auto_bp_type = 1;
268 cortex_m3->fp_num_code = ((fpcr >> 8) & 0x70) |
269 ((fpcr >> 4) & 0xF); /* bits [14:12] and [7:4] */
270 cortex_m3->fp_num_lit = (fpcr >> 8) & 0xF;
271 cortex_m3->fp_code_available = cortex_m3->fp_num_code;
272 cortex_m3->fp_comparator_list = calloc(cortex_m3->fp_num_code +
273 cortex_m3->fp_num_lit, sizeof(struct cortex_m3_fp_comparator));
274 cortex_m3->fpb_enabled = fpcr & 1;
275 for (i = 0; i < cortex_m3->fp_num_code + cortex_m3->fp_num_lit; i++) {
276 cortex_m3->fp_comparator_list[i].type =
277 (i < cortex_m3->fp_num_code) ? FPCR_CODE : FPCR_LITERAL;
278 cortex_m3->fp_comparator_list[i].fpcr_address = FP_COMP0 + 4 * i;
279 }
280 LOG_DEBUG("FPB fpcr 0x%" PRIx32 ", numcode %i, numlit %i", fpcr,
281 cortex_m3->fp_num_code, cortex_m3->fp_num_lit);
282
283 /* Setup DWT */
284 cortex_m3_dwt_setup(cortex_m3, target);
285
286 /* These hardware breakpoints only work for code in flash! */
287 LOG_INFO("%s: hardware has %d breakpoints, %d watchpoints",
288 target_name(target),
289 cortex_m3->fp_num_code,
290 cortex_m3->dwt_num_comp);
291 }
292
293 return ERROR_OK;
294 }
295
296 static int stm32_stlink_load_context(struct target *target)
297 {
298 struct armv7m_common *armv7m = target_to_armv7m(target);
299
300 for (unsigned i = 0; i < 23; i++) {
301 if (!armv7m->core_cache->reg_list[i].valid)
302 armv7m->read_core_reg(target, i);
303 }
304
305 return ERROR_OK;
306 }
307
308 static int stm32_stlink_poll(struct target *target)
309 {
310 enum target_state state;
311 struct stlink_interface_s *stlink_if = target_to_stlink(target);
312 struct armv7m_common *armv7m = target_to_armv7m(target);
313
314 state = stlink_if->layout->api->state(stlink_if->fd);
315
316 if (state == TARGET_UNKNOWN) {
317 LOG_ERROR
318 ("jtag status contains invalid mode value - communication failure");
319 return ERROR_TARGET_FAILURE;
320 }
321
322 if (target->state == state)
323 return ERROR_OK;
324
325 if (state == TARGET_HALTED) {
326 target_call_event_callbacks(target, TARGET_EVENT_DEBUG_HALTED);
327 target->state = state;
328
329 stm32_stlink_load_context(target);
330
331 LOG_INFO("halted: PC: 0x%x", buf_get_u32(armv7m->arm.pc->value, 0, 32));
332 }
333
334 return ERROR_OK;
335 }
336
337 static int stm32_stlink_arch_state(struct target *target)
338 {
339 LOG_DEBUG("%s", __func__);
340 return ERROR_OK;
341 }
342
343 static int stm32_stlink_assert_reset(struct target *target)
344 {
345 int res;
346 struct stlink_interface_s *stlink_if = target_to_stlink(target);
347 struct armv7m_common *armv7m = target_to_armv7m(target);
348
349 LOG_DEBUG("%s", __func__);
350
351 res = stlink_if->layout->api->reset(stlink_if->fd);
352
353 if (res != ERROR_OK)
354 return res;
355
356 /* virtual assert reset, we need it for the internal
357 * jtag state machine
358 */
359 jtag_add_reset(1, 1);
360
361 /* registers are now invalid */
362 register_cache_invalidate(armv7m->core_cache);
363
364 stm32_stlink_load_context(target);
365
366 target->state = TARGET_HALTED;
367
368 return ERROR_OK;
369 }
370
371 static int stm32_stlink_deassert_reset(struct target *target)
372 {
373 int res;
374
375 LOG_DEBUG("%s", __func__);
376
377 /* virtual deassert reset, we need it for the internal
378 * jtag state machine
379 */
380 jtag_add_reset(0, 0);
381
382 if (!target->reset_halt) {
383 res = target_resume(target, 1, 0, 0, 0);
384
385 if (res != ERROR_OK)
386 return res;
387 }
388
389 return ERROR_OK;
390 }
391
392 static int stm32_stlink_soft_reset_halt(struct target *target)
393 {
394 LOG_DEBUG("%s", __func__);
395 return ERROR_OK;
396 }
397
398 static int stm32_stlink_halt(struct target *target)
399 {
400 int res;
401 struct stlink_interface_s *stlink_if = target_to_stlink(target);
402
403 LOG_DEBUG("%s", __func__);
404
405 if (target->state == TARGET_HALTED) {
406 LOG_DEBUG("target was already halted");
407 return ERROR_OK;
408 }
409
410 if (target->state == TARGET_UNKNOWN) {
411 LOG_WARNING
412 ("target was in unknown state when halt was requested");
413 }
414
415 res = stlink_if->layout->api->halt(stlink_if->fd);
416
417 if (res != ERROR_OK)
418 return res;
419
420 target->debug_reason = DBG_REASON_DBGRQ;
421
422 return ERROR_OK;
423 }
424
425 static int stm32_stlink_resume(struct target *target, int current,
426 uint32_t address, int handle_breakpoints,
427 int debug_execution)
428 {
429 int res;
430 struct stlink_interface_s *stlink_if = target_to_stlink(target);
431 struct armv7m_common *armv7m = target_to_armv7m(target);
432 uint32_t resume_pc;
433 struct breakpoint *breakpoint = NULL;
434 struct reg *pc;
435
436 LOG_DEBUG("%s %d %x %d %d", __func__, current, address,
437 handle_breakpoints, debug_execution);
438
439 if (target->state != TARGET_HALTED) {
440 LOG_WARNING("target not halted");
441 return ERROR_TARGET_NOT_HALTED;
442 }
443
444 pc = armv7m->arm.pc;
445 if (!current) {
446 buf_set_u32(pc->value, 0, 32, address);
447 pc->dirty = true;
448 pc->valid = true;
449 }
450
451 if (!breakpoint_find(target, buf_get_u32(pc->value, 0, 32))
452 && !debug_execution) {
453 armv7m_maybe_skip_bkpt_inst(target, NULL);
454 }
455
456 resume_pc = buf_get_u32(pc->value, 0, 32);
457
458 armv7m_restore_context(target);
459
460 /* registers are now invalid */
461 register_cache_invalidate(armv7m->core_cache);
462
463 /* the front-end may request us not to handle breakpoints */
464 if (handle_breakpoints) {
465 /* Single step past breakpoint at current address */
466 breakpoint = breakpoint_find(target, resume_pc);
467 if (breakpoint) {
468 LOG_DEBUG("unset breakpoint at 0x%8.8" PRIx32 " (ID: %d)",
469 breakpoint->address,
470 breakpoint->unique_id);
471 cortex_m3_unset_breakpoint(target, breakpoint);
472
473 res = stlink_if->layout->api->step(stlink_if->fd);
474
475 if (res != ERROR_OK)
476 return res;
477
478 cortex_m3_set_breakpoint(target, breakpoint);
479 }
480 }
481
482 res = stlink_if->layout->api->run(stlink_if->fd);
483
484 if (res != ERROR_OK)
485 return res;
486
487 target->state = TARGET_RUNNING;
488
489 target_call_event_callbacks(target, TARGET_EVENT_DEBUG_RESUMED);
490
491 return ERROR_OK;
492 }
493
494 static int stm32_stlink_step(struct target *target, int current,
495 uint32_t address, int handle_breakpoints)
496 {
497 int res;
498 struct stlink_interface_s *stlink_if = target_to_stlink(target);
499 struct armv7m_common *armv7m = target_to_armv7m(target);
500 struct breakpoint *breakpoint = NULL;
501 struct reg *pc = armv7m->arm.pc;
502 bool bkpt_inst_found = false;
503
504 LOG_DEBUG("%s", __func__);
505
506 if (target->state != TARGET_HALTED) {
507 LOG_WARNING("target not halted");
508 return ERROR_TARGET_NOT_HALTED;
509 }
510
511 if (!current) {
512 buf_set_u32(pc->value, 0, 32, address);
513 pc->dirty = true;
514 pc->valid = true;
515 }
516
517 uint32_t pc_value = buf_get_u32(pc->value, 0, 32);
518
519 /* the front-end may request us not to handle breakpoints */
520 if (handle_breakpoints) {
521 breakpoint = breakpoint_find(target, pc_value);
522 if (breakpoint)
523 cortex_m3_unset_breakpoint(target, breakpoint);
524 }
525
526 armv7m_maybe_skip_bkpt_inst(target, &bkpt_inst_found);
527
528 target->debug_reason = DBG_REASON_SINGLESTEP;
529
530 armv7m_restore_context(target);
531
532 target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
533
534 res = stlink_if->layout->api->step(stlink_if->fd);
535
536 if (res != ERROR_OK)
537 return res;
538
539 /* registers are now invalid */
540 register_cache_invalidate(armv7m->core_cache);
541
542 if (breakpoint)
543 cortex_m3_set_breakpoint(target, breakpoint);
544
545 target->debug_reason = DBG_REASON_SINGLESTEP;
546 target_call_event_callbacks(target, TARGET_EVENT_HALTED);
547
548 stm32_stlink_load_context(target);
549
550 LOG_INFO("halted: PC: 0x%x", buf_get_u32(armv7m->arm.pc->value, 0, 32));
551
552 return ERROR_OK;
553 }
554
555 static int stm32_stlink_read_memory(struct target *target, uint32_t address,
556 uint32_t size, uint32_t count,
557 uint8_t *buffer)
558 {
559 int res;
560 uint32_t buffer_threshold = 128;
561 uint32_t addr_increment = 4;
562 uint8_t *dst = buffer;
563 uint32_t c;
564 struct stlink_interface_s *stlink_if = target_to_stlink(target);
565
566 if (!count || !buffer)
567 return ERROR_COMMAND_SYNTAX_ERROR;
568
569 LOG_DEBUG("%s %x %d %d", __func__, address, size, count);
570
571 /* prepare byte count, buffer threshold
572 * and address increment for none 32bit access
573 */
574 if (size != 4) {
575 count *= size;
576 buffer_threshold = 64;
577 addr_increment = 1;
578 }
579
580 while (count) {
581 if (count > buffer_threshold)
582 c = buffer_threshold;
583 else
584 c = count;
585
586 if (size != 4)
587 res =
588 stlink_if->layout->api->read_mem8(stlink_if->fd, address,
589 c, dst);
590 else
591 res =
592 stlink_if->layout->api->read_mem32(stlink_if->fd, address,
593 c, (uint32_t *)dst);
594
595 if (res != ERROR_OK)
596 return res;
597
598 address += (c * addr_increment);
599 dst += (c * addr_increment);
600 count -= c;
601 }
602
603 return ERROR_OK;
604 }
605
606 static int stm32_stlink_write_memory(struct target *target, uint32_t address,
607 uint32_t size, uint32_t count,
608 const uint8_t *buffer)
609 {
610 int res;
611 uint32_t buffer_threshold = 128;
612 uint32_t addr_increment = 4;
613 const uint8_t *dst = buffer;
614 uint32_t c;
615 struct stlink_interface_s *stlink_if = target_to_stlink(target);
616
617 if (!count || !buffer)
618 return ERROR_COMMAND_SYNTAX_ERROR;
619
620 LOG_DEBUG("%s %x %d %d", __func__, address, size, count);
621
622 /* prepare byte count, buffer threshold
623 * and address increment for none 32bit access
624 */
625 if (size != 4) {
626 count *= size;
627 buffer_threshold = 64;
628 addr_increment = 1;
629 }
630
631 while (count) {
632 if (count > buffer_threshold)
633 c = buffer_threshold;
634 else
635 c = count;
636
637 if (size != 4)
638 res =
639 stlink_if->layout->api->write_mem8(stlink_if->fd, address,
640 c, dst);
641 else
642 res =
643 stlink_if->layout->api->write_mem32(stlink_if->fd, address,
644 c, (uint32_t *)dst);
645
646 if (res != ERROR_OK)
647 return res;
648
649 address += (c * addr_increment);
650 dst += (c * addr_increment);
651 count -= c;
652 }
653
654 return ERROR_OK;
655 }
656
657 static int stm32_stlink_bulk_write_memory(struct target *target,
658 uint32_t address, uint32_t count,
659 const uint8_t *buffer)
660 {
661 return stm32_stlink_write_memory(target, address, 4, count, buffer);
662 }
663
664 struct target_type stm32_stlink_target = {
665 .name = "stm32_stlink",
666
667 .init_target = stm32_stlink_init_target,
668 .target_create = stm32_stlink_target_create,
669 .examine = stm32_stlink_examine,
670
671 .poll = stm32_stlink_poll,
672 .arch_state = stm32_stlink_arch_state,
673
674 .assert_reset = stm32_stlink_assert_reset,
675 .deassert_reset = stm32_stlink_deassert_reset,
676 .soft_reset_halt = stm32_stlink_soft_reset_halt,
677
678 .halt = stm32_stlink_halt,
679 .resume = stm32_stlink_resume,
680 .step = stm32_stlink_step,
681
682 .get_gdb_reg_list = armv7m_get_gdb_reg_list,
683
684 .read_memory = stm32_stlink_read_memory,
685 .write_memory = stm32_stlink_write_memory,
686 .bulk_write_memory = stm32_stlink_bulk_write_memory,
687
688 .run_algorithm = armv7m_run_algorithm,
689 .start_algorithm = armv7m_start_algorithm,
690 .wait_algorithm = armv7m_wait_algorithm,
691
692 .add_breakpoint = cortex_m3_add_breakpoint,
693 .remove_breakpoint = cortex_m3_remove_breakpoint,
694 .add_watchpoint = cortex_m3_add_watchpoint,
695 .remove_watchpoint = cortex_m3_remove_watchpoint,
696 };

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)