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, write to the *
20 * Free Software Foundation, Inc., *
21 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
22 ***************************************************************************/
29 #include "arm_disassembler.h"
30 #include "arm_simulator.h"
31 #include <helper/binarybuffer.h>
33 #include <helper/log.h>
36 static uint32_t arm_shift(uint8_t shift
, uint32_t Rm
,
37 uint32_t shift_amount
, uint8_t *carry
)
39 uint32_t return_value
= 0;
42 if (shift
== 0x0) /* LSL */
44 if ((shift_amount
> 0) && (shift_amount
<= 32))
46 return_value
= Rm
<< shift_amount
;
47 *carry
= Rm
>> (32 - shift_amount
);
49 else if (shift_amount
> 32)
54 else /* (shift_amount == 0) */
59 else if (shift
== 0x1) /* LSR */
61 if ((shift_amount
> 0) && (shift_amount
<= 32))
63 return_value
= Rm
>> shift_amount
;
64 *carry
= (Rm
>> (shift_amount
- 1)) & 1;
66 else if (shift_amount
> 32)
71 else /* (shift_amount == 0) */
76 else if (shift
== 0x2) /* ASR */
78 if ((shift_amount
> 0) && (shift_amount
<= 32))
80 /* C right shifts of unsigned values are guaranteed to
81 * be logical (shift in zeroes); simulate an arithmetic
82 * shift (shift in signed-bit) by adding the sign bit
85 return_value
= Rm
>> shift_amount
;
87 return_value
|= 0xffffffff << (32 - shift_amount
);
89 else if (shift_amount
> 32)
93 return_value
= 0xffffffff;
102 else /* (shift_amount == 0) */
107 else if (shift
== 0x3) /* ROR */
109 if (shift_amount
== 0)
115 shift_amount
= shift_amount
% 32;
116 return_value
= (Rm
>> shift_amount
) | (Rm
<< (32 - shift_amount
));
117 *carry
= (return_value
>> 31) & 0x1;
120 else if (shift
== 0x4) /* RRX */
122 return_value
= Rm
>> 1;
132 static uint32_t arm_shifter_operand(struct arm_sim_interface
*sim
,
133 int variant
, union arm_shifter_operand shifter_operand
,
134 uint8_t *shifter_carry_out
)
136 uint32_t return_value
;
137 int instruction_size
;
139 if (sim
->get_state(sim
) == ARM_STATE_ARM
)
140 instruction_size
= 4;
142 instruction_size
= 2;
144 *shifter_carry_out
= sim
->get_cpsr(sim
, 29, 1);
146 if (variant
== 0) /* 32-bit immediate */
148 return_value
= shifter_operand
.immediate
.immediate
;
150 else if (variant
== 1) /* immediate shift */
152 uint32_t Rm
= sim
->get_reg_mode(sim
, shifter_operand
.immediate_shift
.Rm
);
154 /* adjust RM in case the PC is being read */
155 if (shifter_operand
.immediate_shift
.Rm
== 15)
156 Rm
+= 2 * instruction_size
;
158 return_value
= arm_shift(shifter_operand
.immediate_shift
.shift
,
159 Rm
, shifter_operand
.immediate_shift
.shift_imm
,
162 else if (variant
== 2) /* register shift */
164 uint32_t Rm
= sim
->get_reg_mode(sim
, shifter_operand
.register_shift
.Rm
);
165 uint32_t Rs
= sim
->get_reg_mode(sim
, shifter_operand
.register_shift
.Rs
);
167 /* adjust RM in case the PC is being read */
168 if (shifter_operand
.register_shift
.Rm
== 15)
169 Rm
+= 2 * instruction_size
;
171 return_value
= arm_shift(shifter_operand
.immediate_shift
.shift
,
172 Rm
, Rs
, shifter_carry_out
);
176 LOG_ERROR("BUG: shifter_operand.variant not 0, 1 or 2");
177 return_value
= 0xffffffff;
183 static int pass_condition(uint32_t cpsr
, uint32_t opcode
)
185 switch ((opcode
& 0xf0000000) >> 28)
188 if (cpsr
& 0x40000000)
193 if (!(cpsr
& 0x40000000))
198 if (cpsr
& 0x20000000)
203 if (!(cpsr
& 0x20000000))
208 if (cpsr
& 0x80000000)
213 if (!(cpsr
& 0x80000000))
218 if (cpsr
& 0x10000000)
223 if (!(cpsr
& 0x10000000))
228 if ((cpsr
& 0x20000000) && !(cpsr
& 0x40000000))
233 if (!(cpsr
& 0x20000000) || (cpsr
& 0x40000000))
238 if (((cpsr
& 0x80000000) && (cpsr
& 0x10000000))
239 || (!(cpsr
& 0x80000000) && !(cpsr
& 0x10000000)))
244 if (((cpsr
& 0x80000000) && !(cpsr
& 0x10000000))
245 || (!(cpsr
& 0x80000000) && (cpsr
& 0x10000000)))
250 if (!(cpsr
& 0x40000000) &&
251 (((cpsr
& 0x80000000) && (cpsr
& 0x10000000))
252 || (!(cpsr
& 0x80000000) && !(cpsr
& 0x10000000))))
257 if ((cpsr
& 0x40000000) ||
258 ((cpsr
& 0x80000000) && !(cpsr
& 0x10000000))
259 || (!(cpsr
& 0x80000000) && (cpsr
& 0x10000000)))
269 LOG_ERROR("BUG: should never get here");
273 static int thumb_pass_branch_condition(uint32_t cpsr
, uint16_t opcode
)
275 return pass_condition(cpsr
, (opcode
& 0x0f00) << 20);
278 /* simulate a single step (if possible)
279 * if the dry_run_pc argument is provided, no state is changed,
280 * but the new pc is stored in the variable pointed at by the argument
282 static int arm_simulate_step_core(struct target
*target
,
283 uint32_t *dry_run_pc
, struct arm_sim_interface
*sim
)
285 uint32_t current_pc
= sim
->get_reg(sim
, 15);
286 struct arm_instruction instruction
;
287 int instruction_size
;
288 int retval
= ERROR_OK
;
290 if (sim
->get_state(sim
) == ARM_STATE_ARM
)
294 /* get current instruction, and identify it */
295 if ((retval
= target_read_u32(target
, current_pc
, &opcode
)) != ERROR_OK
)
299 if ((retval
= arm_evaluate_opcode(opcode
, current_pc
, &instruction
)) != ERROR_OK
)
303 instruction_size
= 4;
305 /* check condition code (for all instructions) */
306 if (!pass_condition(sim
->get_cpsr(sim
, 0, 32), opcode
))
310 *dry_run_pc
= current_pc
+ instruction_size
;
314 sim
->set_reg(sim
, 15, current_pc
+ instruction_size
);
324 retval
= target_read_u16(target
, current_pc
, &opcode
);
325 if (retval
!= ERROR_OK
)
327 retval
= thumb_evaluate_opcode(opcode
, current_pc
, &instruction
);
328 if (retval
!= ERROR_OK
)
330 instruction_size
= 2;
332 /* check condition code (only for branch (1) instructions) */
333 if ((opcode
& 0xf000) == 0xd000
334 && !thumb_pass_branch_condition(
335 sim
->get_cpsr(sim
, 0, 32), opcode
))
339 *dry_run_pc
= current_pc
+ instruction_size
;
343 sim
->set_reg(sim
, 15, current_pc
+ instruction_size
);
349 /* Deal with 32-bit BL/BLX */
350 if ((opcode
& 0xf800) == 0xf000) {
351 uint32_t high
= instruction
.info
.b_bl_bx_blx
.target_address
;
352 retval
= target_read_u16(target
, current_pc
+2, &opcode
);
353 if (retval
!= ERROR_OK
)
355 retval
= thumb_evaluate_opcode(opcode
, current_pc
, &instruction
);
356 if (retval
!= ERROR_OK
)
358 instruction
.info
.b_bl_bx_blx
.target_address
+= high
;
362 /* examine instruction type */
364 /* branch instructions */
365 if ((instruction
.type
>= ARM_B
) && (instruction
.type
<= ARM_BLX
))
369 if (instruction
.info
.b_bl_bx_blx
.reg_operand
== -1)
371 target
= instruction
.info
.b_bl_bx_blx
.target_address
;
375 target
= sim
->get_reg_mode(sim
, instruction
.info
.b_bl_bx_blx
.reg_operand
);
376 if (instruction
.info
.b_bl_bx_blx
.reg_operand
== 15)
378 target
+= 2 * instruction_size
;
384 *dry_run_pc
= target
& ~1;
389 if (instruction
.type
== ARM_B
)
391 sim
->set_reg(sim
, 15, target
);
393 else if (instruction
.type
== ARM_BL
)
395 uint32_t old_pc
= sim
->get_reg(sim
, 15);
396 int T
= (sim
->get_state(sim
) == ARM_STATE_THUMB
);
397 sim
->set_reg_mode(sim
, 14, old_pc
+ 4 + T
);
398 sim
->set_reg(sim
, 15, target
);
400 else if (instruction
.type
== ARM_BX
)
404 sim
->set_state(sim
, ARM_STATE_THUMB
);
408 sim
->set_state(sim
, ARM_STATE_ARM
);
410 sim
->set_reg(sim
, 15, target
& 0xfffffffe);
412 else if (instruction
.type
== ARM_BLX
)
414 uint32_t old_pc
= sim
->get_reg(sim
, 15);
415 int T
= (sim
->get_state(sim
) == ARM_STATE_THUMB
);
416 sim
->set_reg_mode(sim
, 14, old_pc
+ 4 + T
);
420 sim
->set_state(sim
, ARM_STATE_THUMB
);
424 sim
->set_state(sim
, ARM_STATE_ARM
);
426 sim
->set_reg(sim
, 15, target
& 0xfffffffe);
432 /* data processing instructions, except compare instructions (CMP, CMN, TST, TEQ) */
433 else if (((instruction
.type
>= ARM_AND
) && (instruction
.type
<= ARM_RSC
))
434 || ((instruction
.type
>= ARM_ORR
) && (instruction
.type
<= ARM_MVN
)))
436 uint32_t Rd
, Rn
, shifter_operand
;
437 uint8_t C
= sim
->get_cpsr(sim
, 29, 1);
441 /* ARM_MOV and ARM_MVN does not use Rn */
442 if ((instruction
.type
!= ARM_MOV
) && (instruction
.type
!= ARM_MVN
))
443 Rn
= sim
->get_reg_mode(sim
, instruction
.info
.data_proc
.Rn
);
447 shifter_operand
= arm_shifter_operand(sim
,
448 instruction
.info
.data_proc
.variant
,
449 instruction
.info
.data_proc
.shifter_operand
,
452 /* adjust Rn in case the PC is being read */
453 if (instruction
.info
.data_proc
.Rn
== 15)
454 Rn
+= 2 * instruction_size
;
456 if (instruction
.type
== ARM_AND
)
457 Rd
= Rn
& shifter_operand
;
458 else if (instruction
.type
== ARM_EOR
)
459 Rd
= Rn
^ shifter_operand
;
460 else if (instruction
.type
== ARM_SUB
)
461 Rd
= Rn
- shifter_operand
;
462 else if (instruction
.type
== ARM_RSB
)
463 Rd
= shifter_operand
- Rn
;
464 else if (instruction
.type
== ARM_ADD
)
465 Rd
= Rn
+ shifter_operand
;
466 else if (instruction
.type
== ARM_ADC
)
467 Rd
= Rn
+ shifter_operand
+ (C
& 1);
468 else if (instruction
.type
== ARM_SBC
)
469 Rd
= Rn
- shifter_operand
- (C
& 1) ? 0 : 1;
470 else if (instruction
.type
== ARM_RSC
)
471 Rd
= shifter_operand
- Rn
- (C
& 1) ? 0 : 1;
472 else if (instruction
.type
== ARM_ORR
)
473 Rd
= Rn
| shifter_operand
;
474 else if (instruction
.type
== ARM_BIC
)
475 Rd
= Rn
& ~(shifter_operand
);
476 else if (instruction
.type
== ARM_MOV
)
477 Rd
= shifter_operand
;
478 else if (instruction
.type
== ARM_MVN
)
479 Rd
= ~shifter_operand
;
481 LOG_WARNING("unhandled instruction type");
485 if (instruction
.info
.data_proc
.Rd
== 15)
486 *dry_run_pc
= Rd
& ~1;
488 *dry_run_pc
= current_pc
+ instruction_size
;
494 if (instruction
.info
.data_proc
.Rd
== 15) {
495 sim
->set_reg_mode(sim
, 15, Rd
& ~1);
497 sim
->set_state(sim
, ARM_STATE_THUMB
);
499 sim
->set_state(sim
, ARM_STATE_ARM
);
502 sim
->set_reg_mode(sim
, instruction
.info
.data_proc
.Rd
, Rd
);
503 LOG_WARNING("no updating of flags yet");
506 /* compare instructions (CMP, CMN, TST, TEQ) */
507 else if ((instruction
.type
>= ARM_TST
) && (instruction
.type
<= ARM_CMN
))
511 *dry_run_pc
= current_pc
+ instruction_size
;
516 LOG_WARNING("no updating of flags yet");
519 /* load register instructions */
520 else if ((instruction
.type
>= ARM_LDR
) && (instruction
.type
<= ARM_LDRSH
))
522 uint32_t load_address
= 0, modified_address
= 0, load_value
;
523 uint32_t Rn
= sim
->get_reg_mode(sim
, instruction
.info
.load_store
.Rn
);
525 /* adjust Rn in case the PC is being read */
526 if (instruction
.info
.load_store
.Rn
== 15)
527 Rn
+= 2 * instruction_size
;
529 if (instruction
.info
.load_store
.offset_mode
== 0)
531 if (instruction
.info
.load_store
.U
)
532 modified_address
= Rn
+ instruction
.info
.load_store
.offset
.offset
;
534 modified_address
= Rn
- instruction
.info
.load_store
.offset
.offset
;
536 else if (instruction
.info
.load_store
.offset_mode
== 1)
539 uint32_t Rm
= sim
->get_reg_mode(sim
,
540 instruction
.info
.load_store
.offset
.reg
.Rm
);
541 uint8_t shift
= instruction
.info
.load_store
.offset
.reg
.shift
;
542 uint8_t shift_imm
= instruction
.info
.load_store
.offset
.reg
.shift_imm
;
543 uint8_t carry
= sim
->get_cpsr(sim
, 29, 1);
545 offset
= arm_shift(shift
, Rm
, shift_imm
, &carry
);
547 if (instruction
.info
.load_store
.U
)
548 modified_address
= Rn
+ offset
;
550 modified_address
= Rn
- offset
;
554 LOG_ERROR("BUG: offset_mode neither 0 (offset) nor 1 (scaled register)");
557 if (instruction
.info
.load_store
.index_mode
== 0)
560 * we load from the modified address, but don't change
561 * the base address register
563 load_address
= modified_address
;
564 modified_address
= Rn
;
566 else if (instruction
.info
.load_store
.index_mode
== 1)
569 * we load from the modified address, and write it
570 * back to the base address register
572 load_address
= modified_address
;
574 else if (instruction
.info
.load_store
.index_mode
== 2)
577 * we load from the unmodified address, and write the
578 * modified address back
583 if ((!dry_run_pc
) || (instruction
.info
.load_store
.Rd
== 15))
585 retval
= target_read_u32(target
, load_address
, &load_value
);
586 if (retval
!= ERROR_OK
)
592 if (instruction
.info
.load_store
.Rd
== 15)
593 *dry_run_pc
= load_value
& ~1;
595 *dry_run_pc
= current_pc
+ instruction_size
;
600 if ((instruction
.info
.load_store
.index_mode
== 1) ||
601 (instruction
.info
.load_store
.index_mode
== 2))
603 sim
->set_reg_mode(sim
, instruction
.info
.load_store
.Rn
, modified_address
);
606 if (instruction
.info
.load_store
.Rd
== 15) {
607 sim
->set_reg_mode(sim
, 15, load_value
& ~1);
609 sim
->set_state(sim
, ARM_STATE_THUMB
);
611 sim
->set_state(sim
, ARM_STATE_ARM
);
614 sim
->set_reg_mode(sim
, instruction
.info
.load_store
.Rd
, load_value
);
617 /* load multiple instruction */
618 else if (instruction
.type
== ARM_LDM
)
621 uint32_t Rn
= sim
->get_reg_mode(sim
, instruction
.info
.load_store_multiple
.Rn
);
622 uint32_t load_values
[16];
625 for (i
= 0; i
< 16; i
++)
627 if (instruction
.info
.load_store_multiple
.register_list
& (1 << i
))
631 switch (instruction
.info
.load_store_multiple
.addressing_mode
)
633 case 0: /* Increment after */
636 case 1: /* Increment before */
639 case 2: /* Decrement after */
640 Rn
= Rn
- (bits_set
* 4) + 4;
642 case 3: /* Decrement before */
643 Rn
= Rn
- (bits_set
* 4);
647 for (i
= 0; i
< 16; i
++)
649 if (instruction
.info
.load_store_multiple
.register_list
& (1 << i
))
651 if ((!dry_run_pc
) || (i
== 15))
653 target_read_u32(target
, Rn
, &load_values
[i
]);
661 if (instruction
.info
.load_store_multiple
.register_list
& 0x8000)
663 *dry_run_pc
= load_values
[15] & ~1;
669 enum arm_mode mode
= sim
->get_mode(sim
);
672 if (instruction
.info
.load_store_multiple
.S
)
674 if (instruction
.info
.load_store_multiple
.register_list
& 0x8000)
680 for (i
= 0; i
< 16; i
++)
682 if (instruction
.info
.load_store_multiple
.register_list
& (1 << i
))
685 uint32_t val
= load_values
[i
];
686 sim
->set_reg_mode(sim
, i
, val
& ~1);
688 sim
->set_state(sim
, ARM_STATE_THUMB
);
690 sim
->set_state(sim
, ARM_STATE_ARM
);
692 sim
->set_reg_mode(sim
, i
, load_values
[i
]);
699 uint32_t spsr
= sim
->get_reg_mode(sim
, 16);
700 sim
->set_reg(sim
, ARMV4_5_CPSR
, spsr
);
703 /* base register writeback */
704 if (instruction
.info
.load_store_multiple
.W
)
705 sim
->set_reg_mode(sim
, instruction
.info
.load_store_multiple
.Rn
, Rn
);
707 if (instruction
.info
.load_store_multiple
.register_list
& 0x8000)
711 /* store multiple instruction */
712 else if (instruction
.type
== ARM_STM
)
718 /* STM wont affect PC (advance by instruction size */
722 uint32_t Rn
= sim
->get_reg_mode(sim
,
723 instruction
.info
.load_store_multiple
.Rn
);
725 enum arm_mode mode
= sim
->get_mode(sim
);
727 for (i
= 0; i
< 16; i
++)
729 if (instruction
.info
.load_store_multiple
.register_list
& (1 << i
))
733 if (instruction
.info
.load_store_multiple
.S
)
738 switch (instruction
.info
.load_store_multiple
.addressing_mode
)
740 case 0: /* Increment after */
743 case 1: /* Increment before */
746 case 2: /* Decrement after */
747 Rn
= Rn
- (bits_set
* 4) + 4;
749 case 3: /* Decrement before */
750 Rn
= Rn
- (bits_set
* 4);
754 for (i
= 0; i
< 16; i
++)
756 if (instruction
.info
.load_store_multiple
.register_list
& (1 << i
))
758 target_write_u32(target
, Rn
, sim
->get_reg_mode(sim
, i
));
763 /* base register writeback */
764 if (instruction
.info
.load_store_multiple
.W
)
765 sim
->set_reg_mode(sim
,
766 instruction
.info
.load_store_multiple
.Rn
, Rn
);
770 else if (!dry_run_pc
)
772 /* the instruction wasn't handled, but we're supposed to simulate it
774 LOG_ERROR("Unimplemented instruction, could not simulate it.");
780 *dry_run_pc
= current_pc
+ instruction_size
;
785 sim
->set_reg(sim
, 15, current_pc
+ instruction_size
);
791 static uint32_t armv4_5_get_reg(struct arm_sim_interface
*sim
, int reg
)
793 struct arm
*armv4_5
= (struct arm
*)sim
->user_data
;
795 return buf_get_u32(armv4_5
->core_cache
->reg_list
[reg
].value
, 0, 32);
798 static void armv4_5_set_reg(struct arm_sim_interface
*sim
, int reg
, uint32_t value
)
800 struct arm
*armv4_5
= (struct arm
*)sim
->user_data
;
802 buf_set_u32(armv4_5
->core_cache
->reg_list
[reg
].value
, 0, 32, value
);
805 static uint32_t armv4_5_get_reg_mode(struct arm_sim_interface
*sim
, int reg
)
807 struct arm
*armv4_5
= (struct arm
*)sim
->user_data
;
809 return buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5
->core_cache
,
810 armv4_5
->core_mode
, reg
).value
, 0, 32);
813 static void armv4_5_set_reg_mode(struct arm_sim_interface
*sim
, int reg
, uint32_t value
)
815 struct arm
*armv4_5
= (struct arm
*)sim
->user_data
;
817 buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5
->core_cache
,
818 armv4_5
->core_mode
, reg
).value
, 0, 32, value
);
821 static uint32_t armv4_5_get_cpsr(struct arm_sim_interface
*sim
, int pos
, int bits
)
823 struct arm
*armv4_5
= (struct arm
*)sim
->user_data
;
825 return buf_get_u32(armv4_5
->cpsr
->value
, pos
, bits
);
828 static enum arm_state
armv4_5_get_state(struct arm_sim_interface
*sim
)
830 struct arm
*armv4_5
= (struct arm
*)sim
->user_data
;
832 return armv4_5
->core_state
;
835 static void armv4_5_set_state(struct arm_sim_interface
*sim
, enum arm_state mode
)
837 struct arm
*armv4_5
= (struct arm
*)sim
->user_data
;
839 armv4_5
->core_state
= mode
;
843 static enum arm_mode
armv4_5_get_mode(struct arm_sim_interface
*sim
)
845 struct arm
*armv4_5
= (struct arm
*)sim
->user_data
;
847 return armv4_5
->core_mode
;
852 int arm_simulate_step(struct target
*target
, uint32_t *dry_run_pc
)
854 struct arm
*armv4_5
= target_to_arm(target
);
855 struct arm_sim_interface sim
;
857 sim
.user_data
= armv4_5
;
858 sim
.get_reg
= &armv4_5_get_reg
;
859 sim
.set_reg
= &armv4_5_set_reg
;
860 sim
.get_reg_mode
= &armv4_5_get_reg_mode
;
861 sim
.set_reg_mode
= &armv4_5_set_reg_mode
;
862 sim
.get_cpsr
= &armv4_5_get_cpsr
;
863 sim
.get_mode
= &armv4_5_get_mode
;
864 sim
.get_state
= &armv4_5_get_state
;
865 sim
.set_state
= &armv4_5_set_state
;
867 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)