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

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)