target/hla_target: set cortex_m->common_magic
[openocd.git] / src / target / hla_target.c
1 /***************************************************************************
2 * Copyright (C) 2011 by Mathias Kuester *
3 * Mathias Kuester <kesmtp@freenet.de> *
4 * *
5 * Copyright (C) 2011 by Spencer Oliver *
6 * spen@spen-soft.co.uk *
7 * *
8 * revised: 4/25/13 by brent@mbari.org [DCC target request support] *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
22 ***************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "jtag/interface.h"
29 #include "jtag/jtag.h"
30 #include "jtag/hla/hla_transport.h"
31 #include "jtag/hla/hla_interface.h"
32 #include "jtag/hla/hla_layout.h"
33 #include "register.h"
34 #include "algorithm.h"
35 #include "target.h"
36 #include "breakpoints.h"
37 #include "target_type.h"
38 #include "armv7m.h"
39 #include "cortex_m.h"
40 #include "arm_adi_v5.h"
41 #include "arm_semihosting.h"
42 #include "target_request.h"
43 #include <rtt/rtt.h>
44
45 #define SAVED_DCRDR dbgbase /* FIXME: using target->dbgbase to preserve DCRDR */
46
47 #define ARMV7M_SCS_DCRSR DCB_DCRSR
48 #define ARMV7M_SCS_DCRDR DCB_DCRDR
49
50 static inline struct hl_interface_s *target_to_adapter(struct target *target)
51 {
52 return target->tap->priv;
53 }
54
55 static int adapter_load_core_reg_u32(struct target *target,
56 uint32_t regsel, uint32_t *value)
57 {
58 struct hl_interface_s *adapter = target_to_adapter(target);
59 return adapter->layout->api->read_reg(adapter->handle, regsel, value);
60 }
61
62 static int adapter_store_core_reg_u32(struct target *target,
63 uint32_t regsel, uint32_t value)
64 {
65 struct hl_interface_s *adapter = target_to_adapter(target);
66 return adapter->layout->api->write_reg(adapter->handle, regsel, value);
67 }
68
69 static int adapter_examine_debug_reason(struct target *target)
70 {
71 if ((target->debug_reason != DBG_REASON_DBGRQ)
72 && (target->debug_reason != DBG_REASON_SINGLESTEP)) {
73 target->debug_reason = DBG_REASON_BREAKPOINT;
74 }
75
76 return ERROR_OK;
77 }
78
79 static int hl_dcc_read(struct hl_interface_s *hl_if, uint8_t *value, uint8_t *ctrl)
80 {
81 uint16_t dcrdr;
82 int retval = hl_if->layout->api->read_mem(hl_if->handle,
83 DCB_DCRDR, 1, sizeof(dcrdr), (uint8_t *)&dcrdr);
84 if (retval == ERROR_OK) {
85 *ctrl = (uint8_t)dcrdr;
86 *value = (uint8_t)(dcrdr >> 8);
87
88 LOG_DEBUG("data 0x%x ctrl 0x%x", *value, *ctrl);
89
90 if (dcrdr & 1) {
91 /* write ack back to software dcc register
92 * to signify we have read data */
93 /* atomically clear just the byte containing the busy bit */
94 static const uint8_t zero;
95 retval = hl_if->layout->api->write_mem(hl_if->handle, DCB_DCRDR, 1, 1, &zero);
96 }
97 }
98 return retval;
99 }
100
101 static int hl_target_request_data(struct target *target,
102 uint32_t size, uint8_t *buffer)
103 {
104 struct hl_interface_s *hl_if = target_to_adapter(target);
105 uint8_t data;
106 uint8_t ctrl;
107 uint32_t i;
108
109 for (i = 0; i < (size * 4); i++) {
110 int err = hl_dcc_read(hl_if, &data, &ctrl);
111 if (err != ERROR_OK)
112 return err;
113
114 buffer[i] = data;
115 }
116
117 return ERROR_OK;
118 }
119
120 static int hl_handle_target_request(void *priv)
121 {
122 struct target *target = priv;
123 int err;
124
125 if (!target_was_examined(target))
126 return ERROR_OK;
127 struct hl_interface_s *hl_if = target_to_adapter(target);
128
129 if (!target->dbg_msg_enabled)
130 return ERROR_OK;
131
132 if (target->state == TARGET_RUNNING) {
133 uint8_t data;
134 uint8_t ctrl;
135
136 err = hl_dcc_read(hl_if, &data, &ctrl);
137 if (err != ERROR_OK)
138 return err;
139
140 /* check if we have data */
141 if (ctrl & (1 << 0)) {
142 uint32_t request;
143
144 /* we assume target is quick enough */
145 request = data;
146 err = hl_dcc_read(hl_if, &data, &ctrl);
147 if (err != ERROR_OK)
148 return err;
149
150 request |= (data << 8);
151 err = hl_dcc_read(hl_if, &data, &ctrl);
152 if (err != ERROR_OK)
153 return err;
154
155 request |= (data << 16);
156 err = hl_dcc_read(hl_if, &data, &ctrl);
157 if (err != ERROR_OK)
158 return err;
159
160 request |= (data << 24);
161 target_request(target, request);
162 }
163 }
164
165 return ERROR_OK;
166 }
167
168 static int adapter_init_arch_info(struct target *target,
169 struct cortex_m_common *cortex_m,
170 struct jtag_tap *tap)
171 {
172 struct armv7m_common *armv7m;
173
174 LOG_DEBUG("%s", __func__);
175
176 armv7m = &cortex_m->armv7m;
177 armv7m_init_arch_info(target, armv7m);
178
179 armv7m->load_core_reg_u32 = adapter_load_core_reg_u32;
180 armv7m->store_core_reg_u32 = adapter_store_core_reg_u32;
181
182 armv7m->examine_debug_reason = adapter_examine_debug_reason;
183 armv7m->is_hla_target = true;
184
185 target_register_timer_callback(hl_handle_target_request, 1,
186 TARGET_TIMER_TYPE_PERIODIC, target);
187
188 return ERROR_OK;
189 }
190
191 static int adapter_init_target(struct command_context *cmd_ctx,
192 struct target *target)
193 {
194 LOG_DEBUG("%s", __func__);
195
196 armv7m_build_reg_cache(target);
197 arm_semihosting_init(target);
198 return ERROR_OK;
199 }
200
201 static int adapter_target_create(struct target *target,
202 Jim_Interp *interp)
203 {
204 LOG_DEBUG("%s", __func__);
205 struct adiv5_private_config *pc = target->private_config;
206 if (pc && pc->ap_num > 0) {
207 LOG_ERROR("hla_target: invalid parameter -ap-num (> 0)");
208 return ERROR_COMMAND_SYNTAX_ERROR;
209 }
210
211 struct cortex_m_common *cortex_m = calloc(1, sizeof(struct cortex_m_common));
212 if (!cortex_m) {
213 LOG_ERROR("No memory creating target");
214 return ERROR_FAIL;
215 }
216
217 cortex_m->common_magic = CORTEX_M_COMMON_MAGIC;
218
219 adapter_init_arch_info(target, cortex_m, target->tap);
220
221 return ERROR_OK;
222 }
223
224 static int adapter_load_context(struct target *target)
225 {
226 struct armv7m_common *armv7m = target_to_armv7m(target);
227 int num_regs = armv7m->arm.core_cache->num_regs;
228
229 for (int i = 0; i < num_regs; i++) {
230
231 struct reg *r = &armv7m->arm.core_cache->reg_list[i];
232 if (r->exist && !r->valid)
233 armv7m->arm.read_core_reg(target, r, i, ARM_MODE_ANY);
234 }
235
236 return ERROR_OK;
237 }
238
239 static int adapter_debug_entry(struct target *target)
240 {
241 struct hl_interface_s *adapter = target_to_adapter(target);
242 struct armv7m_common *armv7m = target_to_armv7m(target);
243 struct arm *arm = &armv7m->arm;
244 struct reg *r;
245 uint32_t xPSR;
246 int retval;
247
248 /* preserve the DCRDR across halts */
249 retval = target_read_u32(target, DCB_DCRDR, &target->SAVED_DCRDR);
250 if (retval != ERROR_OK)
251 return retval;
252
253 retval = armv7m->examine_debug_reason(target);
254 if (retval != ERROR_OK)
255 return retval;
256
257 adapter_load_context(target);
258
259 /* make sure we clear the vector catch bit */
260 adapter->layout->api->write_debug_reg(adapter->handle, DCB_DEMCR, TRCENA);
261
262 r = arm->cpsr;
263 xPSR = buf_get_u32(r->value, 0, 32);
264
265 /* Are we in an exception handler */
266 if (xPSR & 0x1FF) {
267 armv7m->exception_number = (xPSR & 0x1FF);
268
269 arm->core_mode = ARM_MODE_HANDLER;
270 arm->map = armv7m_msp_reg_map;
271 } else {
272 unsigned control = buf_get_u32(arm->core_cache
273 ->reg_list[ARMV7M_CONTROL].value, 0, 3);
274
275 /* is this thread privileged? */
276 arm->core_mode = control & 1
277 ? ARM_MODE_USER_THREAD
278 : ARM_MODE_THREAD;
279
280 /* which stack is it using? */
281 if (control & 2)
282 arm->map = armv7m_psp_reg_map;
283 else
284 arm->map = armv7m_msp_reg_map;
285
286 armv7m->exception_number = 0;
287 }
288
289 LOG_DEBUG("entered debug state in core mode: %s at PC 0x%08" PRIx32 ", target->state: %s",
290 arm_mode_name(arm->core_mode),
291 buf_get_u32(arm->pc->value, 0, 32),
292 target_state_name(target));
293
294 return retval;
295 }
296
297 static int adapter_poll(struct target *target)
298 {
299 enum target_state state;
300 struct hl_interface_s *adapter = target_to_adapter(target);
301 struct armv7m_common *armv7m = target_to_armv7m(target);
302 enum target_state prev_target_state = target->state;
303
304 state = adapter->layout->api->state(adapter->handle);
305
306 if (state == TARGET_UNKNOWN) {
307 LOG_ERROR("jtag status contains invalid mode value - communication failure");
308 return ERROR_TARGET_FAILURE;
309 }
310
311 if (prev_target_state == state)
312 return ERROR_OK;
313
314 if (prev_target_state == TARGET_DEBUG_RUNNING && state == TARGET_RUNNING)
315 return ERROR_OK;
316
317 target->state = state;
318
319 if (state == TARGET_HALTED) {
320
321 int retval = adapter_debug_entry(target);
322 if (retval != ERROR_OK)
323 return retval;
324
325 if (prev_target_state == TARGET_DEBUG_RUNNING) {
326 target_call_event_callbacks(target, TARGET_EVENT_DEBUG_HALTED);
327 } else {
328 if (arm_semihosting(target, &retval) != 0)
329 return retval;
330
331 target_call_event_callbacks(target, TARGET_EVENT_HALTED);
332 }
333
334 LOG_DEBUG("halted: PC: 0x%08" PRIx32, buf_get_u32(armv7m->arm.pc->value, 0, 32));
335 }
336
337 return ERROR_OK;
338 }
339
340 static int hl_assert_reset(struct target *target)
341 {
342 int res = ERROR_OK;
343 struct hl_interface_s *adapter = target_to_adapter(target);
344 struct armv7m_common *armv7m = target_to_armv7m(target);
345 bool use_srst_fallback = true;
346
347 LOG_DEBUG("%s", __func__);
348
349 enum reset_types jtag_reset_config = jtag_get_reset_config();
350
351 bool srst_asserted = false;
352
353 if ((jtag_reset_config & RESET_HAS_SRST) &&
354 (jtag_reset_config & RESET_SRST_NO_GATING)) {
355 res = adapter_assert_reset();
356 srst_asserted = true;
357 }
358
359 adapter->layout->api->write_debug_reg(adapter->handle, DCB_DHCSR, DBGKEY|C_DEBUGEN);
360
361 /* only set vector catch if halt is requested */
362 if (target->reset_halt)
363 adapter->layout->api->write_debug_reg(adapter->handle, DCB_DEMCR, TRCENA|VC_CORERESET);
364 else
365 adapter->layout->api->write_debug_reg(adapter->handle, DCB_DEMCR, TRCENA);
366
367 if (jtag_reset_config & RESET_HAS_SRST) {
368 if (!srst_asserted) {
369 res = adapter_assert_reset();
370 }
371 if (res == ERROR_COMMAND_NOTFOUND)
372 LOG_ERROR("Hardware srst not supported, falling back to software reset");
373 else if (res == ERROR_OK) {
374 /* hardware srst supported */
375 use_srst_fallback = false;
376 }
377 }
378
379 if (use_srst_fallback) {
380 /* stlink v1 api does not support hardware srst, so we use a software reset fallback */
381 adapter->layout->api->write_debug_reg(adapter->handle, NVIC_AIRCR, AIRCR_VECTKEY | AIRCR_SYSRESETREQ);
382 }
383
384 res = adapter->layout->api->reset(adapter->handle);
385
386 if (res != ERROR_OK)
387 return res;
388
389 /* registers are now invalid */
390 register_cache_invalidate(armv7m->arm.core_cache);
391
392 if (target->reset_halt) {
393 target->state = TARGET_RESET;
394 target->debug_reason = DBG_REASON_DBGRQ;
395 } else {
396 target->state = TARGET_HALTED;
397 }
398
399 return ERROR_OK;
400 }
401
402 static int hl_deassert_reset(struct target *target)
403 {
404 enum reset_types jtag_reset_config = jtag_get_reset_config();
405
406 LOG_DEBUG("%s", __func__);
407
408 if (jtag_reset_config & RESET_HAS_SRST)
409 adapter_deassert_reset();
410
411 target->SAVED_DCRDR = 0; /* clear both DCC busy bits on initial resume */
412
413 return target->reset_halt ? ERROR_OK : target_resume(target, 1, 0, 0, 0);
414 }
415
416 static int adapter_halt(struct target *target)
417 {
418 int res;
419 struct hl_interface_s *adapter = target_to_adapter(target);
420
421 LOG_DEBUG("%s", __func__);
422
423 if (target->state == TARGET_HALTED) {
424 LOG_DEBUG("target was already halted");
425 return ERROR_OK;
426 }
427
428 if (target->state == TARGET_UNKNOWN)
429 LOG_WARNING("target was in unknown state when halt was requested");
430
431 res = adapter->layout->api->halt(adapter->handle);
432
433 if (res != ERROR_OK)
434 return res;
435
436 target->debug_reason = DBG_REASON_DBGRQ;
437
438 return ERROR_OK;
439 }
440
441 static int adapter_resume(struct target *target, int current,
442 target_addr_t address, int handle_breakpoints,
443 int debug_execution)
444 {
445 int res;
446 struct hl_interface_s *adapter = target_to_adapter(target);
447 struct armv7m_common *armv7m = target_to_armv7m(target);
448 uint32_t resume_pc;
449 struct breakpoint *breakpoint = NULL;
450 struct reg *pc;
451
452 LOG_DEBUG("%s %d " TARGET_ADDR_FMT " %d %d", __func__, current,
453 address, handle_breakpoints, debug_execution);
454
455 if (target->state != TARGET_HALTED) {
456 LOG_WARNING("target not halted");
457 return ERROR_TARGET_NOT_HALTED;
458 }
459
460 if (!debug_execution) {
461 target_free_all_working_areas(target);
462 cortex_m_enable_breakpoints(target);
463 cortex_m_enable_watchpoints(target);
464 }
465
466 pc = armv7m->arm.pc;
467 if (!current) {
468 buf_set_u32(pc->value, 0, 32, address);
469 pc->dirty = true;
470 pc->valid = true;
471 }
472
473 if (!breakpoint_find(target, buf_get_u32(pc->value, 0, 32))
474 && !debug_execution) {
475 armv7m_maybe_skip_bkpt_inst(target, NULL);
476 }
477
478 resume_pc = buf_get_u32(pc->value, 0, 32);
479
480 /* write any user vector flags */
481 res = target_write_u32(target, DCB_DEMCR, TRCENA | armv7m->demcr);
482 if (res != ERROR_OK)
483 return res;
484
485 armv7m_restore_context(target);
486
487 /* restore SAVED_DCRDR */
488 res = target_write_u32(target, DCB_DCRDR, target->SAVED_DCRDR);
489 if (res != ERROR_OK)
490 return res;
491
492 /* registers are now invalid */
493 register_cache_invalidate(armv7m->arm.core_cache);
494
495 /* the front-end may request us not to handle breakpoints */
496 if (handle_breakpoints) {
497 /* Single step past breakpoint at current address */
498 breakpoint = breakpoint_find(target, resume_pc);
499 if (breakpoint) {
500 LOG_DEBUG("unset breakpoint at " TARGET_ADDR_FMT " (ID: %" PRIu32 ")",
501 breakpoint->address,
502 breakpoint->unique_id);
503 cortex_m_unset_breakpoint(target, breakpoint);
504
505 res = adapter->layout->api->step(adapter->handle);
506
507 if (res != ERROR_OK)
508 return res;
509
510 cortex_m_set_breakpoint(target, breakpoint);
511 }
512 }
513
514 res = adapter->layout->api->run(adapter->handle);
515
516 if (res != ERROR_OK)
517 return res;
518
519 target->debug_reason = DBG_REASON_NOTHALTED;
520
521 if (!debug_execution) {
522 target->state = TARGET_RUNNING;
523 target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
524 } else {
525 target->state = TARGET_DEBUG_RUNNING;
526 target_call_event_callbacks(target, TARGET_EVENT_DEBUG_RESUMED);
527 }
528
529 return ERROR_OK;
530 }
531
532 static int adapter_step(struct target *target, int current,
533 target_addr_t address, int handle_breakpoints)
534 {
535 int res;
536 struct hl_interface_s *adapter = target_to_adapter(target);
537 struct armv7m_common *armv7m = target_to_armv7m(target);
538 struct breakpoint *breakpoint = NULL;
539 struct reg *pc = armv7m->arm.pc;
540 bool bkpt_inst_found = false;
541
542 LOG_DEBUG("%s", __func__);
543
544 if (target->state != TARGET_HALTED) {
545 LOG_WARNING("target not halted");
546 return ERROR_TARGET_NOT_HALTED;
547 }
548
549 if (!current) {
550 buf_set_u32(pc->value, 0, 32, address);
551 pc->dirty = true;
552 pc->valid = true;
553 }
554
555 uint32_t pc_value = buf_get_u32(pc->value, 0, 32);
556
557 /* the front-end may request us not to handle breakpoints */
558 if (handle_breakpoints) {
559 breakpoint = breakpoint_find(target, pc_value);
560 if (breakpoint)
561 cortex_m_unset_breakpoint(target, breakpoint);
562 }
563
564 armv7m_maybe_skip_bkpt_inst(target, &bkpt_inst_found);
565
566 target->debug_reason = DBG_REASON_SINGLESTEP;
567
568 armv7m_restore_context(target);
569
570 /* restore SAVED_DCRDR */
571 res = target_write_u32(target, DCB_DCRDR, target->SAVED_DCRDR);
572 if (res != ERROR_OK)
573 return res;
574
575 target_call_event_callbacks(target, TARGET_EVENT_RESUMED);
576
577 res = adapter->layout->api->step(adapter->handle);
578
579 if (res != ERROR_OK)
580 return res;
581
582 /* registers are now invalid */
583 register_cache_invalidate(armv7m->arm.core_cache);
584
585 if (breakpoint)
586 cortex_m_set_breakpoint(target, breakpoint);
587
588 adapter_debug_entry(target);
589 target_call_event_callbacks(target, TARGET_EVENT_HALTED);
590
591 LOG_INFO("halted: PC: 0x%08" PRIx32, buf_get_u32(armv7m->arm.pc->value, 0, 32));
592
593 return ERROR_OK;
594 }
595
596 static int adapter_read_memory(struct target *target, target_addr_t address,
597 uint32_t size, uint32_t count,
598 uint8_t *buffer)
599 {
600 struct hl_interface_s *adapter = target_to_adapter(target);
601
602 if (!count || !buffer)
603 return ERROR_COMMAND_SYNTAX_ERROR;
604
605 LOG_DEBUG("%s " TARGET_ADDR_FMT " %" PRIu32 " %" PRIu32,
606 __func__, address, size, count);
607
608 return adapter->layout->api->read_mem(adapter->handle, address, size, count, buffer);
609 }
610
611 static int adapter_write_memory(struct target *target, target_addr_t address,
612 uint32_t size, uint32_t count,
613 const uint8_t *buffer)
614 {
615 struct hl_interface_s *adapter = target_to_adapter(target);
616
617 if (!count || !buffer)
618 return ERROR_COMMAND_SYNTAX_ERROR;
619
620 LOG_DEBUG("%s " TARGET_ADDR_FMT " %" PRIu32 " %" PRIu32,
621 __func__, address, size, count);
622
623 return adapter->layout->api->write_mem(adapter->handle, address, size, count, buffer);
624 }
625
626 static const struct command_registration hla_command_handlers[] = {
627 {
628 .chain = arm_command_handlers,
629 },
630 {
631 .chain = armv7m_trace_command_handlers,
632 },
633 {
634 .chain = rtt_target_command_handlers,
635 },
636 /* START_DEPRECATED_TPIU */
637 {
638 .chain = arm_tpiu_deprecated_command_handlers,
639 },
640 /* END_DEPRECATED_TPIU */
641 COMMAND_REGISTRATION_DONE
642 };
643
644 struct target_type hla_target = {
645 .name = "hla_target",
646
647 .init_target = adapter_init_target,
648 .deinit_target = cortex_m_deinit_target,
649 .target_create = adapter_target_create,
650 .target_jim_configure = adiv5_jim_configure,
651 .examine = cortex_m_examine,
652 .commands = hla_command_handlers,
653
654 .poll = adapter_poll,
655 .arch_state = armv7m_arch_state,
656
657 .target_request_data = hl_target_request_data,
658 .assert_reset = hl_assert_reset,
659 .deassert_reset = hl_deassert_reset,
660
661 .halt = adapter_halt,
662 .resume = adapter_resume,
663 .step = adapter_step,
664
665 .get_gdb_arch = arm_get_gdb_arch,
666 .get_gdb_reg_list = armv7m_get_gdb_reg_list,
667
668 .read_memory = adapter_read_memory,
669 .write_memory = adapter_write_memory,
670 .checksum_memory = armv7m_checksum_memory,
671 .blank_check_memory = armv7m_blank_check_memory,
672
673 .run_algorithm = armv7m_run_algorithm,
674 .start_algorithm = armv7m_start_algorithm,
675 .wait_algorithm = armv7m_wait_algorithm,
676
677 .add_breakpoint = cortex_m_add_breakpoint,
678 .remove_breakpoint = cortex_m_remove_breakpoint,
679 .add_watchpoint = cortex_m_add_watchpoint,
680 .remove_watchpoint = cortex_m_remove_watchpoint,
681 .profiling = cortex_m_profiling,
682 };

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)