smp : infra for smp minimum support
authorMichel Jaouen <michel.jaouen@stericsson.com>
Tue, 19 Apr 2011 06:43:33 +0000 (08:43 +0200)
committerØyvind Harboe <oyvind.harboe@zylin.com>
Thu, 28 Apr 2011 10:22:10 +0000 (12:22 +0200)
src/server/gdb_server.c
src/server/gdb_server.h
src/target/Makefile.am
src/target/breakpoints.c
src/target/smp.c [new file with mode: 0644]
src/target/smp.h [new file with mode: 0644]
src/target/target.c
src/target/target.h

index 0b8085896615fa61aa5338f456d8987ab76933c8..aeb4e2e883b6a061b773e7608caf0ef8a04a7448 100644 (file)
@@ -11,6 +11,9 @@
  *   Copyright (C) 2011 by Broadcom Corporation                            *
  *   Evan Hunter - ehunter@broadcom.com                                    *
  *                                                                         *
+ *   Copyright (C) ST-Ericsson SA 2011                                     *
+ *   michel.jaouen@stericsson.com : smp minimum support                    *
+ *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   it under the terms of the GNU General Public License as published by  *
  *   the Free Software Foundation; either version 2 of the License, or     *
@@ -39,6 +42,7 @@
 #include <target/image.h>
 #include <jtag/jtag.h>
 #include "rtos/rtos.h"
+#include "target/smp.h"
 
 
 /**
@@ -62,7 +66,7 @@ struct gdb_connection
        int closed;
        int busy;
        int noack_mode;
-       bool sync;      /* set flag to true if you want the next stepi to return immediately.
+       bool sync;      /* set flag to true if you want the next stepi to return immediately.
                       allowing GDB to pick up a fresh set of register values from the target
                       without modifying the target state. */
        /* We delay reporting memory write errors until next step/continue or memory
@@ -2360,6 +2364,24 @@ static int gdb_input_inner(struct connection *connection)
                                                        "ocd_gdb_restart %s",
                                                        target_name(target));
                                        break;
+
+                               case 'j':
+                                   /*  packet supported only by smp target i.e cortex_a.c*/
+                                       /* handle smp packet replying coreid played to gbd */
+                                       gdb_read_smp_packet(
+                                                       connection, target,
+                                                       packet, packet_size);
+                                       break;
+
+                               case 'J':
+                                       /*  packet supported only by smp target i.e cortex_a.c */
+                                       /*  handle smp packet setting coreid to be played at next
+                                        *  resume to gdb */
+                                       gdb_write_smp_packet(
+                                                       connection, target,
+                                                       packet, packet_size);
+                                       break;
+
                                default:
                                        /* ignore unknown packets */
                                        LOG_DEBUG("ignoring 0x%2.2x packet", packet[0]);
