- merged XScale branch back into trunk
[openocd.git] / src / target / arm_simulator.c
1 /***************************************************************************
2 * Copyright (C) 2006 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 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "target.h"
25 #include "armv4_5.h"
26 #include "arm_disassembler.h"
27 #include "arm_simulator.h"
28 #include "log.h"
29 #include "binarybuffer.h"
30
31 #include <string.h>
32
33 u32 arm_shift(u8 shift, u32 Rm, u32 shift_amount, u8 *carry)
34 {
35 u32 return_value;
36 shift_amount &= 0xff;
37
38 if (shift == 0x0) /* LSL */
39 {
40 if ((shift_amount > 0) && (shift_amount <= 32))
41 {
42 return_value = Rm << shift_amount;
43 *carry = Rm >> (32 - shift_amount);
44 }
45 else if (shift_amount > 32)
46 {
47 return_value = 0x0;
48 *carry = 0x0;
49 }
50 else /* (shift_amount == 0) */
51 {
52 return_value = Rm;
53 }
54 }
55 else if (shift == 0x1) /* LSR */
56 {
57 if ((shift_amount > 0) && (shift_amount <= 32))
58 {
59 return_value = Rm >> shift_amount;
60 *carry = (Rm >> (shift_amount - 1)) & 1;
61 }
62 else if (shift_amount > 32)
63 {
64 return_value = 0x0;
65 *carry = 0x0;
66 }
67 else /* (shift_amount == 0) */
68 {
69 return_value = Rm;
70 }
71 }
72 else if (shift == 0x2) /* ASR */
73 {
74 if ((shift_amount > 0) && (shift_amount <= 32))
75 {
76 /* right shifts of unsigned values are guaranteed to be logical (shift in zeroes)
77 * simulate an arithmetic shift (shift in signed-bit) by adding the signed-bit manually */
78 return_value = Rm >> shift_amount;
79 if (Rm & 0x80000000)
80 return_value |= 0xffffffff << (32 - shift_amount);
81 }
82 else if (shift_amount > 32)
83 {
84 if (Rm & 0x80000000)
85 {
86 return_value = 0xffffffff;
87 *carry = 0x1;
88 }
89 else
90 {
91 return_value = 0x0;
92 *carry = 0x0;
93 }
94 }
95 else /* (shift_amount == 0) */
96 {
97 return_value = Rm;
98 }
99 }
100 else if (shift == 0x3) /* ROR */
101 {
102 if (shift_amount == 0)
103 {
104 return_value = Rm;
105 }
106 else
107 {
108 shift_amount = shift_amount % 32;
109 return_value = (Rm >> shift_amount) | (Rm << (32 - shift_amount));
110 *carry = (return_value >> 31) & 0x1;
111 }
112 }
113 else if (shift == 0x4) /* RRX */
114 {
115 return_value = Rm >> 1;
116 if (*carry)
117 Rm |= 0x80000000;
118 *carry = Rm & 0x1;
119 }
120
121 return return_value;
122 }
123
124 u32 arm_shifter_operand(armv4_5_common_t *armv4_5, int variant, union arm_shifter_operand shifter_operand, u8 *shifter_carry_out)
125 {
126 u32 return_value;
127 int instruction_size;
128
129 if (armv4_5->core_state == ARMV4_5_STATE_ARM)
130 instruction_size = 4;
131 else
132 instruction_size = 2;
133
134 *shifter_carry_out = buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 29, 1);
135
136 if (variant == 0) /* 32-bit immediate */
137 {
138 return_value = shifter_operand.immediate.immediate;
139 }
140 else if (variant == 1) /* immediate shift */
141 {
142 u32 Rm = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, shifter_operand.immediate_shift.Rm).value, 0, 32);
143
144 /* adjust RM in case the PC is being read */
145 if (shifter_operand.immediate_shift.Rm == 15)
146 Rm += 2 * instruction_size;
147
148 return_value = arm_shift(shifter_operand.immediate_shift.shift, Rm, shifter_operand.immediate_shift.shift_imm, shifter_carry_out);
149 }
150 else if (variant == 2) /* register shift */
151 {
152 u32 Rm = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, shifter_operand.register_shift.Rm).value, 0, 32);
153 u32 Rs = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, shifter_operand.register_shift.Rs).value, 0, 32);
154
155 /* adjust RM in case the PC is being read */
156 if (shifter_operand.register_shift.Rm == 15)
157 Rm += 2 * instruction_size;
158
159 return_value = arm_shift(shifter_operand.immediate_shift.shift, Rm, Rs, shifter_carry_out);
160 }
161 else
162 {
163 ERROR("BUG: shifter_operand.variant not 0, 1 or 2");
164 return_value = 0xffffffff;
165 }
166
167 return return_value;
168 }
169
170 int pass_condition(u32 cpsr, u32 opcode)
171 {
172 switch ((opcode & 0xf0000000) >> 28)
173 {
174 case 0x0: /* EQ */
175 if (cpsr & 0x40000000)
176 return 1;
177 else
178 return 0;
179 case 0x1: /* NE */
180 if (!(cpsr & 0x40000000))
181 return 1;
182 else
183 return 0;
184 case 0x2: /* CS */
185 if (cpsr & 0x20000000)
186 return 1;
187 else
188 return 0;
189 case 0x3: /* CC */
190 if (!(cpsr & 0x20000000))
191 return 1;
192 else
193 return 0;
194 case 0x4: /* MI */
195 if (cpsr & 0x80000000)
196 return 1;
197 else
198 return 0;
199 case 0x5: /* PL */
200 if (!(cpsr & 0x80000000))
201 return 1;
202 else
203 return 0;
204 case 0x6: /* VS */
205 if (cpsr & 0x10000000)
206 return 1;
207 else
208 return 0;
209 case 0x7: /* VC */
210 if (!(cpsr & 0x10000000))
211 return 1;
212 else
213 return 0;
214 case 0x8: /* HI */
215 if ((cpsr & 0x20000000) && !(cpsr & 0x40000000))
216 return 1;
217 else
218 return 0;
219 case 0x9: /* LS */
220 if (!(cpsr & 0x20000000) || (cpsr & 0x40000000))
221 return 1;
222 else
223 return 0;
224 case 0xa: /* GE */
225 if (((cpsr & 0x80000000) && (cpsr & 0x10000000))
226 || (!(cpsr & 0x80000000) && !(cpsr & 0x10000000)))
227 return 1;
228 else
229 return 0;
230 case 0xb: /* LT */
231 if (((cpsr & 0x80000000) && !(cpsr & 0x10000000))
232 || (!(cpsr & 0x80000000) && (cpsr & 0x10000000)))
233 return 1;
234 else
235 return 0;
236 case 0xc: /* GT */
237 if (!(cpsr & 0x40000000) &&
238 (((cpsr & 0x80000000) && (cpsr & 0x10000000))
239 || (!(cpsr & 0x80000000) && !(cpsr & 0x10000000))))
240 return 1;
241 else
242 return 0;
243 case 0xd: /* LE */
244 if ((cpsr & 0x40000000) &&
245 (((cpsr & 0x80000000) && !(cpsr & 0x10000000))
246 || (!(cpsr & 0x80000000) && (cpsr & 0x10000000))))
247 return 1;
248 else
249 return 0;
250 case 0xe:
251 case 0xf:
252 return 1;
253
254 }
255
256 ERROR("BUG: should never get here");
257 return 0;
258 }
259
260 /* simulate a single step (if possible)
261 * if the dry_run_pc argument is provided, no state is changed,
262 * but the new pc is stored in the variable pointed at by the argument
263 */
264 int arm_simulate_step(target_t *target, u32 *dry_run_pc)
265 {
266 armv4_5_common_t *armv4_5 = target->arch_info;
267 u32 opcode;
268 u32 current_pc = buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32);
269 arm_instruction_t instruction;
270 int instruction_size;
271
272 if (armv4_5->core_state == ARMV4_5_STATE_ARM)
273 {
274 /* get current instruction, and identify it */
275 target_read_u32(target, current_pc, &opcode);
276 arm_evaluate_opcode(opcode, current_pc, &instruction);
277 instruction_size = 4;
278 }
279 else
280 {
281 /* TODO: add support for Thumb instruction set */
282 instruction_size = 2;
283 }
284
285 /* check condition code */
286 if (!pass_condition(buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32), opcode))
287 {
288 if (dry_run_pc)
289 {
290 *dry_run_pc = current_pc + instruction_size;
291 }
292 else
293 {
294 buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, current_pc + instruction_size);
295 }
296
297 return ERROR_OK;
298 }
299
300 /* examine instruction type */
301
302 /* branch instructions */
303 if ((instruction.type >= ARM_B) && (instruction.type <= ARM_BLX))
304 {
305 u32 target;
306
307 if (instruction.info.b_bl_bx_blx.reg_operand == -1)
308 {
309 target = instruction.info.b_bl_bx_blx.target_address;
310 }
311 else
312 {
313 target = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.b_bl_bx_blx.reg_operand).value, 0, 32);
314 }
315
316 if (dry_run_pc)
317 {
318 *dry_run_pc = target;
319 return ERROR_OK;
320 }
321 else
322 {
323 if (instruction.type == ARM_B)
324 {
325 buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, target);
326 }
327 else if (instruction.type == ARM_BL)
328 {
329 u32 old_pc = buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32);
330 buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 14).value, 0, 32, old_pc + 4);
331 buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, target);
332 }
333 else if (instruction.type == ARM_BX)
334 {
335 if (target & 0x1)
336 {
337 armv4_5->core_state = ARMV4_5_STATE_THUMB;
338 }
339 else
340 {
341 armv4_5->core_state = ARMV4_5_STATE_ARM;
342 }
343 buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, target & 0xfffffffe);
344 }
345 else if (instruction.type == ARM_BLX)
346 {
347 u32 old_pc = buf_get_u32(armv4_5->core_cache->reg_list[15].value, 0, 32);
348 buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 14).value, 0, 32, old_pc + 4);
349
350 if (target & 0x1)
351 {
352 armv4_5->core_state = ARMV4_5_STATE_THUMB;
353 }
354 else
355 {
356 armv4_5->core_state = ARMV4_5_STATE_ARM;
357 }
358 buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, target & 0xfffffffe);
359 }
360
361 return ERROR_OK;
362 }
363 }
364 /* data processing instructions, except compare instructions (CMP, CMN, TST, TEQ) */
365 else if (((instruction.type >= ARM_AND) && (instruction.type <= ARM_RSC))
366 || ((instruction.type >= ARM_ORR) && (instruction.type <= ARM_MVN)))
367 {
368 u32 Rd, Rn, shifter_operand;
369 u8 C = buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 29, 1);
370 u8 carry_out;
371
372 Rd = 0x0;
373 Rn = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.data_proc.Rn).value, 0, 32);
374 shifter_operand = arm_shifter_operand(armv4_5, instruction.info.data_proc.variant, instruction.info.data_proc.shifter_operand, &carry_out);
375
376 /* adjust Rn in case the PC is being read */
377 if (instruction.info.data_proc.Rn == 15)
378 Rn += 2 * instruction_size;
379
380 if (instruction.type == ARM_AND)
381 Rd = Rn & shifter_operand;
382 else if (instruction.type == ARM_EOR)
383 Rd = Rn ^ shifter_operand;
384 else if (instruction.type == ARM_SUB)
385 Rd = Rn - shifter_operand;
386 else if (instruction.type == ARM_RSB)
387 Rd = shifter_operand - Rn;
388 else if (instruction.type == ARM_ADD)
389 Rd = Rn + shifter_operand;
390 else if (instruction.type == ARM_ADC)
391 Rd = Rn + shifter_operand + (C & 1);
392 else if (instruction.type == ARM_SBC)
393 Rd = Rn - shifter_operand - (C & 1) ? 0 : 1;
394 else if (instruction.type == ARM_RSC)
395 Rd = shifter_operand - Rn - (C & 1) ? 0 : 1;
396 else if (instruction.type == ARM_ORR)
397 Rd = Rn | shifter_operand;
398 else if (instruction.type == ARM_BIC)
399 Rd = Rn & ~(shifter_operand);
400 else if (instruction.type == ARM_MOV)
401 Rd = shifter_operand;
402 else if (instruction.type == ARM_MVN)
403 Rd = ~shifter_operand;
404
405 if (dry_run_pc)
406 {
407 if (instruction.info.data_proc.Rd == 15)
408 {
409 *dry_run_pc = Rd;
410 return ERROR_OK;
411 }
412 else
413 {
414 *dry_run_pc = current_pc + instruction_size;
415 }
416
417 return ERROR_OK;
418 }
419 else
420 {
421 buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.data_proc.Rd).value, 0, 32, Rd);
422 WARNING("no updating of flags yet");
423
424 if (instruction.info.data_proc.Rd == 15)
425 return ERROR_OK;
426 }
427 }
428 /* compare instructions (CMP, CMN, TST, TEQ) */
429 else if ((instruction.type >= ARM_TST) && (instruction.type <= ARM_CMN))
430 {
431 if (dry_run_pc)
432 {
433 *dry_run_pc = current_pc + instruction_size;
434 return ERROR_OK;
435 }
436 else
437 {
438 WARNING("no updating of flags yet");
439 }
440 }
441 /* load register instructions */
442 else if ((instruction.type >= ARM_LDR) && (instruction.type <= ARM_LDRSH))
443 {
444 u32 load_address, modified_address, load_value;
445 u32 Rn = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.load_store.Rn).value, 0, 32);
446
447 /* adjust Rn in case the PC is being read */
448 if (instruction.info.load_store.Rn == 15)
449 Rn += 2 * instruction_size;
450
451 if (instruction.info.load_store.offset_mode == 0)
452 {
453 if (instruction.info.load_store.U)
454 modified_address = Rn + instruction.info.load_store.offset.offset;
455 else
456 modified_address = Rn - instruction.info.load_store.offset.offset;
457 }
458 else if (instruction.info.load_store.offset_mode == 1)
459 {
460 u32 offset;
461 u32 Rm = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.load_store.offset.reg.Rm).value, 0, 32);
462 u8 shift = instruction.info.load_store.offset.reg.shift;
463 u8 shift_imm = instruction.info.load_store.offset.reg.shift_imm;
464 u8 carry = buf_get_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 29, 1);
465
466 offset = arm_shift(shift, Rm, shift_imm, &carry);
467
468 if (instruction.info.load_store.U)
469 modified_address = Rn + offset;
470 else
471 modified_address = Rn - offset;
472 }
473 else
474 {
475 ERROR("BUG: offset_mode neither 0 (offset) nor 1 (scaled register)");
476 }
477
478 if (instruction.info.load_store.index_mode == 0)
479 {
480 /* offset mode
481 * we load from the modified address, but don't change the base address register */
482 load_address = modified_address;
483 modified_address = Rn;
484 }
485 else if (instruction.info.load_store.index_mode == 1)
486 {
487 /* pre-indexed mode
488 * we load from the modified address, and write it back to the base address register */
489 load_address = modified_address;
490 }
491 else if (instruction.info.load_store.index_mode == 2)
492 {
493 /* post-indexed mode
494 * we load from the unmodified address, and write the modified address back */
495 load_address = Rn;
496 }
497
498 target_read_u32(target, load_address, &load_value);
499
500 if (dry_run_pc)
501 {
502 if (instruction.info.load_store.Rd == 15)
503 {
504 *dry_run_pc = load_value;
505 return ERROR_OK;
506 }
507 else
508 {
509 *dry_run_pc = current_pc + instruction_size;
510 }
511
512 return ERROR_OK;
513 }
514 else
515 {
516 if ((instruction.info.load_store.index_mode == 1) ||
517 (instruction.info.load_store.index_mode == 2))
518 {
519 buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.load_store.Rn).value, 0, 32, modified_address);
520 }
521 buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.load_store.Rd).value, 0, 32, load_value);
522
523 if (instruction.info.load_store.Rd == 15)
524 return ERROR_OK;
525 }
526 }
527 /* load multiple instruction */
528 else if (instruction.type == ARM_LDM)
529 {
530 int i;
531 u32 Rn = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.load_store_multiple.Rn).value, 0, 32);
532 u32 load_values[16];
533 int bits_set = 0;
534
535 for (i = 0; i < 16; i++)
536 {
537 if (instruction.info.load_store_multiple.register_list & (1 << i))
538 bits_set++;
539 }
540
541 switch (instruction.info.load_store_multiple.addressing_mode)
542 {
543 case 0: /* Increment after */
544 Rn = Rn;
545 break;
546 case 1: /* Increment before */
547 Rn = Rn + 4;
548 break;
549 case 2: /* Decrement after */
550 Rn = Rn - (bits_set * 4) + 4;
551 break;
552 case 3: /* Decrement before */
553 Rn = Rn - (bits_set * 4);
554 break;
555 }
556
557 for (i = 0; i < 16; i++)
558 {
559 if (instruction.info.load_store_multiple.register_list & (1 << i))
560 {
561 target_read_u32(target, Rn, &load_values[i]);
562 Rn += 4;
563 }
564 }
565
566 if (dry_run_pc)
567 {
568 if (instruction.info.load_store_multiple.register_list & 0x8000)
569 {
570 *dry_run_pc = load_values[15];
571 return ERROR_OK;
572 }
573 }
574 else
575 {
576 enum armv4_5_mode mode = armv4_5->core_mode;
577 int update_cpsr = 0;
578
579 if (instruction.info.load_store_multiple.S)
580 {
581 if (instruction.info.load_store_multiple.register_list & 0x8000)
582 update_cpsr = 1;
583 else
584 mode = ARMV4_5_MODE_USR;
585 }
586
587 for (i = 0; i < 16; i++)
588 {
589 if (instruction.info.load_store_multiple.register_list & (1 << i))
590 {
591 buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, mode, i).value, 0, 32, load_values[i]);
592 }
593 }
594
595 if (update_cpsr)
596 {
597 u32 spsr = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, 16).value, 0, 32);
598 buf_set_u32(armv4_5->core_cache->reg_list[ARMV4_5_CPSR].value, 0, 32, spsr);
599 }
600
601 /* base register writeback */
602 if (instruction.info.load_store_multiple.W)
603 buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.load_store_multiple.Rn).value, 0, 32, Rn);
604
605 if (instruction.info.load_store_multiple.register_list & 0x8000)
606 return ERROR_OK;
607 }
608 }
609 /* store multiple instruction */
610 else if (instruction.type == ARM_STM)
611 {
612 int i;
613
614 if (dry_run_pc)
615 {
616 /* STM wont affect PC (advance by instruction size */
617 }
618 else
619 {
620 u32 Rn = buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.load_store_multiple.Rn).value, 0, 32);
621 int bits_set = 0;
622 enum armv4_5_mode mode = armv4_5->core_mode;
623
624 for (i = 0; i < 16; i++)
625 {
626 if (instruction.info.load_store_multiple.register_list & (1 << i))
627 bits_set++;
628 }
629
630 if (instruction.info.load_store_multiple.S)
631 {
632 mode = ARMV4_5_MODE_USR;
633 }
634
635 switch (instruction.info.load_store_multiple.addressing_mode)
636 {
637 case 0: /* Increment after */
638 Rn = Rn;
639 break;
640 case 1: /* Increment before */
641 Rn = Rn + 4;
642 break;
643 case 2: /* Decrement after */
644 Rn = Rn - (bits_set * 4) + 4;
645 break;
646 case 3: /* Decrement before */
647 Rn = Rn - (bits_set * 4);
648 break;
649 }
650
651 for (i = 0; i < 16; i++)
652 {
653 if (instruction.info.load_store_multiple.register_list & (1 << i))
654 {
655 target_write_u32(target, Rn, buf_get_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, i).value, 0, 32));
656 Rn += 4;
657 }
658 }
659
660 /* base register writeback */
661 if (instruction.info.load_store_multiple.W)
662 buf_set_u32(ARMV4_5_CORE_REG_MODE(armv4_5->core_cache, armv4_5->core_mode, instruction.info.load_store_multiple.Rn).value, 0, 32, Rn);
663
664 }
665 }
666 else if (!dry_run_pc)
667 {
668 /* the instruction wasn't handled, but we're supposed to simulate it
669 */
670 return ERROR_ARM_SIMULATOR_NOT_IMPLEMENTED;
671 }
672
673 if (dry_run_pc)
674 {
675 *dry_run_pc = current_pc + instruction_size;
676 return ERROR_OK;
677 }
678 else
679 {
680 buf_set_u32(armv4_5->core_cache->reg_list[15].value, 0, 32, current_pc + instruction_size);
681 return ERROR_OK;
682 }
683
684 }

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)