- fixed build issues with win32
[openocd.git] / src / openocd.c
1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.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
21 #define OPENOCD_VERSION "Open On-Chip Debugger " VERSION " (" PKGBLDDATE ") svn:" PKGBLDREV
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "log.h"
28 #include "types.h"
29 #include "jtag.h"
30 #include "configuration.h"
31 #include "interpreter.h"
32 #include "xsvf.h"
33 #include "target.h"
34 #include "flash.h"
35 #include "nand.h"
36 #include "pld.h"
37
38 #include "command.h"
39 #include "server.h"
40 #include "telnet_server.h"
41 #include "gdb_server.h"
42 #include "tcl_server.h"
43
44 #include <sys/time.h>
45 #include <sys/types.h>
46 #include <strings.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include <errno.h>
52
53 #ifdef _WIN32
54 #include <malloc.h>
55 #else
56 #include <alloca.h>
57 #endif
58
59 #ifdef __ECOS
60 /* Jim is provied by eCos */
61 #include <cyg/jimtcl/jim.h>
62 #else
63 #define JIM_EMBEDDED
64 #include "jim.h"
65 #endif
66
67 #include "replacements.h"
68
69 int launchTarget(struct command_context_s *cmd_ctx)
70 {
71 int retval;
72 /* Try to examine & validate jtag chain, though this may require a reset first
73 * in which case we continue setup */
74 jtag_init(cmd_ctx);
75
76 /* try to examine target at this point. If it fails, perhaps a reset will
77 * bring it up later on via a telnet/gdb session */
78 target_examine(cmd_ctx);
79
80 retval=flash_init_drivers(cmd_ctx);
81 if (retval!=ERROR_OK)
82 return retval;
83 LOG_DEBUG("flash init complete");
84
85 retval=nand_init(cmd_ctx);
86 if (retval!=ERROR_OK)
87 return retval;
88 LOG_DEBUG("NAND init complete");
89
90 retval=pld_init(cmd_ctx);
91 if (retval!=ERROR_OK)
92 return retval;
93 LOG_DEBUG("pld init complete");
94 return retval;
95 }
96
97 /* Give TELNET a way to find out what version this is */
98 int handle_version_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
99 {
100 command_print(cmd_ctx, OPENOCD_VERSION);
101
102 return ERROR_OK;
103 }
104
105 static int daemon_startup = 0;
106
107 int handle_daemon_startup_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
108 {
109 if (argc==0)
110 return ERROR_OK;
111 if (argc > 1 )
112 return ERROR_COMMAND_SYNTAX_ERROR;
113
114 daemon_startup = strcmp("reset", args[0])==0;
115
116 command_print(cmd_ctx, OPENOCD_VERSION);
117
118 return ERROR_OK;
119 }
120
121 void exit_handler(void)
122 {
123 /* close JTAG interface */
124 if (jtag && jtag->quit)
125 jtag->quit();
126 }
127
128 /* OpenOCD can't really handle failure of this command. Patches welcome! :-) */
129 int handle_init_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
130 {
131 int retval;
132 static int initialized=0;
133 if (initialized)
134 return ERROR_OK;
135
136 initialized=1;
137
138 command_set_output_handler(cmd_ctx, configuration_output_handler, NULL);
139
140 atexit(exit_handler);
141
142 if (target_init(cmd_ctx) != ERROR_OK)
143 return ERROR_FAIL;
144 LOG_DEBUG("target init complete");
145
146 if ((retval=jtag_interface_init(cmd_ctx)) != ERROR_OK)
147 {
148 /* we must be able to set up the jtag interface */
149 return retval;
150 }
151 LOG_DEBUG("jtag interface init complete");
152
153 /* Try to initialize & examine the JTAG chain at this point, but
154 * continue startup regardless
155 */
156 if (jtag_init(cmd_ctx) == ERROR_OK)
157 {
158 LOG_DEBUG("jtag init complete");
159 if (target_examine(cmd_ctx) == ERROR_OK)
160 {
161 LOG_DEBUG("jtag examine complete");
162 }
163 }
164
165 if (flash_init_drivers(cmd_ctx) != ERROR_OK)
166 return ERROR_FAIL;
167 LOG_DEBUG("flash init complete");
168
169 if (nand_init(cmd_ctx) != ERROR_OK)
170 return ERROR_FAIL;
171 LOG_DEBUG("NAND init complete");
172
173 if (pld_init(cmd_ctx) != ERROR_OK)
174 return ERROR_FAIL;
175 LOG_DEBUG("pld init complete");
176
177 /* initialize tcp server */
178 server_init();
179
180 /* initialize telnet subsystem */
181 telnet_init("Open On-Chip Debugger");
182 gdb_init();
183 tcl_init(); /* allows tcl to just connect without going thru telnet */
184
185 return ERROR_OK;
186 }
187
188 void lockBigLock();
189 void unlockBigLock();
190
191 Jim_Interp *interp;
192 command_context_t *active_cmd_ctx;
193
194 static int new_int_array_element(Jim_Interp * interp, const char *varname, int idx, u32 val)
195 {
196 char *namebuf;
197 Jim_Obj *nameObjPtr, *valObjPtr;
198 int result;
199
200 namebuf = alloc_printf("%s(%d)", varname, idx );
201
202 nameObjPtr = Jim_NewStringObj(interp, namebuf, -1);
203 valObjPtr = Jim_NewIntObj(interp, val );
204 Jim_IncrRefCount(nameObjPtr);
205 Jim_IncrRefCount(valObjPtr);
206 result = Jim_SetVariable(interp, nameObjPtr, valObjPtr);
207 Jim_DecrRefCount(interp, nameObjPtr);
208 Jim_DecrRefCount(interp, valObjPtr);
209 free(namebuf);
210 // printf( "%s = 0%08x\n", namebuf, val );
211 return result;
212 }
213
214 static int Jim_Command_mem2array(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
215 {
216 target_t *target;
217 long l;
218 u32 width;
219 u32 len;
220 u32 addr;
221 u32 count;
222 u32 v;
223 const char *varname;
224 u8 buffer[4096];
225 int i,n,e,retval;
226
227 /* argv[1] = name of array to receive the data
228 * argv[2] = desired width
229 * argv[3] = memory address
230 * argv[4] = length in bytes to read
231 */
232 if( argc != 5 ){
233 Jim_WrongNumArgs( interp, 1, argv, "varname width addr nelems" );
234 return JIM_ERR;
235 }
236 varname = Jim_GetString( argv[1], &len );
237 /* given "foo" get space for worse case "foo(%d)" .. add 20 */
238
239 e = Jim_GetLong( interp, argv[2], &l );
240 width = l;
241 if( e != JIM_OK ){
242 return e;
243 }
244
245 e = Jim_GetLong( interp, argv[3], &l );
246 addr = l;
247 if( e != JIM_OK ){
248 return e;
249 }
250 e = Jim_GetLong( interp, argv[4], &l );
251 len = l;
252 if( e != JIM_OK ){
253 return e;
254 }
255 switch(width){
256 case 8:
257 width = 1;
258 break;
259 case 16:
260 width = 2;
261 break;
262 case 32:
263 width = 4;
264 break;
265 default:
266 Jim_SetResult(interp,
267 Jim_NewEmptyStringObj(interp));
268 Jim_AppendStrings( interp, Jim_GetResult(interp),
269 "Invalid width param, must be 8/16/32", NULL );
270 return JIM_ERR;
271 }
272 if( len == 0 ){
273 Jim_SetResult(interp,
274 Jim_NewEmptyStringObj(interp));
275 Jim_AppendStrings( interp, Jim_GetResult(interp),
276 "mem2array: zero width read?", NULL );
277 return JIM_ERR;
278 }
279 if( (addr + (len * width)) < addr ){
280 Jim_SetResult(interp,
281 Jim_NewEmptyStringObj(interp));
282 Jim_AppendStrings( interp, Jim_GetResult(interp),
283 "mem2array: addr + len - wraps to zero?", NULL );
284 return JIM_ERR;
285 }
286 /* absurd transfer size? */
287 if( len > 65536 ){
288 Jim_SetResult(interp,
289 Jim_NewEmptyStringObj(interp));
290 Jim_AppendStrings( interp, Jim_GetResult(interp),
291 "mem2array: absurd > 64K item request", NULL );
292 return JIM_ERR;
293 }
294
295 if( (width == 1) ||
296 ((width == 2) && ((addr & 1) == 0)) ||
297 ((width == 4) && ((addr & 3) == 0)) ){
298 /* all is well */
299 } else {
300 char buf[100];
301 Jim_SetResult(interp,
302 Jim_NewEmptyStringObj(interp));
303 sprintf( buf,
304 "mem2array address: 0x%08x is not aligned for %d byte reads",
305 addr, width );
306
307 Jim_AppendStrings( interp, Jim_GetResult(interp),
308 buf , NULL );
309 return JIM_ERR;
310 }
311
312 target = get_current_target( active_cmd_ctx );
313
314 /* Transfer loop */
315
316 /* index counter */
317 n = 0;
318 /* assume ok */
319 e = JIM_OK;
320 while( len ){
321
322 /* Slurp... in buffer size chunks */
323
324 count = len; /* in objects.. */
325 if( count > (sizeof(buffer)/width)){
326 count = (sizeof(buffer)/width);
327 }
328
329 retval = target->type->read_memory( target,
330 addr,
331 width,
332 count,
333 buffer );
334
335 if( retval != ERROR_OK ){
336 /* BOO !*/
337 LOG_ERROR("mem2array: Read @ 0x%08x, w=%d, cnt=%d, failed",
338 addr, width, count );
339 Jim_SetResult(interp,
340 Jim_NewEmptyStringObj(interp));
341 Jim_AppendStrings( interp, Jim_GetResult(interp),
342 "mem2array: cannot read memory", NULL );
343 e = JIM_ERR;
344 len = 0;
345 } else {
346 v = 0; /* shut up gcc */
347 for( i = 0 ; i < count ; i++, n++ ){
348 switch(width){
349 case 4:
350 v = target_buffer_get_u32( target, &buffer[i*width] );
351 break;
352 case 2:
353 v = target_buffer_get_u16( target, &buffer[i*width] );
354 break;
355 case 1:
356 v = buffer[i] & 0x0ff;
357 break;
358 }
359 new_int_array_element( interp, varname, n, v );
360 }
361 len -= count;
362 }
363 }
364 Jim_SetResult(interp,
365 Jim_NewEmptyStringObj(interp));
366
367 return JIM_OK;
368 }
369
370 static void tcl_output(void *privData, const char *file, int line, const char *function, const char *string)
371 {
372 Jim_Obj *tclOutput=(Jim_Obj *)privData;
373
374 Jim_AppendString(interp, tclOutput, string, strlen(string));
375 }
376
377 /* try to execute as Jim command, otherwise fall back to standard command.
378
379 Note that even if the Jim command caused an error, then we succeeded
380 to execute it, hence this fn pretty much always returns ERROR_OK.
381
382 */
383 int jim_command(command_context_t *context, char *line)
384 {
385 int retval=ERROR_OK;
386 /* FIX!!!! in reality there is only one cmd_ctx handler, but consider
387 what might happen here if there are multiple handlers w/reentrant callback
388 fn's... shudder! */
389 active_cmd_ctx=context;
390 int retcode=Jim_Eval(interp, line);
391
392 const char *result;
393 int reslen;
394 result = Jim_GetString(Jim_GetResult(interp), &reslen);
395 if (retcode == JIM_ERR) {
396 int len, i;
397
398 LOG_USER_N("Runtime error, file \"%s\", line %d:" JIM_NL,
399 interp->errorFileName, interp->errorLine);
400 LOG_USER_N(" %s" JIM_NL,
401 Jim_GetString(interp->result, NULL));
402 Jim_ListLength(interp, interp->stackTrace, &len);
403 for (i = 0; i < len; i+= 3) {
404 Jim_Obj *objPtr;
405 const char *proc, *file, *line;
406
407 Jim_ListIndex(interp, interp->stackTrace, i, &objPtr, JIM_NONE);
408 proc = Jim_GetString(objPtr, NULL);
409 Jim_ListIndex(interp, interp->stackTrace, i+1, &objPtr,
410 JIM_NONE);
411 file = Jim_GetString(objPtr, NULL);
412 Jim_ListIndex(interp, interp->stackTrace, i+2, &objPtr,
413 JIM_NONE);
414 line = Jim_GetString(objPtr, NULL);
415 LOG_USER_N("In procedure '%s' called at file \"%s\", line %s" JIM_NL,
416 proc, file, line);
417 }
418 } else if (retcode == JIM_EXIT) {
419 // ignore.
420 //exit(Jim_GetExitCode(interp));
421 } else {
422 if (reslen) {
423 int i;
424 char buff[256+1];
425 for (i=0; i<reslen; i+=256)
426 {
427 int chunk;
428 chunk=reslen-i;
429 if (chunk>256)
430 chunk=256;
431 strncpy(buff, result, chunk);
432 buff[chunk]=0;
433 LOG_USER_N("%s", buff);
434 }
435 LOG_USER_N("%s", "\n");
436 }
437 }
438 return retval;
439 }
440
441 int startLoop=0;
442
443 static int Jim_Command_openocd_ignore(Jim_Interp *interp, int argc, Jim_Obj *const *argv, int ignore)
444 {
445 int retval;
446 char *cmd = (char*)Jim_GetString(argv[1], NULL);
447
448 lockBigLock();
449
450 Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
451
452 if (startLoop)
453 {
454 // We don't know whether or not the telnet/gdb server is running...
455 target_call_timer_callbacks_now();
456 }
457
458 log_add_callback(tcl_output, tclOutput);
459 retval=command_run_line_internal(active_cmd_ctx, cmd);
460
461 if (startLoop)
462 {
463 target_call_timer_callbacks_now();
464 }
465 log_remove_callback(tcl_output, tclOutput);
466
467 Jim_SetResult(interp, tclOutput);
468 unlockBigLock();
469
470 return (ignore||(retval==ERROR_OK))?JIM_OK:JIM_ERR;
471 }
472
473 static int Jim_Command_openocd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
474 {
475 return Jim_Command_openocd_ignore(interp, argc, argv, 1);
476 }
477
478 static int Jim_Command_openocd_throw(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
479 {
480 return Jim_Command_openocd_ignore(interp, argc, argv, 0);
481 }
482
483 /* find full path to file */
484 static int Jim_Command_find(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
485 {
486 if (argc!=2)
487 return JIM_ERR;
488 char *file = (char*)Jim_GetString(argv[1], NULL);
489 char *full_path=find_file(file);
490 if (full_path==NULL)
491 return JIM_ERR;
492 Jim_Obj *result = Jim_NewStringObj(interp, full_path, strlen(full_path));
493 free(full_path);
494
495 Jim_SetResult(interp, result);
496 return JIM_OK;
497 }
498
499 static int Jim_Command_echo(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
500 {
501 if (argc!=2)
502 return JIM_ERR;
503 char *str = (char*)Jim_GetString(argv[1], NULL);
504 LOG_USER("%s", str);
505 return JIM_OK;
506 }
507
508 static size_t openocd_jim_fwrite(const void *_ptr, size_t size, size_t n, void *cookie)
509 {
510 size_t nbytes;
511 const char *ptr;
512
513 /* make it a char easier to read code */
514 ptr = _ptr;
515
516 nbytes = size * n;
517 if( nbytes == 0 ){
518 return 0;
519 }
520
521 if( !active_cmd_ctx ){
522 /* FIXME: Where should this go? */
523 return n;
524 }
525
526 /* do we have to chunk it? */
527 if( ptr[ nbytes ] == 0 ){
528 /* no it is a C style string */
529 command_output_text( active_cmd_ctx, ptr );
530 return strlen(ptr);
531 }
532 /* GRR we must chunk - not null terminated */
533 while( nbytes ){
534 char chunk[128+1];
535 int x;
536
537 x = nbytes;
538 if( x > 128 ){
539 x = 128;
540 }
541 /* copy it */
542 memcpy( chunk, ptr, x );
543 /* terminate it */
544 chunk[n] = 0;
545 /* output it */
546 command_output_text( active_cmd_ctx, chunk );
547 ptr += x;
548 nbytes -= x;
549 }
550
551 return n;
552 }
553
554 static size_t openocd_jim_fread(void *ptr, size_t size, size_t n, void *cookie )
555 {
556 /* TCL wants to read... tell him no */
557 return 0;
558 }
559
560 static int openocd_jim_vfprintf(void *cookie, const char *fmt, va_list ap)
561 {
562 char *cp;
563 int n;
564
565 n = -1;
566 if( active_cmd_ctx ){
567 cp = alloc_vprintf( fmt, ap );
568 if( cp ){
569 command_output_text( active_cmd_ctx, cp );
570 n = strlen(cp);
571 free(cp);
572 }
573 }
574 return n;
575 }
576
577 static int openocd_jim_fflush(void *cookie)
578 {
579 /* nothing to flush */
580 return 0;
581 }
582
583 static char* openocd_jim_fgets(char *s, int size, void *cookie)
584 {
585 /* not supported */
586 errno = ENOTSUP;
587 return NULL;
588 }
589
590 void initJim(void)
591 {
592 Jim_CreateCommand(interp, "openocd", Jim_Command_openocd, NULL, NULL);
593 Jim_CreateCommand(interp, "openocd_throw", Jim_Command_openocd_throw, NULL, NULL);
594 Jim_CreateCommand(interp, "find", Jim_Command_find, NULL, NULL);
595 Jim_CreateCommand(interp, "echo", Jim_Command_echo, NULL, NULL);
596 Jim_CreateCommand(interp, "mem2array", Jim_Command_mem2array, NULL, NULL );
597
598 /* Set Jim's STDIO */
599 interp->cookie_stdin = NULL;
600 interp->cookie_stdout = NULL;
601 interp->cookie_stderr = NULL;
602 interp->cb_fwrite = openocd_jim_fwrite;
603 interp->cb_fread = openocd_jim_fread ;
604 interp->cb_vfprintf = openocd_jim_vfprintf;
605 interp->cb_fflush = openocd_jim_fflush;
606 interp->cb_fgets = openocd_jim_fgets;
607 }
608
609 /* after command line parsing */
610 void initJim2(void)
611 {
612 Jim_Eval(interp, "source [find tcl/commands.tcl]");
613 }
614
615 command_context_t *setup_command_handler()
616 {
617 command_context_t *cmd_ctx;
618
619 cmd_ctx = command_init();
620
621 register_command(cmd_ctx, NULL, "version", handle_version_command,
622 COMMAND_EXEC, "show OpenOCD version");
623 register_command(cmd_ctx, NULL, "daemon_startup", handle_daemon_startup_command, COMMAND_CONFIG,
624 "deprecated - use \"init\" and \"reset\" at end of startup script instead");
625
626 /* register subsystem commands */
627 server_register_commands(cmd_ctx);
628 telnet_register_commands(cmd_ctx);
629 gdb_register_commands(cmd_ctx);
630 tcl_register_commands(cmd_ctx); /* tcl server commands */
631 log_register_commands(cmd_ctx);
632 jtag_register_commands(cmd_ctx);
633 interpreter_register_commands(cmd_ctx);
634 xsvf_register_commands(cmd_ctx);
635 target_register_commands(cmd_ctx);
636 flash_register_commands(cmd_ctx);
637 nand_register_commands(cmd_ctx);
638 pld_register_commands(cmd_ctx);
639
640 if (log_init(cmd_ctx) != ERROR_OK)
641 {
642 exit(-1);
643 }
644 LOG_DEBUG("log init complete");
645
646 LOG_OUTPUT( OPENOCD_VERSION "\n" );
647
648
649 /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
650 /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
651 /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
652 /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
653 /* DANGER!!! make sure that the line below does not appear in a patch, do not remove */
654 LOG_OUTPUT( "$URL$\n");
655 /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
656 /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
657 /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
658 /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
659 /* DANGER!!! make sure that the line above does not appear in a patch, do not remove */
660
661 register_command(cmd_ctx, NULL, "init", handle_init_command,
662 COMMAND_ANY, "initializes target and servers - nop on subsequent invocations");
663
664 return cmd_ctx;
665 }
666
667 /*
668 normally this is the main() function entry, but if OpenOCD is linked
669 into application, then this fn will not be invoked, but rather that
670 application will have it's own implementation of main().
671 */
672 int openocd_main(int argc, char *argv[])
673 {
674 #ifdef JIM_EMBEDDED
675 Jim_InitEmbedded();
676 /* Create an interpreter */
677 interp = Jim_CreateInterp();
678 /* Add all the Jim core commands */
679 Jim_RegisterCoreCommands(interp);
680 #endif
681
682 initJim();
683
684 /* initialize commandline interface */
685 command_context_t *cmd_ctx;
686 cmd_ctx=setup_command_handler();
687
688 command_context_t *cfg_cmd_ctx;
689 cfg_cmd_ctx = copy_command_context(cmd_ctx);
690 cfg_cmd_ctx->mode = COMMAND_CONFIG;
691 command_set_output_handler(cfg_cmd_ctx, configuration_output_handler, NULL);
692
693 if (parse_cmdline_args(cfg_cmd_ctx, argc, argv) != ERROR_OK)
694 return EXIT_FAILURE;
695
696 initJim2();
697
698 if (parse_config_file(cfg_cmd_ctx) != ERROR_OK)
699 return EXIT_FAILURE;
700
701 command_done(cfg_cmd_ctx);
702
703 if (command_run_line(cmd_ctx, "init")!=ERROR_OK)
704 return EXIT_FAILURE;
705
706 if (daemon_startup)
707 command_run_line(cmd_ctx, "reset");
708
709 startLoop=1;
710
711 /* handle network connections */
712 server_loop(cmd_ctx);
713
714 /* shut server down */
715 server_quit();
716
717 unregister_all_commands(cmd_ctx);
718
719 /* free commandline interface */
720 command_done(cmd_ctx);
721
722 return EXIT_SUCCESS;
723 }

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)