@@ -2411,21 +2433,43 @@ static int gdb_input(struct connection *connection)
 
 static int gdb_target_start(struct target *target, const char *port)
 {
-       struct gdb_service *gdb_service = malloc(sizeof(struct gdb_service));
+
+       struct gdb_service *gdb_service;
+       int ret;
+       gdb_service = malloc(sizeof(struct gdb_service));
+
        if (NULL == gdb_service)
                return -ENOMEM;
 
        gdb_service->target = target;
+       gdb_service->core[0] = -1;
+       gdb_service->core[1] = -1;
+       target->gdb_service = gdb_service;
 
-       return add_service("gdb",
+       ret = add_service("gdb",
                        port, 1, &gdb_new_connection, &gdb_input,
                        &gdb_connection_closed, gdb_service);
+       /* initialialize all targets gdb service with the same pointer */
+       {
+               struct target_list *head;
+               struct target *curr;
+               head = target->head;
+               while(head != (struct target_list*)NULL)
+               {
+                       curr = head->target;
+                       if (curr != target) curr->gdb_service = gdb_service;
+                       head = head->next;      
+               }
+       }
+       return ret;
 }
 
 static int gdb_target_add_one(struct target *target)
 {
+       /*  one gdb instance per smp list */
+       if ((target->smp) && (target->gdb_service)) return ERROR_OK;
        int retval = gdb_target_start(target, gdb_port_next);
-       if (retval == ERROR_OK)
+       if (retval == ERROR_OK) 
        {
                long portnumber;
                /* If we can parse the port number
index cb3962ff971cca4b3a437ddbde47e0868efb3727..e393fb726a375a85311b7bdb68f0d50d45a029ec 100644 (file)
 #define GDB_SERVER_H
 
 struct image;
+struct reg;
 #include <target/target.h>
 
 #define GDB_BUFFER_SIZE        16384
 
-struct gdb_service
-{
-       struct target *target;
-};
-
 int gdb_target_add_all(struct target *target);
 int gdb_register_commands(struct command_context *command_context);
 
index 5907e83f49c4a9aaf6c70e725c89d50aaab2d400..1a2fbd6c68b3223992a31ef8c3044337cd467447 100644 (file)
@@ -42,7 +42,8 @@ TARGET_CORE_SRC = \
        breakpoints.c \
        target.c \
        target_request.c \
-       testee.c
+       testee.c \
+       smp.c
 
 ARMV4_5_SRC = \
        armv4_5.c \
index 917dfc7897cf0aa4c6fc4873f7c4054572a7b211..80f98dc1101d72df063558e898b14bced1cb68f7 100644 (file)
@@ -2,6 +2,9 @@
  *   Copyright (C) 2005 by Dominic Rath                                    *
  *   Dominic.Rath@gmx.de                                                   *
  *                                                                         *
+ *   Copyright (C) ST-Ericsson SA 2011                                     *
+ *   michel.jaouen@stericsson.com : smp minimum support                    *
+ *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   it under the terms of the GNU General Public License as published by  *
  *   the Free Software Foundation; either version 2 of the License, or     *
@@ -42,10 +45,11 @@ static char *watchpoint_rw_strings[] =
 // monotonic counter/id-number for breakpoints and watch points
 static int bpwp_unique_id;
 
-int breakpoint_add(struct target *target, uint32_t address, uint32_t length, enum breakpoint_type type)
+int breakpoint_add_internal(struct target *target, uint32_t address, uint32_t length, enum breakpoint_type type)
 {
        struct breakpoint *breakpoint = target->breakpoints;
        struct breakpoint **breakpoint_p = &target->breakpoints;
+       char *reason;
        int retval;
        int n;
 
@@ -76,9 +80,19 @@ int breakpoint_add(struct target *target, uint32_t address, uint32_t length, enu
        (*breakpoint_p)->unique_id = bpwp_unique_id++;
 
        retval = target_add_breakpoint(target, *breakpoint_p);
-       if (retval != ERROR_OK)
-       {
-               LOG_ERROR("could not add breakpoint");
+       switch (retval) {
+       case ERROR_OK:
+               break;
+       case ERROR_TARGET_RESOURCE_NOT_AVAILABLE:
+               reason = "resource not available";
+               goto fail;
+       case ERROR_TARGET_NOT_HALTED:
+               reason = "target running";
+               goto fail;
+       default:
+               reason = "unknown reason";
+fail:
+               LOG_ERROR("can't add breakpoint: %s", reason);
                free((*breakpoint_p)->orig_instr);
                free(*breakpoint_p);
                *breakpoint_p = NULL;
@@ -93,6 +107,29 @@ int breakpoint_add(struct target *target, uint32_t address, uint32_t length, enu
        return ERROR_OK;
 }
 
+int breakpoint_add(struct target *target, uint32_t address, uint32_t length, enum breakpoint_type type)
+{
+
+int retval = ERROR_OK;
+    if (target->smp)
+       {
+               struct target_list *head;
+               struct target *curr;
+               head = target->head;
+               while(head != (struct target_list*)NULL)
+               {
+                       curr = head->target;
+                       retval = breakpoint_add_internal(curr, address,length, type);
+                       if (retval != ERROR_OK) return retval;
+                       head = head->next;      
+               }
+               return retval;
+       }
+       else
+       return(breakpoint_add_internal(target, address, length, type));
+
+}
+
 /* free up a breakpoint */
 static void breakpoint_free(struct target *target, struct breakpoint *breakpoint_to_remove)
 {
@@ -119,7 +156,7 @@ static void breakpoint_free(struct target *target, struct breakpoint *breakpoint
        free(breakpoint);
 }
 
-void breakpoint_remove(struct target *target, uint32_t address)
+void breakpoint_remove_internal(struct target *target, uint32_t address)
 {
        struct breakpoint *breakpoint = target->breakpoints;
        struct breakpoint **breakpoint_p = &target->breakpoints;
@@ -141,8 +178,24 @@ void breakpoint_remove(struct target *target, uint32_t address)
                LOG_ERROR("no breakpoint at address 0x%8.8" PRIx32 " found", address);
        }
 }
+void breakpoint_remove(struct target *target, uint32_t address)
+{
+    if ((target->smp))
+       {
+               struct target_list *head;
+               struct target *curr;
+               head = target->head;
+               while(head != (struct target_list*)NULL)
+               {
+                       curr = head->target;
+                       breakpoint_remove_internal(curr, address);
+                       head = head->next;      
+               }
+       }
+       else  breakpoint_remove_internal(target, address);
+}
 
-void breakpoint_clear_target(struct target *target)
+void breakpoint_clear_target_internal(struct target *target)
 {
        struct breakpoint *breakpoint;
 
@@ -154,6 +207,25 @@ void breakpoint_clear_target(struct target *target)
        }
 }
 
+void breakpoint_clear_target(struct target *target)
+{
+    if (target->smp)
+       {
+               struct target_list *head;
+               struct target *curr;
+               head = target->head;
+               while(head != (struct target_list*)NULL)
+               {
+                       curr = head->target;
+                   breakpoint_clear_target_internal(curr);
+                       head = head->next;      
+               }
+     }
+        else breakpoint_clear_target_internal(target);
+       
+}
+
+
 struct breakpoint* breakpoint_find(struct target *target, uint32_t address)
 {
        struct breakpoint *breakpoint = target->breakpoints;
@@ -174,6 +246,7 @@ int watchpoint_add(struct target *target, uint32_t address, uint32_t length,
        struct watchpoint *watchpoint = target->watchpoints;
        struct watchpoint **watchpoint_p = &target->watchpoints;
        int retval;
+       char *reason;
 
        while (watchpoint)
        {
@@ -204,11 +277,21 @@ int watchpoint_add(struct target *target, uint32_t address, uint32_t length,
        (*watchpoint_p)->unique_id = bpwp_unique_id++;
 
        retval = target_add_watchpoint(target, *watchpoint_p);
-       if (retval != ERROR_OK)
-       {
-               LOG_ERROR("can't add %s watchpoint at 0x%8.8" PRIx32,
+       switch (retval) {
+       case ERROR_OK:
+               break;
+       case ERROR_TARGET_RESOURCE_NOT_AVAILABLE:
+               reason = "resource not available";
+               goto bye;
+       case ERROR_TARGET_NOT_HALTED:
+               reason = "target running";
+               goto bye;
+       default:
+               reason = "unrecognized error";
+bye:
+               LOG_ERROR("can't add %s watchpoint at 0x%8.8" PRIx32 ", %s",
                         watchpoint_rw_strings[(*watchpoint_p)->rw],
-                        address);
+                        address, reason);
                free (*watchpoint_p);
                *watchpoint_p = NULL;
                return retval;
diff --git a/src/target/smp.c b/src/target/smp.c
new file mode 100644 (file)
index 0000000..aabfa5b
--- /dev/null
@@ -0,0 +1,116 @@
+/***************************************************************************
+ *                                                                         *
+ * Copyright (C) ST-Ericsson SA 2011                                       *
+ * Author: Michel Jaouen <michel.jaouen@stericsson.com> for ST-Ericsson.   *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "server/server.h"
+#include <helper/types.h>
+
+#include "target/target.h"
+
+#include "server/gdb_server.h"
+#include "smp.h"
+
+
+/*  implementation of new packet in gdb interface for smp feature          */
+/*                                                                         */
+/*   j : smp  status request                                               */
+/*   J : smp  set request                                                  */
+/*                                                                         */
+/*   jc :read core id displayed by gdb connection                          */
+/*   reply XXXXXXXX core id is int32_t , 8 hex digits                      */
+/*                                                                         */
+/*   Reply ENN error not supported (target not smp)                        */
+/*                                                                         */
+/*   JcXX  set core id displayed at next gdb continue                      */
+/*   maximum 8 bytes described core id int32_t (8 hex digits)              */
+/*  (core id -1 , reserved for returning to normal continue mode) */
+/*  Reply ENN error not supported(target not smp,core id out of range)     */
+/*  Reply OK : for success                                                 */
+/*                                                                         */
+/*  handling of this packet within gdb can be done by the creation         */
+/*  internal variable by mean of function allocate_computed_value          */
+/*  set $_core 1 => Jc01 packet is sent                                    */
+/*  print $_core => jc packet is sent and result is affected in $          */
+/*  Another way to test this packet is the usage of maintenance packet     */
+/*  maint packet Jc01                                                      */
+/*  maint packet jc                                                        */
+                                                
+static const char DIGITS[16] = "0123456789abcdef";
+
+
+/* packet j :smp status request */
+int gdb_read_smp_packet(struct connection *connection,
+               struct target *target, char *packet, int packet_size)
+{
+       uint32_t len = sizeof(int32_t);
+       uint8_t *buffer;
+       char *hex_buffer;
+       int retval = ERROR_OK;
+       if (target->smp)
+       {
+               if (strstr(packet, "jc"))
+               {
+                       hex_buffer = malloc(len * 2 + 1);
+                       buffer = (uint8_t *)&target->gdb_service->core[0];
+                       uint32_t i;
+                       for (i = 0; i < 4; i++)
+                       {
+                               uint8_t t = buffer[i];
+                               hex_buffer[2 * i] = DIGITS[(t >> 4) & 0xf];
+                               hex_buffer[2 * i + 1] = DIGITS[t & 0xf];
+                       }
+
+                       gdb_put_packet(connection, hex_buffer, len * 2);
+
+                       free(hex_buffer);
+               }
+       }
+       else
+               retval = gdb_put_packet(connection,"E01",3);
+       return retval;
+}
+
+/* J :  smp set request */
+int gdb_write_smp_packet(struct connection *connection,
+               struct target *target, char *packet, int packet_size)
+{
+       char *separator;
+       int coreid = 0;
+    int retval = ERROR_OK;
+
+       /* skip command character */
+       if (target->smp)
+       {
+               if (strstr(packet, "Jc"))
+               {
+                       packet+=2;
+                       coreid = strtoul(packet, &separator, 16);
+                       target->gdb_service->core[1] = coreid;
+                       gdb_put_packet(connection, "OK", 2);
+               }
+       }
+       else
+       {
+               retval = gdb_put_packet(connection,"E01",3);
+       }
+       return ERROR_OK;
+}
diff --git a/src/target/smp.h b/src/target/smp.h
new file mode 100644 (file)
index 0000000..f85c9a4
--- /dev/null
@@ -0,0 +1,25 @@
+/***************************************************************************
+ *                                                                         *
+ * Copyright (C) ST-Ericsson SA 2011                                       *
+ * Author: Michel Jaouen <michel.jaouen@stericsson.com> for ST-Ericsson.   *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include "server/server.h"
+int gdb_read_smp_packet(struct connection *connection,
+               struct target *target, char *packet, int packet_size);
+int gdb_write_smp_packet(struct connection *connection,
+               struct target *target, char *packet, int packet_size);
+
index abe1b43ae03e01f5d79a464f5281d2cf93b23545..a2e3ccfb06bf03d52f50fac497eb6f79d27f2e99 100644 (file)
@@ -17,6 +17,9 @@
  *   Copyright (C) 2011 by Broadcom Corporation                            *
  *   Evan Hunter - ehunter@broadcom.com                                    *
  *                                                                         *
+ *   Copyright (C) ST-Ericsson SA 2011                                     *
+ *   michel.jaouen@stericsson.com : smp minimum support                    *
+ *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   it under the terms of the GNU General Public License as published by  *
  *   the Free Software Foundation; either version 2 of the License, or     *
@@ -729,7 +732,7 @@ int target_bulk_write_memory(struct target *target,
 int target_add_breakpoint(struct target *target,
                struct breakpoint *breakpoint)
 {
-       if (target->state != TARGET_HALTED) {
+       if ((target->state != TARGET_HALTED)&&(breakpoint->type!=BKPT_HARD)) {
                LOG_WARNING("target %s is not halted", target->cmd_name);
                return ERROR_TARGET_NOT_HALTED;
        }
@@ -3931,6 +3934,7 @@ static int target_configure(Jim_GetOptInfo *goi, struct target *target)
                        /* loop for more e*/
                        break;
 
+
                case TCFG_ENDIAN:
                        if (goi->isconfigure) {
                                e = Jim_GetOpt_Nvp(goi, nvp_target_endian, &n);
@@ -3981,7 +3985,7 @@ static int target_configure(Jim_GetOptInfo *goi, struct target *target)
                                if (e != JIM_OK) {
                                        return e;
                                }
-                               target->coreid = (int)w;
+                               target->coreid = (int32_t)w;
                        } else {
                                if (goi->argc != 0) {
                                        goto no_params;
@@ -4893,6 +4897,61 @@ static int jim_target_names(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
        return JIM_OK;
 }
 
+static int jim_target_smp(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+       int i;
+       const char *targetname;
+       int retval,len;
+       struct target *target;
+       struct target_list *head, *curr, *new;
+    curr = (struct target_list*) NULL;
+       head = (struct target_list*) NULL;
+       new = (struct target_list*) NULL;
+
+       retval = 0;
+       LOG_DEBUG("%d",argc);
+       /* argv[1] = target to associate in smp
+        * argv[2] = target to assoicate in smp 
+        * argv[3] ...
+        */
+
+       for(i=1;i<argc;i++)
+       {
+
+               targetname = Jim_GetString(argv[i], &len);
+               target = get_target(targetname);
+               LOG_DEBUG("%s ",targetname);
+               if (target)
+               {
+                       new=malloc(sizeof(struct target_list));
+                       new->target = target;
+                       new->next = (struct target_list*)NULL;
+                       if (head == (struct target_list*)NULL)
+                       {
+                               head = new;
+                               curr = head;
+                       }
+                       else
+                       {
+                               curr->next = new;
+                               curr = new;
+                       }
+               }
+       }
+    /*  now parse the list of cpu and put the target in smp mode*/
+       curr=head;
+
+    while(curr!=(struct target_list *)NULL)
+       {
+    target=curr->target;
+       target->smp = 1;
+       target->head = head;
+       curr=curr->next;
+       }
+       return retval; 
+}
+
+
 static int jim_target_create(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
        Jim_GetOptInfo goi;
@@ -5008,6 +5067,14 @@ static const struct command_registration target_subcommand_handlers[] = {
                .help = "Returns the number of targets as an integer "
                        "(DEPRECATED)",
        },
+       {
+               .name = "smp",
+               .mode = COMMAND_ANY,
+               .jim_handler = jim_target_smp,
+               .usage = "targetname1 targetname2 ...",
+               .help = "gather several target in a smp list"
+       },
+
        COMMAND_REGISTRATION_DONE
 };
 
index 5b67bf345daa2e3e5e28cee98599de8003ba87cb..74f74dee1504f97756df3b2f1cbdb1d53623ddf4 100644 (file)
@@ -11,6 +11,9 @@
  *   Copyright (C) 2011 by Broadcom Corporation                            *
  *   Evan Hunter - ehunter@broadcom.com                                    *
  *                                                                         *
+ *   Copyright (C) ST-Ericsson SA 2011                                     *
+ *   michel.jaouen@stericsson.com : smp minimum support                    *
+ *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
  *   it under the terms of the GNU General Public License as published by  *
  *   the Free Software Foundation; either version 2 of the License, or     *
@@ -38,7 +41,7 @@ struct breakpoint;
 struct watchpoint;
 struct mem_param;
 struct reg_param;
-
+struct target_list;
 
 /*
  * TARGET_UNKNOWN = 0: we don't know anything about the target yet
@@ -102,6 +105,17 @@ struct working_area
        struct working_area **user;
        struct working_area *next;
 };
+struct gdb_service
+{
+       struct target *target;
+       /*  field for smp display  */
+       /*  element 0 coreid currently displayed ( 1 till n) */
+    /*  element 1 coreid to be displayed at next resume 1 till n 0 means resume
+        *  all cores
+         core displayed  */
+       int32_t core[2];
+};
 
 // target_type.h contains the full definitionof struct targe_type
 struct target
@@ -110,7 +124,7 @@ struct target
        const char *cmd_name;                           /* tcl Name of target */
        int target_number;                                      /* DO NOT USE!  field to be removed in 2010 */
        struct jtag_tap *tap;                                   /* where on the jtag chain is this */
-       int coreid;                                                     /* which device on the TAP? */
+       int32_t coreid;                                                 /* which device on the TAP? */
        const char *variant;                            /* what variant of this chip is it? */
 
        /**
@@ -166,6 +180,20 @@ struct target
        struct rtos *rtos;                                      /* Instance of Real Time Operating System support */
        bool rtos_auto_detect;                          /* A flag that indicates that the RTOS has been specified as "auto" 
                                             * and must be detected when symbols are offered */
+
+       int smp;                                                                /*  add some target attributes for smp support */
+       struct target_list *head;
+       /*  the gdb service is there in case of smp , we have only one gdb server
+        *  for all smp target
+        *  the target attached to the gdb is changing dynamically by changing
+        *  gdb_service->target pointer */
+       struct gdb_service *gdb_service;
+};
+
+
+struct target_list {
+       struct target *target;
+       struct target_list *next;
 };
 
 /** Returns the instance-specific name of the specified target. */

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)