1 /***************************************************************************
2 * Copyright (C) 2006 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
5 * Copyright (C) 2008 by Hongtao Zheng *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
20 ***************************************************************************/
28 #include "arm_disassembler.h"
29 #include "arm_simulator.h"
30 #include <helper/binarybuffer.h>
32 #include <helper/log.h>
34 static uint32_t arm_shift(uint8_t shift
, uint32_t Rm
,
35 uint32_t shift_amount
, uint8_t *carry
)
37 uint32_t return_value
= 0;
40 if (shift
== 0x0) { /* LSL */
41 if ((shift_amount
> 0) && (shift_amount
<= 32)) {
42 return_value
= Rm
<< shift_amount
;
43 *carry
= Rm
>> (32 - shift_amount
);
44 } else if (shift_amount
> 32) {
47 } else /* (shift_amount == 0) */
49 } else if (shift
== 0x1) { /* LSR */
50 if ((shift_amount
> 0) && (shift_amount
<= 32)) {
51 return_value
= Rm
>> shift_amount
;
52 *carry
= (Rm
>> (shift_amount
- 1)) & 1;
53 } else if (shift_amount
> 32) {
56 } else /* (shift_amount == 0) */
58 } else if (shift
== 0x2) { /* ASR */
59 if ((shift_amount
> 0) && (shift_amount
<= 32)) {
60 /* C right shifts of unsigned values are guaranteed to
61 * be logical (shift in zeroes); simulate an arithmetic
62 * shift (shift in signed-bit) by adding the sign bit
65 return_value
= Rm
>> shift_amount
;
67 return_value
|= 0xffffffff << (32 - shift_amount
);
68 } else if (shift_amount
> 32) {
69 if (Rm
& 0x80000000) {
70 return_value
= 0xffffffff;
76 } else /* (shift_amount == 0) */
78 } else if (shift
== 0x3) { /* ROR */
79 if (shift_amount
== 0)
82 shift_amount
= shift_amount
% 32;
83 return_value
= (Rm
>> shift_amount
) | (Rm
<< (32 - shift_amount
));
84 *carry
= (return_value
>> 31) & 0x1;
86 } else if (shift
== 0x4) { /* RRX */
87 return_value
= Rm
>> 1;
97 static uint32_t arm_shifter_operand(struct arm_sim_interface
*sim
,
98 int variant
, union arm_shifter_operand shifter_operand
,
99 uint8_t *shifter_carry_out
)
101 uint32_t return_value
;
102 int instruction_size
;
104 if (sim
->get_state(sim
) == ARM_STATE_ARM
)
105 instruction_size
= 4;
107 instruction_size
= 2;
109 *shifter_carry_out
= sim
->get_cpsr(sim
, 29, 1);
111 if (variant
== 0) /* 32-bit immediate */
112 return_value
= shifter_operand
.immediate
.immediate
;
113 else if (variant
== 1) {/* immediate shift */
114 uint32_t Rm
= sim
->get_reg_mode(sim
, shifter_operand
.immediate_shift
.Rm
);
116 /* adjust RM in case the PC is being read */
117 if (shifter_operand
.immediate_shift
.Rm
== 15)
118 Rm
+= 2 * instruction_size
;
120 return_value
= arm_shift(shifter_operand
.immediate_shift
.shift
,
121 Rm
, shifter_operand
.immediate_shift
.shift_imm
,
123 } else if (variant
== 2) { /* register shift */
124 uint32_t Rm
= sim
->get_reg_mode(sim
, shifter_operand
.register_shift
.Rm
);
125 uint32_t Rs
= sim
->get_reg_mode(sim
, shifter_operand
.register_shift
.Rs
);
127 /* adjust RM in case the PC is being read */
128 if (shifter_operand
.register_shift
.Rm
== 15)
129 Rm
+= 2 * instruction_size
;
131 return_value
= arm_shift(shifter_operand
.immediate_shift
.shift
,
132 Rm
, Rs
, shifter_carry_out
);
134 LOG_ERROR("BUG: shifter_operand.variant not 0, 1 or 2");
135 return_value
= 0xffffffff;
141 static int pass_condition(uint32_t cpsr
, uint32_t opcode
)
143 switch ((opcode
& 0xf0000000) >> 28) {
145 if (cpsr
& 0x40000000)
150 if (!(cpsr
& 0x40000000))
155 if (cpsr
& 0x20000000)
160 if (!(cpsr
& 0x20000000))
165 if (cpsr
& 0x80000000)
170 if (!(cpsr
& 0x80000000))
175 if (cpsr
& 0x10000000)
180 if (!(cpsr
& 0x10000000))
185 if ((cpsr
& 0x20000000) && !(cpsr
& 0x40000000))
190 if (!(cpsr
& 0x20000000) || (cpsr
& 0x40000000))
195 if (((cpsr
& 0x80000000) && (cpsr
& 0x10000000))
196 || (!(cpsr
& 0x80000000) && !(cpsr
& 0x10000000)))
201 if (((cpsr
& 0x80000000) && !(cpsr
& 0x10000000))
202 || (!(cpsr
& 0x80000000) && (cpsr
& 0x10000000)))
207 if (!(cpsr
& 0x40000000) &&
208 (((cpsr
& 0x80000000) && (cpsr
& 0x10000000))
209 || (!(cpsr
& 0x80000000) && !(cpsr
& 0x10000000))))
214 if ((cpsr
& 0x40000000) ||
215 ((cpsr
& 0x80000000) && !(cpsr
& 0x10000000))
216 || (!(cpsr
& 0x80000000) && (cpsr
& 0x10000000)))
226 LOG_ERROR("BUG: should never get here");
230 static int thumb_pass_branch_condition(uint32_t cpsr
, uint16_t opcode
)
232 return pass_condition(cpsr
, (opcode
& 0x0f00) << 20);
235 /* simulate a single step (if possible)
236 * if the dry_run_pc argument is provided, no state is changed,
237 * but the new pc is stored in the variable pointed at by the argument
239 static int arm_simulate_step_core(struct target
*target
,
240 uint32_t *dry_run_pc
, struct arm_sim_interface
*sim
)
242 uint32_t current_pc
= sim
->get_reg(sim
, 15);
243 struct arm_instruction instruction
;
244 int instruction_size
;
245 int retval
= ERROR_OK
;
247 if (sim
->get_state(sim
) == ARM_STATE_ARM
) {
250 /* get current instruction, and identify it */
251 retval
= target_read_u32(target
, current_pc
, &opcode
);
252 if (retval
!= ERROR_OK
)
254 retval
= arm_evaluate_opcode(opcode
, current_pc
, &instruction
);
255 if (retval
!= ERROR_OK
)
257 instruction_size
= 4;
259 /* check condition code (for all instructions) */
260 if (!pass_condition(sim
->get_cpsr(sim
, 0, 32), opcode
)) {
262 *dry_run_pc
= current_pc
+ instruction_size
;
264 sim
->set_reg(sim
, 15, current_pc
+ instruction_size
);
271 retval
= target_read_u16(target
, current_pc
, &opcode
);
272 if (retval
!= ERROR_OK
)
274 retval
= thumb_evaluate_opcode(opcode
, current_pc
, &instruction
);
275 if (retval
!= ERROR_OK
)
277 instruction_size
= 2;
279 /* check condition code (only for branch (1) instructions) */
280 if ((opcode
& 0xf000) == 0xd000
281 && !thumb_pass_branch_condition(
282 sim
->get_cpsr(sim
, 0, 32), opcode
)) {
284 *dry_run_pc
= current_pc
+ instruction_size
;
286 sim
->set_reg(sim
, 15, current_pc
+ instruction_size
);
291 /* Deal with 32-bit BL/BLX */
292 if ((opcode
& 0xf800) == 0xf000) {
293 uint32_t high
= instruction
.info
.b_bl_bx_blx
.target_address
;
294 retval
= target_read_u16(target
, current_pc
+2, &opcode
);
295 if (retval
!= ERROR_OK
)
297 retval
= thumb_evaluate_opcode(opcode
, current_pc
, &instruction
);
298 if (retval
!= ERROR_OK
)
300 instruction
.info
.b_bl_bx_blx
.target_address
+= high
;
304 /* examine instruction type */
306 /* branch instructions */
307 if ((instruction
.type
>= ARM_B
) && (instruction
.type
<= ARM_BLX
)) {
308 uint32_t target_address
;
310 if (instruction
.info
.b_bl_bx_blx
.reg_operand
== -1)
311 target_address
= instruction
.info
.b_bl_bx_blx
.target_address
;
313 target_address
= sim
->get_reg_mode(sim
,
314 instruction
.info
.b_bl_bx_blx
.reg_operand
);
315 if (instruction
.info
.b_bl_bx_blx
.reg_operand
== 15)
316 target_address
+= 2 * instruction_size
;
320 *dry_run_pc
= target_address
& ~1;
323 if (instruction
.type
== ARM_B
)
324 sim
->set_reg(sim
, 15, target_address
);
325 else if (instruction
.type
== ARM_BL
) {
326 uint32_t old_pc
= sim
->get_reg(sim
, 15);
327 int T
= (sim
->get_state(sim
) == ARM_STATE_THUMB
);
328 sim
->set_reg_mode(sim
, 14, old_pc
+ 4 + T
);
329 sim
->set_reg(sim
, 15, target_address
);
330 } else if (instruction
.type
== ARM_BX
) {
331 if (target_address
& 0x1)
332 sim
->set_state(sim
, ARM_STATE_THUMB
);
334 sim
->set_state(sim
, ARM_STATE_ARM
);
335 sim
->set_reg(sim
, 15, target_address
& 0xfffffffe);
336 } else if (instruction
.type
== ARM_BLX
) {
337 uint32_t old_pc
= sim
->get_reg(sim
, 15);
338 int T
= (sim
->get_state(sim
) == ARM_STATE_THUMB
);
339 sim
->set_reg_mode(sim
, 14, old_pc
+ 4 + T
);
341 if (target_address
& 0x1)
342 sim
->set_state(sim
, ARM_STATE_THUMB
);
344 sim
->set_state(sim
, ARM_STATE_ARM
);
345 sim
->set_reg(sim
, 15, target_address
& 0xfffffffe);
351 /* data processing instructions, except compare instructions (CMP, CMN, TST, TEQ) */
352 else if (((instruction
.type
>= ARM_AND
) && (instruction
.type
<= ARM_RSC
))
353 || ((instruction
.type
>= ARM_ORR
) && (instruction
.type
<= ARM_MVN
))) {
354 uint32_t Rd
, Rn
, shifter_operand
;
355 uint8_t C
= sim
->get_cpsr(sim
, 29, 1);
359 /* ARM_MOV and ARM_MVN does not use Rn */
360 if ((instruction
.type
!= ARM_MOV
) && (instruction
.type
!= ARM_MVN
))
361 Rn
= sim
->get_reg_mode(sim
, instruction
.info
.data_proc
.Rn
);
365 shifter_operand
= arm_shifter_operand(sim
,
366 instruction
.info
.data_proc
.variant
,
367 instruction
.info
.data_proc
.shifter_operand
,
370 /* adjust Rn in case the PC is being read */
371 if (instruction
.info
.data_proc
.Rn
== 15)
372 Rn
+= 2 * instruction_size
;
374 if (instruction
.type
== ARM_AND
)
375 Rd
= Rn
& shifter_operand
;
376 else if (instruction
.type
== ARM_EOR
)
377 Rd
= Rn
^ shifter_operand
;
378 else if (instruction
.type
== ARM_SUB
)
379 Rd
= Rn
- shifter_operand
;
380 else if (instruction
.type
== ARM_RSB
)
381 Rd
= shifter_operand
- Rn
;
382 else if (instruction
.type
== ARM_ADD
)
383 Rd
= Rn
+ shifter_operand
;
384 else if (instruction
.type
== ARM_ADC
)
385 Rd
= Rn
+ shifter_operand
+ (C
& 1);
386 else if (instruction
.type
== ARM_SBC
)
387 Rd
= Rn
- shifter_operand
- (C
& 1) ? 0 : 1;
388 else if (instruction
.type
== ARM_RSC
)
389 Rd
= shifter_operand
- Rn
- (C
& 1) ? 0 : 1;
390 else if (instruction
.type
== ARM_ORR
)
391 Rd
= Rn
| shifter_operand
;
392 else if (instruction
.type
== ARM_BIC
)
393 Rd
= Rn
& ~(shifter_operand
);
394 else if (instruction
.type
== ARM_MOV
)
395 Rd
= shifter_operand
;
396 else if (instruction
.type
== ARM_MVN
)
397 Rd
= ~shifter_operand
;
399 LOG_WARNING("unhandled instruction type");
402 if (instruction
.info
.data_proc
.Rd
== 15)
403 *dry_run_pc
= Rd
& ~1;
405 *dry_run_pc
= current_pc
+ instruction_size
;
409 if (instruction
.info
.data_proc
.Rd
== 15) {
410 sim
->set_reg_mode(sim
, 15, Rd
& ~1);
412 sim
->set_state(sim
, ARM_STATE_THUMB
);
414 sim
->set_state(sim
, ARM_STATE_ARM
);
417 sim
->set_reg_mode(sim
, instruction
.info
.data_proc
.Rd
, Rd
);
418 LOG_WARNING("no updating of flags yet");
421 /* compare instructions (CMP, CMN, TST, TEQ) */
422 else if ((instruction
.type
>= ARM_TST
) && (instruction
.type
<= ARM_CMN
)) {
424 *dry_run_pc
= current_pc
+ instruction_size
;
427 LOG_WARNING("no updating of flags yet");
429 /* load register instructions */
430 else if ((instruction
.type
>= ARM_LDR
) && (instruction
.type
<= ARM_LDRSH
)) {
431 uint32_t load_address
= 0, modified_address
= 0, load_value
= 0;
432 uint32_t Rn
= sim
->get_reg_mode(sim
, instruction
.info
.load_store
.Rn
);
434 /* adjust Rn in case the PC is being read */
435 if (instruction
.info
.load_store
.Rn
== 15)
436 Rn
+= 2 * instruction_size
;
438 if (instruction
.info
.load_store
.offset_mode
== 0) {
439 if (instruction
.info
.load_store
.U
)
440 modified_address
= Rn
+ instruction
.info
.load_store
.offset
.offset
;
442 modified_address
= Rn
- instruction
.info
.load_store
.offset
.offset
;
443 } else if (instruction
.info
.load_store
.offset_mode
== 1) {
445 uint32_t Rm
= sim
->get_reg_mode(sim
,
446 instruction
.info
.load_store
.offset
.reg
.Rm
);
447 uint8_t shift
= instruction
.info
.load_store
.offset
.reg
.shift
;
448 uint8_t shift_imm
= instruction
.info
.load_store
.offset
.reg
.shift_imm
;
449 uint8_t carry
= sim
->get_cpsr(sim
, 29, 1);
451 offset
= arm_shift(shift
, Rm
, shift_imm
, &carry
);
453 if (instruction
.info
.load_store
.U
)
454 modified_address
= Rn
+ offset
;
456 modified_address
= Rn
- offset
;
458 LOG_ERROR("BUG: offset_mode neither 0 (offset) nor 1 (scaled register)");
460 if (instruction
.info
.load_store
.index_mode
== 0) {
462 * we load from the modified address, but don't change
463 * the base address register
465 load_address
= modified_address
;
466 modified_address
= Rn
;
467 } else if (instruction
.info
.load_store
.index_mode
== 1) {
469 * we load from the modified address, and write it
470 * back to the base address register
472 load_address
= modified_address
;
473 } else if (instruction
.info
.load_store
.index_mode
== 2) {
475 * we load from the unmodified address, and write the
476 * modified address back
481 if ((!dry_run_pc
) || (instruction
.info
.load_store
.Rd
== 15)) {
482 retval
= target_read_u32(target
, load_address
, &load_value
);
483 if (retval
!= ERROR_OK
)
488 if (instruction
.info
.load_store
.Rd
== 15)
489 *dry_run_pc
= load_value
& ~1;
491 *dry_run_pc
= current_pc
+ instruction_size
;
494 if ((instruction
.info
.load_store
.index_mode
== 1) ||
495 (instruction
.info
.load_store
.index_mode
== 2))
496 sim
->set_reg_mode(sim
,
497 instruction
.info
.load_store
.Rn
,
500 if (instruction
.info
.load_store
.Rd
== 15) {
501 sim
->set_reg_mode(sim
, 15, load_value
& ~1);
503 sim
->set_state(sim
, ARM_STATE_THUMB
);
505 sim
->set_state(sim
, ARM_STATE_ARM
);
508 sim
->set_reg_mode(sim
, instruction
.info
.load_store
.Rd
, load_value
);
511 /* load multiple instruction */
512 else if (instruction
.type
== ARM_LDM
) {
514 uint32_t Rn
= sim
->get_reg_mode(sim
, instruction
.info
.load_store_multiple
.Rn
);
515 uint32_t load_values
[16];
518 for (i
= 0; i
< 16; i
++) {
519 if (instruction
.info
.load_store_multiple
.register_list
& (1 << i
))
523 switch (instruction
.info
.load_store_multiple
.addressing_mode
) {
524 case 0: /* Increment after */
527 case 1: /* Increment before */
530 case 2: /* Decrement after */
531 Rn
= Rn
- (bits_set
* 4) + 4;
533 case 3: /* Decrement before */
534 Rn
= Rn
- (bits_set
* 4);
538 for (i
= 0; i
< 16; i
++) {
539 if (instruction
.info
.load_store_multiple
.register_list
& (1 << i
)) {
540 if ((!dry_run_pc
) || (i
== 15))
541 target_read_u32(target
, Rn
, &load_values
[i
]);
547 if (instruction
.info
.load_store_multiple
.register_list
& 0x8000) {
548 *dry_run_pc
= load_values
[15] & ~1;
554 if (instruction
.info
.load_store_multiple
.S
) {
555 if (instruction
.info
.load_store_multiple
.register_list
& 0x8000)
559 for (i
= 0; i
< 16; i
++) {
560 if (instruction
.info
.load_store_multiple
.register_list
& (1 << i
)) {
562 uint32_t val
= load_values
[i
];
563 sim
->set_reg_mode(sim
, i
, val
& ~1);
565 sim
->set_state(sim
, ARM_STATE_THUMB
);
567 sim
->set_state(sim
, ARM_STATE_ARM
);
569 sim
->set_reg_mode(sim
, i
, load_values
[i
]);
574 uint32_t spsr
= sim
->get_reg_mode(sim
, 16);
575 sim
->set_reg(sim
, ARMV4_5_CPSR
, spsr
);
578 /* base register writeback */
579 if (instruction
.info
.load_store_multiple
.W
)
580 sim
->set_reg_mode(sim
, instruction
.info
.load_store_multiple
.Rn
, Rn
);
583 if (instruction
.info
.load_store_multiple
.register_list
& 0x8000)
587 /* store multiple instruction */
588 else if (instruction
.type
== ARM_STM
) {
592 /* STM wont affect PC (advance by instruction size */
594 uint32_t Rn
= sim
->get_reg_mode(sim
,
595 instruction
.info
.load_store_multiple
.Rn
);
598 for (i
= 0; i
< 16; i
++) {
599 if (instruction
.info
.load_store_multiple
.register_list
& (1 << i
))
603 switch (instruction
.info
.load_store_multiple
.addressing_mode
) {
604 case 0: /* Increment after */
607 case 1: /* Increment before */
610 case 2: /* Decrement after */
611 Rn
= Rn
- (bits_set
* 4) + 4;
613 case 3: /* Decrement before */
614 Rn
= Rn
- (bits_set
* 4);
618 for (i
= 0; i
< 16; i
++) {
619 if (instruction
.info
.load_store_multiple
.register_list
& (1 << i
)) {
620 target_write_u32(target
, Rn
, sim
->get_reg_mode(sim
, i
));
625 /* base register writeback */
626 if (instruction
.info
.load_store_multiple
.W
)
627 sim
->set_reg_mode(sim
,
628 instruction
.info
.load_store_multiple
.Rn
, Rn
);
631 } else if (!dry_run_pc
) {
632 /* the instruction wasn't handled, but we're supposed to simulate it
634 LOG_ERROR("Unimplemented instruction, could not simulate it.");
639 *dry_run_pc
= current_pc
+ instruction_size
;
642 sim
->set_reg(sim
, 15, current_pc
+ instruction_size
);
648 static uint32_t armv4_5_get_reg(struct arm_sim_interface
*sim
, int reg
)
650 struct arm
*arm
= (struct arm
*)sim
->user_data
;
652 return buf_get_u32(arm
->core_cache
->reg_list
[reg
].value
, 0, 32);
655 static void armv4_5_set_reg(struct arm_sim_interface
*sim
, int reg
, uint32_t value
)
657 struct arm
*arm
= (struct arm
*)sim
->user_data
;
659 buf_set_u32(arm
->core_cache
->reg_list
[reg
].value
, 0, 32, value
);
662 static uint32_t armv4_5_get_reg_mode(struct arm_sim_interface
*sim
, int reg
)
664 struct arm
*arm
= (struct arm
*)sim
->user_data
;
666 return buf_get_u32(ARMV4_5_CORE_REG_MODE(arm
->core_cache
,
667 arm
->core_mode
, reg
).value
, 0, 32);
670 static void armv4_5_set_reg_mode(struct arm_sim_interface
*sim
, int reg
, uint32_t value
)
672 struct arm
*arm
= (struct arm
*)sim
->user_data
;
674 buf_set_u32(ARMV4_5_CORE_REG_MODE(arm
->core_cache
,
675 arm
->core_mode
, reg
).value
, 0, 32, value
);
678 static uint32_t armv4_5_get_cpsr(struct arm_sim_interface
*sim
, int pos
, int bits
)
680 struct arm
*arm
= (struct arm
*)sim
->user_data
;
682 return buf_get_u32(arm
->cpsr
->value
, pos
, bits
);
685 static enum arm_state
armv4_5_get_state(struct arm_sim_interface
*sim
)
687 struct arm
*arm
= (struct arm
*)sim
->user_data
;
689 return arm
->core_state
;
692 static void armv4_5_set_state(struct arm_sim_interface
*sim
, enum arm_state mode
)
694 struct arm
*arm
= (struct arm
*)sim
->user_data
;
696 arm
->core_state
= mode
;
699 static enum arm_mode
armv4_5_get_mode(struct arm_sim_interface
*sim
)
701 struct arm
*arm
= (struct arm
*)sim
->user_data
;
703 return arm
->core_mode
;
706 int arm_simulate_step(struct target
*target
, uint32_t *dry_run_pc
)
708 struct arm
*arm
= target_to_arm(target
);
709 struct arm_sim_interface sim
;
712 sim
.get_reg
= &armv4_5_get_reg
;
713 sim
.set_reg
= &armv4_5_set_reg
;
714 sim
.get_reg_mode
= &armv4_5_get_reg_mode
;
715 sim
.set_reg_mode
= &armv4_5_set_reg_mode
;
716 sim
.get_cpsr
= &armv4_5_get_cpsr
;
717 sim
.get_mode
= &armv4_5_get_mode
;
718 sim
.get_state
= &armv4_5_get_state
;
719 sim
.set_state
= &armv4_5_set_state
;
721 return arm_simulate_step_core(target
, dry_run_pc
, &sim
);
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)