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 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
22 ***************************************************************************/
30 #include "arm_disassembler.h"
31 #include "arm_simulator.h"
32 #include <helper/binarybuffer.h>
34 #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 */
43 if ((shift_amount
> 0) && (shift_amount
<= 32)) {
44 return_value
= Rm
<< shift_amount
;
45 *carry
= Rm
>> (32 - shift_amount
);
46 } else if (shift_amount
> 32) {
49 } else /* (shift_amount == 0) */
51 } else if (shift
== 0x1) { /* LSR */
52 if ((shift_amount
> 0) && (shift_amount
<= 32)) {
53 return_value
= Rm
>> shift_amount
;
54 *carry
= (Rm
>> (shift_amount
- 1)) & 1;
55 } else if (shift_amount
> 32) {
58 } else /* (shift_amount == 0) */
60 } else if (shift
== 0x2) { /* ASR */
61 if ((shift_amount
> 0) && (shift_amount
<= 32)) {
62 /* C right shifts of unsigned values are guaranteed to
63 * be logical (shift in zeroes); simulate an arithmetic
64 * shift (shift in signed-bit) by adding the sign bit
67 return_value
= Rm
>> shift_amount
;
69 return_value
|= 0xffffffff << (32 - shift_amount
);
70 } else if (shift_amount
> 32) {
71 if (Rm
& 0x80000000) {
72 return_value
= 0xffffffff;
78 } else /* (shift_amount == 0) */
80 } else if (shift
== 0x3) { /* ROR */
81 if (shift_amount
== 0)
84 shift_amount
= shift_amount
% 32;
85 return_value
= (Rm
>> shift_amount
) | (Rm
<< (32 - shift_amount
));
86 *carry
= (return_value
>> 31) & 0x1;
88 } else if (shift
== 0x4) { /* RRX */
89 return_value
= Rm
>> 1;
99 static uint32_t arm_shifter_operand(struct arm_sim_interface
*sim
,
100 int variant
, union arm_shifter_operand shifter_operand
,
101 uint8_t *shifter_carry_out
)
103 uint32_t return_value
;
104 int instruction_size
;
106 if (sim
->get_state(sim
) == ARM_STATE_ARM
)
107 instruction_size
= 4;
109 instruction_size
= 2;
111 *shifter_carry_out
= sim
->get_cpsr(sim
, 29, 1);
113 if (variant
== 0) /* 32-bit immediate */
114 return_value
= shifter_operand
.immediate
.immediate
;
115 else if (variant
== 1) {/* immediate shift */
116 uint32_t Rm
= sim
->get_reg_mode(sim
, shifter_operand
.immediate_shift
.Rm
);
118 /* adjust RM in case the PC is being read */
119 if (shifter_operand
.immediate_shift
.Rm
== 15)
120 Rm
+= 2 * instruction_size
;
122 return_value
= arm_shift(shifter_operand
.immediate_shift
.shift
,
123 Rm
, shifter_operand
.immediate_shift
.shift_imm
,
125 } else if (variant
== 2) { /* register shift */
126 uint32_t Rm
= sim
->get_reg_mode(sim
, shifter_operand
.register_shift
.Rm
);
127 uint32_t Rs
= sim
->get_reg_mode(sim
, shifter_operand
.register_shift
.Rs
);
129 /* adjust RM in case the PC is being read */
130 if (shifter_operand
.register_shift
.Rm
== 15)
131 Rm
+= 2 * instruction_size
;
133 return_value
= arm_shift(shifter_operand
.immediate_shift
.shift
,
134 Rm
, Rs
, shifter_carry_out
);
136 LOG_ERROR("BUG: shifter_operand.variant not 0, 1 or 2");
137 return_value
= 0xffffffff;
143 static int pass_condition(uint32_t cpsr
, uint32_t opcode
)
145 switch ((opcode
& 0xf0000000) >> 28) {
147 if (cpsr
& 0x40000000)
152 if (!(cpsr
& 0x40000000))
157 if (cpsr
& 0x20000000)
162 if (!(cpsr
& 0x20000000))
167 if (cpsr
& 0x80000000)
172 if (!(cpsr
& 0x80000000))
177 if (cpsr
& 0x10000000)
182 if (!(cpsr
& 0x10000000))
187 if ((cpsr
& 0x20000000) && !(cpsr
& 0x40000000))
192 if (!(cpsr
& 0x20000000) || (cpsr
& 0x40000000))
197 if (((cpsr
& 0x80000000) && (cpsr
& 0x10000000))
198 || (!(cpsr
& 0x80000000) && !(cpsr
& 0x10000000)))
203 if (((cpsr
& 0x80000000) && !(cpsr
& 0x10000000))
204 || (!(cpsr
& 0x80000000) && (cpsr
& 0x10000000)))
209 if (!(cpsr
& 0x40000000) &&
210 (((cpsr
& 0x80000000) && (cpsr
& 0x10000000))
211 || (!(cpsr
& 0x80000000) && !(cpsr
& 0x10000000))))
216 if ((cpsr
& 0x40000000) ||
217 ((cpsr
& 0x80000000) && !(cpsr
& 0x10000000))
218 || (!(cpsr
& 0x80000000) && (cpsr
& 0x10000000)))
228 LOG_ERROR("BUG: should never get here");
232 static int thumb_pass_branch_condition(uint32_t cpsr
, uint16_t opcode
)
234 return pass_condition(cpsr
, (opcode
& 0x0f00) << 20);
237 /* simulate a single step (if possible)
238 * if the dry_run_pc argument is provided, no state is changed,
239 * but the new pc is stored in the variable pointed at by the argument
241 static int arm_simulate_step_core(struct target
*target
,
242 uint32_t *dry_run_pc
, struct arm_sim_interface
*sim
)
244 uint32_t current_pc
= sim
->get_reg(sim
, 15);
245 struct arm_instruction instruction
;
246 int instruction_size
;
247 int retval
= ERROR_OK
;
249 if (sim
->get_state(sim
) == ARM_STATE_ARM
) {
252 /* get current instruction, and identify it */
253 retval
= target_read_u32(target
, current_pc
, &opcode
);
254 if (retval
!= ERROR_OK
)
256 retval
= arm_evaluate_opcode(opcode
, current_pc
, &instruction
);
257 if (retval
!= ERROR_OK
)
259 instruction_size
= 4;
261 /* check condition code (for all instructions) */
262 if (!pass_condition(sim
->get_cpsr(sim
, 0, 32), opcode
)) {
264 *dry_run_pc
= current_pc
+ instruction_size
;
266 sim
->set_reg(sim
, 15, current_pc
+ instruction_size
);
273 retval
= target_read_u16(target
, current_pc
, &opcode
);
274 if (retval
!= ERROR_OK
)
276 retval
= thumb_evaluate_opcode(opcode
, current_pc
, &instruction
);
277 if (retval
!= ERROR_OK
)
279 instruction_size
= 2;
281 /* check condition code (only for branch (1) instructions) */
282 if ((opcode
& 0xf000) == 0xd000
283 && !thumb_pass_branch_condition(
284 sim
->get_cpsr(sim
, 0, 32), opcode
)) {
286 *dry_run_pc
= current_pc
+ instruction_size
;
288 sim
->set_reg(sim
, 15, current_pc
+ instruction_size
);
293 /* Deal with 32-bit BL/BLX */
294 if ((opcode
& 0xf800) == 0xf000) {
295 uint32_t high
= instruction
.info
.b_bl_bx_blx
.target_address
;
296 retval
= target_read_u16(target
, current_pc
+2, &opcode
);
297 if (retval
!= ERROR_OK
)
299 retval
= thumb_evaluate_opcode(opcode
, current_pc
, &instruction
);
300 if (retval
!= ERROR_OK
)
302 instruction
.info
.b_bl_bx_blx
.target_address
+= high
;
306 /* examine instruction type */
308 /* branch instructions */
309 if ((instruction
.type
>= ARM_B
) && (instruction
.type
<= ARM_BLX
)) {
310 uint32_t target_address
;
312 if (instruction
.info
.b_bl_bx_blx
.reg_operand
== -1)
313 target_address
= instruction
.info
.b_bl_bx_blx
.target_address
;
315 target_address
= sim
->get_reg_mode(sim
,
316 instruction
.info
.b_bl_bx_blx
.reg_operand
);
317 if (instruction
.info
.b_bl_bx_blx
.reg_operand
== 15)
318 target_address
+= 2 * instruction_size
;
322 *dry_run_pc
= target_address
& ~1;
325 if (instruction
.type
== ARM_B
)
326 sim
->set_reg(sim
, 15, target_address
);
327 else if (instruction
.type
== ARM_BL
) {
328 uint32_t old_pc
= sim
->get_reg(sim
, 15);
329 int T
= (sim
->get_state(sim
) == ARM_STATE_THUMB
);
330 sim
->set_reg_mode(sim
, 14, old_pc
+ 4 + T
);
331 sim
->set_reg(sim
, 15, target_address
);
332 } else if (instruction
.type
== ARM_BX
) {
333 if (target_address
& 0x1)
334 sim
->set_state(sim
, ARM_STATE_THUMB
);
336 sim
->set_state(sim
, ARM_STATE_ARM
);
337 sim
->set_reg(sim
, 15, target_address
& 0xfffffffe);
338 } else if (instruction
.type
== ARM_BLX
) {
339 uint32_t old_pc
= sim
->get_reg(sim
, 15);
340 int T
= (sim
->get_state(sim
) == ARM_STATE_THUMB
);
341 sim
->set_reg_mode(sim
, 14, old_pc
+ 4 + T
);
343 if (target_address
& 0x1)
344 sim
->set_state(sim
, ARM_STATE_THUMB
);
346 sim
->set_state(sim
, ARM_STATE_ARM
);
347 sim
->set_reg(sim
, 15, target_address
& 0xfffffffe);
353 /* data processing instructions, except compare instructions (CMP, CMN, TST, TEQ) */
354 else if (((instruction
.type
>= ARM_AND
) && (instruction
.type
<= ARM_RSC
))
355 || ((instruction
.type
>= ARM_ORR
) && (instruction
.type
<= ARM_MVN
))) {
356 uint32_t Rd
, Rn
, shifter_operand
;
357 uint8_t C
= sim
->get_cpsr(sim
, 29, 1);
361 /* ARM_MOV and ARM_MVN does not use Rn */
362 if ((instruction
.type
!= ARM_MOV
) && (instruction
.type
!= ARM_MVN
))
363 Rn
= sim
->get_reg_mode(sim
, instruction
.info
.data_proc
.Rn
);
367 shifter_operand
= arm_shifter_operand(sim
,
368 instruction
.info
.data_proc
.variant
,
369 instruction
.info
.data_proc
.shifter_operand
,
372 /* adjust Rn in case the PC is being read */
373 if (instruction
.info
.data_proc
.Rn
== 15)
374 Rn
+= 2 * instruction_size
;
376 if (instruction
.type
== ARM_AND
)
377 Rd
= Rn
& shifter_operand
;
378 else if (instruction
.type
== ARM_EOR
)
379 Rd
= Rn
^ shifter_operand
;
380 else if (instruction
.type
== ARM_SUB
)
381 Rd
= Rn
- shifter_operand
;
382 else if (instruction
.type
== ARM_RSB
)
383 Rd
= shifter_operand
- Rn
;
384 else if (instruction
.type
== ARM_ADD
)
385 Rd
= Rn
+ shifter_operand
;
386 else if (instruction
.type
== ARM_ADC
)
387 Rd
= Rn
+ shifter_operand
+ (C
& 1);
388 else if (instruction
.type
== ARM_SBC
)
389 Rd
= Rn
- shifter_operand
- (C
& 1) ? 0 : 1;
390 else if (instruction
.type
== ARM_RSC
)
391 Rd
= shifter_operand
- Rn
- (C
& 1) ? 0 : 1;
392 else if (instruction
.type
== ARM_ORR
)
393 Rd
= Rn
| shifter_operand
;
394 else if (instruction
.type
== ARM_BIC
)
395 Rd
= Rn
& ~(shifter_operand
);
396 else if (instruction
.type
== ARM_MOV
)
397 Rd
= shifter_operand
;
398 else if (instruction
.type
== ARM_MVN
)
399 Rd
= ~shifter_operand
;
401 LOG_WARNING("unhandled instruction type");
404 if (instruction
.info
.data_proc
.Rd
== 15)
405 *dry_run_pc
= Rd
& ~1;
407 *dry_run_pc
= current_pc
+ instruction_size
;
411 if (instruction
.info
.data_proc
.Rd
== 15) {
412 sim
->set_reg_mode(sim
, 15, Rd
& ~1);
414 sim
->set_state(sim
, ARM_STATE_THUMB
);
416 sim
->set_state(sim
, ARM_STATE_ARM
);
419 sim
->set_reg_mode(sim
, instruction
.info
.data_proc
.Rd
, Rd
);
420 LOG_WARNING("no updating of flags yet");
423 /* compare instructions (CMP, CMN, TST, TEQ) */
424 else if ((instruction
.type
>= ARM_TST
) && (instruction
.type
<= ARM_CMN
)) {
426 *dry_run_pc
= current_pc
+ instruction_size
;
429 LOG_WARNING("no updating of flags yet");
431 /* load register instructions */
432 else if ((instruction
.type
>= ARM_LDR
) && (instruction
.type
<= ARM_LDRSH
)) {
433 uint32_t load_address
= 0, modified_address
= 0, load_value
= 0;
434 uint32_t Rn
= sim
->get_reg_mode(sim
, instruction
.info
.load_store
.Rn
);
436 /* adjust Rn in case the PC is being read */
437 if (instruction
.info
.load_store
.Rn
== 15)
438 Rn
+= 2 * instruction_size
;
440 if (instruction
.info
.load_store
.offset_mode
== 0) {
441 if (instruction
.info
.load_store
.U
)
442 modified_address
= Rn
+ instruction
.info
.load_store
.offset
.offset
;
444 modified_address
= Rn
- instruction
.info
.load_store
.offset
.offset
;
445 } else if (instruction
.info
.load_store
.offset_mode
== 1) {
447 uint32_t Rm
= sim
->get_reg_mode(sim
,
448 instruction
.info
.load_store
.offset
.reg
.Rm
);
449 uint8_t shift
= instruction
.info
.load_store
.offset
.reg
.shift
;
450 uint8_t shift_imm
= instruction
.info
.load_store
.offset
.reg
.shift_imm
;
451 uint8_t carry
= sim
->get_cpsr(sim
, 29, 1);
453 offset
= arm_shift(shift
, Rm
, shift_imm
, &carry
);
455 if (instruction
.info
.load_store
.U
)
456 modified_address
= Rn
+ offset
;
458 modified_address
= Rn
- offset
;
460 LOG_ERROR("BUG: offset_mode neither 0 (offset) nor 1 (scaled register)");
462 if (instruction
.info
.load_store
.index_mode
== 0) {
464 * we load from the modified address, but don't change
465 * the base address register
467 load_address
= modified_address
;
468 modified_address
= Rn
;
469 } else if (instruction
.info
.load_store
.index_mode
== 1) {
471 * we load from the modified address, and write it
472 * back to the base address register
474 load_address
= modified_address
;
475 } else if (instruction
.info
.load_store
.index_mode
== 2) {
477 * we load from the unmodified address, and write the
478 * modified address back
483 if ((!dry_run_pc
) || (instruction
.info
.load_store
.Rd
== 15)) {
484 retval
= target_read_u32(target
, load_address
, &load_value
);
485 if (retval
!= ERROR_OK
)
490 if (instruction
.info
.load_store
.Rd
== 15)
491 *dry_run_pc
= load_value
& ~1;
493 *dry_run_pc
= current_pc
+ instruction_size
;
496 if ((instruction
.info
.load_store
.index_mode
== 1) ||
497 (instruction
.info
.load_store
.index_mode
== 2))
498 sim
->set_reg_mode(sim
,
499 instruction
.info
.load_store
.Rn
,
502 if (instruction
.info
.load_store
.Rd
== 15) {
503 sim
->set_reg_mode(sim
, 15, load_value
& ~1);
505 sim
->set_state(sim
, ARM_STATE_THUMB
);
507 sim
->set_state(sim
, ARM_STATE_ARM
);
510 sim
->set_reg_mode(sim
, instruction
.info
.load_store
.Rd
, load_value
);
513 /* load multiple instruction */
514 else if (instruction
.type
== ARM_LDM
) {
516 uint32_t Rn
= sim
->get_reg_mode(sim
, instruction
.info
.load_store_multiple
.Rn
);
517 uint32_t load_values
[16];
520 for (i
= 0; i
< 16; i
++) {
521 if (instruction
.info
.load_store_multiple
.register_list
& (1 << i
))
525 switch (instruction
.info
.load_store_multiple
.addressing_mode
) {
526 case 0: /* Increment after */
529 case 1: /* Increment before */
532 case 2: /* Decrement after */
533 Rn
= Rn
- (bits_set
* 4) + 4;
535 case 3: /* Decrement before */
536 Rn
= Rn
- (bits_set
* 4);
540 for (i
= 0; i
< 16; i
++) {
541 if (instruction
.info
.load_store_multiple
.register_list
& (1 << i
)) {
542 if ((!dry_run_pc
) || (i
== 15))
543 target_read_u32(target
, Rn
, &load_values
[i
]);
549 if (instruction
.info
.load_store_multiple
.register_list
& 0x8000) {
550 *dry_run_pc
= load_values
[15] & ~1;
556 if (instruction
.info
.load_store_multiple
.S
) {
557 if (instruction
.info
.load_store_multiple
.register_list
& 0x8000)
561 for (i
= 0; i
< 16; i
++) {
562 if (instruction
.info
.load_store_multiple
.register_list
& (1 << i
)) {
564 uint32_t val
= load_values
[i
];
565 sim
->set_reg_mode(sim
, i
, val
& ~1);
567 sim
->set_state(sim
, ARM_STATE_THUMB
);
569 sim
->set_state(sim
, ARM_STATE_ARM
);
571 sim
->set_reg_mode(sim
, i
, load_values
[i
]);
576 uint32_t spsr
= sim
->get_reg_mode(sim
, 16);
577 sim
->set_reg(sim
, ARMV4_5_CPSR
, spsr
);
580 /* base register writeback */
581 if (instruction
.info
.load_store_multiple
.W
)
582 sim
->set_reg_mode(sim
, instruction
.info
.load_store_multiple
.Rn
, Rn
);
585 if (instruction
.info
.load_store_multiple
.register_list
& 0x8000)
589 /* store multiple instruction */
590 else if (instruction
.type
== ARM_STM
) {
594 /* STM wont affect PC (advance by instruction size */
596 uint32_t Rn
= sim
->get_reg_mode(sim
,
597 instruction
.info
.load_store_multiple
.Rn
);
600 for (i
= 0; i
< 16; i
++) {
601 if (instruction
.info
.load_store_multiple
.register_list
& (1 << i
))
605 switch (instruction
.info
.load_store_multiple
.addressing_mode
) {
606 case 0: /* Increment after */
609 case 1: /* Increment before */
612 case 2: /* Decrement after */
613 Rn
= Rn
- (bits_set
* 4) + 4;
615 case 3: /* Decrement before */
616 Rn
= Rn
- (bits_set
* 4);
620 for (i
= 0; i
< 16; i
++) {
621 if (instruction
.info
.load_store_multiple
.register_list
& (1 << i
)) {
622 target_write_u32(target
, Rn
, sim
->get_reg_mode(sim
, i
));
627 /* base register writeback */
628 if (instruction
.info
.load_store_multiple
.W
)
629 sim
->set_reg_mode(sim
,
630 instruction
.info
.load_store_multiple
.Rn
, Rn
);
633 } else if (!dry_run_pc
) {
634 /* the instruction wasn't handled, but we're supposed to simulate it
636 LOG_ERROR("Unimplemented instruction, could not simulate it.");
641 *dry_run_pc
= current_pc
+ instruction_size
;
644 sim
->set_reg(sim
, 15, current_pc
+ instruction_size
);
650 static uint32_t armv4_5_get_reg(struct arm_sim_interface
*sim
, int reg
)
652 struct arm
*arm
= (struct arm
*)sim
->user_data
;
654 return buf_get_u32(arm
->core_cache
->reg_list
[reg
].value
, 0, 32);
657 static void armv4_5_set_reg(struct arm_sim_interface
*sim
, int reg
, uint32_t value
)
659 struct arm
*arm
= (struct arm
*)sim
->user_data
;
661 buf_set_u32(arm
->core_cache
->reg_list
[reg
].value
, 0, 32, value
);
664 static uint32_t armv4_5_get_reg_mode(struct arm_sim_interface
*sim
, int reg
)
666 struct arm
*arm
= (struct arm
*)sim
->user_data
;
668 return buf_get_u32(ARMV4_5_CORE_REG_MODE(arm
->core_cache
,
669 arm
->core_mode
, reg
).value
, 0, 32);
672 static void armv4_5_set_reg_mode(struct arm_sim_interface
*sim
, int reg
, uint32_t value
)
674 struct arm
*arm
= (struct arm
*)sim
->user_data
;
676 buf_set_u32(ARMV4_5_CORE_REG_MODE(arm
->core_cache
,
677 arm
->core_mode
, reg
).value
, 0, 32, value
);
680 static uint32_t armv4_5_get_cpsr(struct arm_sim_interface
*sim
, int pos
, int bits
)
682 struct arm
*arm
= (struct arm
*)sim
->user_data
;
684 return buf_get_u32(arm
->cpsr
->value
, pos
, bits
);
687 static enum arm_state
armv4_5_get_state(struct arm_sim_interface
*sim
)
689 struct arm
*arm
= (struct arm
*)sim
->user_data
;
691 return arm
->core_state
;
694 static void armv4_5_set_state(struct arm_sim_interface
*sim
, enum arm_state mode
)
696 struct arm
*arm
= (struct arm
*)sim
->user_data
;
698 arm
->core_state
= mode
;
701 static enum arm_mode
armv4_5_get_mode(struct arm_sim_interface
*sim
)
703 struct arm
*arm
= (struct arm
*)sim
->user_data
;
705 return arm
->core_mode
;
708 int arm_simulate_step(struct target
*target
, uint32_t *dry_run_pc
)
710 struct arm
*arm
= target_to_arm(target
);
711 struct arm_sim_interface sim
;
714 sim
.get_reg
= &armv4_5_get_reg
;
715 sim
.set_reg
= &armv4_5_set_reg
;
716 sim
.get_reg_mode
= &armv4_5_get_reg_mode
;
717 sim
.set_reg_mode
= &armv4_5_set_reg_mode
;
718 sim
.get_cpsr
= &armv4_5_get_cpsr
;
719 sim
.get_mode
= &armv4_5_get_mode
;
720 sim
.get_state
= &armv4_5_get_state
;
721 sim
.set_state
= &armv4_5_set_state
;
723 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)