arm946e: cleanup C0.C cache type reg access
[openocd.git] / src / target / arm946e.c
1 /***************************************************************************
2 * Copyright (C) 2005 by Dominic Rath *
3 * Dominic.Rath@gmx.de *
4 * *
5 * Copyright (C) 2008 by Spencer Oliver *
6 * spen@spen-soft.co.uk *
7 * *
8 * Copyright (C) 2010 by Drasko DRASKOVIC *
9 * drasko.draskovic@gmail.com *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License for more details. *
20 * *
21 * You should have received a copy of the GNU General Public License *
22 * along with this program; if not, write to the *
23 * Free Software Foundation, Inc., *
24 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
25 ***************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include "arm946e.h"
32 #include "target_type.h"
33 #include "arm_opcodes.h"
34
35 #include "breakpoints.h"
36
37 #if 0
38 #define _DEBUG_INSTRUCTION_EXECUTION_
39 #endif
40
41 #define NB_CACHE_WAYS 4
42
43 #define CP15_CTL 0x02
44 #define CP15_CTL_DCACHE (1<<2)
45 #define CP15_CTL_ICACHE (1<<12)
46
47 /**
48 * flag to give info about cache manipulation during debug :
49 * "0" - cache lines are invalidated "on the fly", for affected addresses.
50 * This is prefered from performance point of view.
51 * "1" - cache is invalidated and switched off on debug_entry, and switched back on on restore.
52 * It is kept off during debugging.
53 */
54 static uint8_t arm946e_preserve_cache;
55
56 int arm946e_post_debug_entry(struct target *target);
57 void arm946e_pre_restore_context(struct target *target);
58 static int arm946e_read_cp15(struct target *target, int reg_addr, uint32_t *value);
59
60 int arm946e_init_arch_info(struct target *target,
61 struct arm946e_common *arm946e,
62 struct jtag_tap *tap)
63 {
64 struct arm7_9_common *arm7_9 = &arm946e->arm7_9_common;
65
66 /* initialize arm7/arm9 specific info (including armv4_5) */
67 arm9tdmi_init_arch_info(target, arm7_9, tap);
68
69 arm946e->common_magic = ARM946E_COMMON_MAGIC;
70
71 /**
72 * The ARM946E-S implements the ARMv5TE architecture which
73 * has the BKPT instruction, so we don't have to use a watchpoint comparator
74 */
75 arm7_9->arm_bkpt = ARMV5_BKPT(0x0);
76 arm7_9->thumb_bkpt = ARMV5_T_BKPT(0x0) & 0xffff;
77
78
79 arm7_9->post_debug_entry = arm946e_post_debug_entry;
80 arm7_9->pre_restore_context = arm946e_pre_restore_context;
81
82 /**
83 * disabling linefills leads to lockups, so keep them enabled for now
84 * this doesn't affect correctness, but might affect timing issues, if
85 * important data is evicted from the cache during the debug session
86 */
87 arm946e_preserve_cache = 0;
88
89 /* override hw single-step capability from ARM9TDMI */
90 /* arm7_9->has_single_step = 1; */
91
92 return ERROR_OK;
93 }
94
95 static int arm946e_target_create(struct target *target, Jim_Interp *interp)
96 {
97 struct arm946e_common *arm946e = calloc(1, sizeof(struct arm946e_common));
98
99 arm946e_init_arch_info(target, arm946e, target->tap);
100
101 return ERROR_OK;
102 }
103
104 static int arm946e_verify_pointer(struct command_context *cmd_ctx,
105 struct arm946e_common *arm946e)
106 {
107 if (arm946e->common_magic != ARM946E_COMMON_MAGIC) {
108 command_print(cmd_ctx, "target is not an ARM946");
109 return ERROR_TARGET_INVALID;
110 }
111 return ERROR_OK;
112 }
113
114 /*
115 * REVISIT: The "read_cp15" and "write_cp15" commands could hook up
116 * to eventual mrc() and mcr() routines ... the reg_addr values being
117 * constructed (for CP15 only) from Opcode_1, Opcode_2, and CRn values.
118 * See section 7.3 of the ARM946E-S TRM.
119 */
120 static int arm946e_read_cp15(struct target *target, int reg_addr, uint32_t *value)
121 {
122 int retval = ERROR_OK;
123 struct arm7_9_common *arm7_9 = target_to_arm7_9(target);
124 struct arm_jtag *jtag_info = &arm7_9->jtag_info;
125 struct scan_field fields[3];
126 uint8_t reg_addr_buf = reg_addr & 0x3f;
127 uint8_t nr_w_buf = 0;
128
129 retval = arm_jtag_scann(jtag_info, 0xf, TAP_IDLE);
130 if (retval != ERROR_OK)
131 return retval;
132 retval = arm_jtag_set_instr(jtag_info, jtag_info->intest_instr, NULL, TAP_IDLE);
133 if (retval != ERROR_OK)
134 return retval;
135
136 fields[0].num_bits = 32;
137 /* REVISIT: table 7-2 shows that bits 31-31 need to be
138 * specified for accessing BIST registers ...
139 */
140 fields[0].out_value = NULL;
141 fields[0].in_value = NULL;
142
143 fields[1].num_bits = 6;
144 fields[1].out_value = &reg_addr_buf;
145 fields[1].in_value = NULL;
146
147 fields[2].num_bits = 1;
148 fields[2].out_value = &nr_w_buf;
149 fields[2].in_value = NULL;
150
151 jtag_add_dr_scan(jtag_info->tap, 3, fields, TAP_IDLE);
152
153 fields[0].in_value = (uint8_t *)value;
154 jtag_add_dr_scan(jtag_info->tap, 3, fields, TAP_IDLE);
155
156 jtag_add_callback(arm_le_to_h_u32, (jtag_callback_data_t)value);
157
158 #ifdef _DEBUG_INSTRUCTION_EXECUTION_
159 LOG_DEBUG("addr: 0x%x value: %8.8x", reg_addr, *value);
160 #endif
161
162 retval = jtag_execute_queue();
163 if (retval != ERROR_OK)
164 return retval;
165
166 return ERROR_OK;
167 }
168
169 int arm946e_write_cp15(struct target *target, int reg_addr, uint32_t value)
170 {
171 int retval = ERROR_OK;
172 struct arm7_9_common *arm7_9 = target_to_arm7_9(target);
173 struct arm_jtag *jtag_info = &arm7_9->jtag_info;
174 struct scan_field fields[3];
175 uint8_t reg_addr_buf = reg_addr & 0x3f;
176 uint8_t nr_w_buf = 1;
177 uint8_t value_buf[4];
178
179 buf_set_u32(value_buf, 0, 32, value);
180
181 retval = arm_jtag_scann(jtag_info, 0xf, TAP_IDLE);
182 if (retval != ERROR_OK)
183 return retval;
184 retval = arm_jtag_set_instr(jtag_info, jtag_info->intest_instr, NULL, TAP_IDLE);
185 if (retval != ERROR_OK)
186 return retval;
187
188 fields[0].num_bits = 32;
189 fields[0].out_value = value_buf;
190 fields[0].in_value = NULL;
191
192 fields[1].num_bits = 6;
193 fields[1].out_value = &reg_addr_buf;
194 fields[1].in_value = NULL;
195
196 fields[2].num_bits = 1;
197 fields[2].out_value = &nr_w_buf;
198 fields[2].in_value = NULL;
199
200 jtag_add_dr_scan(jtag_info->tap, 3, fields, TAP_IDLE);
201
202 #ifdef _DEBUG_INSTRUCTION_EXECUTION_
203 LOG_DEBUG("addr: 0x%x value: %8.8x", reg_addr, value);
204 #endif
205
206 retval = jtag_execute_queue();
207 if (retval != ERROR_OK)
208 return retval;
209
210 return ERROR_OK;
211 }
212
213 #define GET_ICACHE_SIZE 6
214 #define GET_DCACHE_SIZE 18
215
216 /*
217 * \param target struct target pointer
218 * \param idsel select GET_ICACHE_SIZE or GET_DCACHE_SIZE
219 * \returns cache size, given in bytes
220 */
221 static uint32_t arm946e_cp15_get_csize(struct target *target, int idsel)
222 {
223 struct arm946e_common *arm946e = target_to_arm946(target);
224 uint32_t csize = arm946e->cp15_cache_info;
225 if (csize == 0) {
226 if (arm946e_read_cp15(target, 0x01, &csize) == ERROR_OK)
227 arm946e->cp15_cache_info = csize;
228 }
229 if (csize & (1<<(idsel-4))) /* cache absent */
230 return 0;
231 csize = (csize >> idsel) & 0x0F;
232 return csize ? 1 << (12 + (csize-3)) : 0;
233 }
234
235 uint32_t arm946e_invalidate_whole_dcache(struct target *target)
236 {
237 uint32_t csize = arm946e_cp15_get_csize(target, GET_DCACHE_SIZE);
238 if (csize == 0)
239 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
240
241 /* One line (index) is 32 bytes (8 words) long, 4-way assoc
242 * ARM DDI 0201D, Section 3.3.5
243 */
244 int nb_idx = (csize / (4*8*NB_CACHE_WAYS)); /* gives nb of lines (indexes) in the cache */
245
246 /* Loop for all segmentde (i.e. ways) */
247 uint32_t seg;
248 for (seg = 0; seg < NB_CACHE_WAYS; seg++) {
249 /* Loop for all indexes */
250 int idx;
251 for (idx = 0; idx < nb_idx; idx++) {
252 /* Form and write cp15 index (segment + line idx) */
253 uint32_t cp15_idx = seg << 30 | idx << 5;
254 int retval = arm946e_write_cp15(target, 0x3a, cp15_idx);
255 if (retval != ERROR_OK) {
256 LOG_DEBUG("ERROR writing index");
257 return retval;
258 }
259
260 /* Read dtag */
261 uint32_t dtag;
262 arm946e_read_cp15(target, 0x16, (uint32_t *) &dtag);
263
264 /* Check cache line VALID bit */
265 if (!(dtag >> 4 & 0x1))
266 continue;
267
268 /* Clean data cache line */
269 retval = arm946e_write_cp15(target, 0x35, 0x1);
270 if (retval != ERROR_OK) {
271 LOG_DEBUG("ERROR cleaning cache line");
272 return retval;
273 }
274
275 /* Flush data cache line */
276 retval = arm946e_write_cp15(target, 0x1a, 0x1);
277 if (retval != ERROR_OK) {
278 LOG_DEBUG("ERROR flushing cache line");
279 return retval;
280 }
281 }
282 }
283
284 return ERROR_OK;
285 }
286
287 uint32_t arm946e_invalidate_whole_icache(struct target *target)
288 {
289 /* Check cache presence before flushing - avoid undefined behavior */
290 uint32_t csize = arm946e_cp15_get_csize(target, GET_ICACHE_SIZE);
291 if (csize == 0)
292 return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
293
294 LOG_DEBUG("FLUSHING I$");
295 /**
296 * Invalidate (flush) I$
297 * mcr 15, 0, r0, cr7, cr5, {0}
298 */
299 int retval = arm946e_write_cp15(target, 0x0f, 0x1);
300 if (retval != ERROR_OK) {
301 LOG_DEBUG("ERROR flushing I$");
302 return retval;
303 }
304
305 return ERROR_OK;
306 }
307
308 int arm946e_post_debug_entry(struct target *target)
309 {
310 uint32_t ctr_reg = 0x0;
311 uint32_t retval = ERROR_OK;
312 struct arm946e_common *arm946e = target_to_arm946(target);
313
314 /* See if CACHES are enabled, and save that info
315 * in the context bits, so that arm946e_pre_restore_context() can use them */
316 arm946e_read_cp15(target, CP15_CTL, (uint32_t *) &ctr_reg);
317
318 /* Save control reg in the context */
319 arm946e->cp15_control_reg = ctr_reg;
320
321 if (arm946e_preserve_cache) {
322 if (ctr_reg & CP15_CTL_DCACHE) {
323 /* Clean and flush D$ */
324 arm946e_invalidate_whole_dcache(target);
325
326 /* Disable D$ */
327 ctr_reg &= ~CP15_CTL_DCACHE;
328 }
329
330 if (ctr_reg & CP15_CTL_ICACHE) {
331 /* Flush I$ */
332 arm946e_invalidate_whole_icache(target);
333
334 /* Disable I$ */
335 ctr_reg &= ~CP15_CTL_ICACHE;
336 }
337
338 /* Write the new configuration */
339 retval = arm946e_write_cp15(target, CP15_CTL, ctr_reg);
340 if (retval != ERROR_OK) {
341 LOG_DEBUG("ERROR disabling cache");
342 return retval;
343 }
344 } /* if preserve_cache */
345
346 return ERROR_OK;
347 }
348
349 void arm946e_pre_restore_context(struct target *target)
350 {
351 uint32_t ctr_reg = 0x0;
352 uint32_t retval;
353
354 if (arm946e_preserve_cache) {
355 struct arm946e_common *arm946e = target_to_arm946(target);
356 /* Get the contents of the CTR reg */
357 arm946e_read_cp15(target, CP15_CTL, (uint32_t *) &ctr_reg);
358
359 /**
360 * Read-modify-write CP15 control
361 * to reenable I/D-cache operation
362 * NOTE: It is not possible to disable cache by CP15.
363 * if arm946e_preserve_cache debugging flag enabled.
364 */
365 ctr_reg |= arm946e->cp15_control_reg & (CP15_CTL_DCACHE|CP15_CTL_ICACHE);
366
367 /* Write the new configuration */
368 retval = arm946e_write_cp15(target, CP15_CTL, ctr_reg);
369 if (retval != ERROR_OK)
370 LOG_DEBUG("ERROR enabling cache");
371 } /* if preserve_cache */
372 }
373
374 uint32_t arm946e_invalidate_dcache(struct target *target, uint32_t address,
375 uint32_t size, uint32_t count)
376 {
377 uint32_t cur_addr = 0x0;
378 uint32_t cp15_idx, set, way, dtag;
379 uint32_t i = 0;
380 int retval;
381
382 for (i = 0; i < count*size; i++) {
383 cur_addr = address + i;
384
385
386 set = (cur_addr >> 5) & 0xff; /* set field is 8 bits long */
387
388 for (way = 0; way < NB_CACHE_WAYS; way++) {
389 /**
390 * Find if the affected address is kept in the cache.
391 * Because JTAG Scan Chain 15 offers limited approach,
392 * we have to loop through all cache ways (segments) and
393 * read cache tags, then compare them with with address.
394 */
395
396 /* Form and write cp15 index (segment + line idx) */
397 cp15_idx = way << 30 | set << 5;
398 retval = arm946e_write_cp15(target, 0x3a, cp15_idx);
399 if (retval != ERROR_OK) {
400 LOG_DEBUG("ERROR writing index");
401 return retval;
402 }
403
404 /* Read dtag */
405 arm946e_read_cp15(target, 0x16, (uint32_t *) &dtag);
406
407 /* Check cache line VALID bit */
408 if (!(dtag >> 4 & 0x1))
409 continue;
410
411 /* If line is valid and corresponds to affected address - invalidate it */
412 if (dtag >> 5 == cur_addr >> 5) {
413 /* Clean data cache line */
414 retval = arm946e_write_cp15(target, 0x35, 0x1);
415 if (retval != ERROR_OK) {
416 LOG_DEBUG("ERROR cleaning cache line");
417 return retval;
418 }
419
420 /* Flush data cache line */
421 retval = arm946e_write_cp15(target, 0x1c, 0x1);
422 if (retval != ERROR_OK) {
423 LOG_DEBUG("ERROR flushing cache line");
424 return retval;
425 }
426
427 break;
428 }
429 } /* loop through all 4 ways */
430 } /* loop through all addresses */
431
432 return ERROR_OK;
433 }
434
435 uint32_t arm946e_invalidate_icache(struct target *target, uint32_t address,
436 uint32_t size, uint32_t count)
437 {
438 uint32_t cur_addr = 0x0;
439 uint32_t cp15_idx, set, way, itag;
440 uint32_t i = 0;
441 int retval;
442
443 for (i = 0; i < count*size; i++) {
444 cur_addr = address + i;
445
446 set = (cur_addr >> 5) & 0xff; /* set field is 8 bits long */
447
448 for (way = 0; way < NB_CACHE_WAYS; way++) {
449 /* Form and write cp15 index (segment + line idx) */
450 cp15_idx = way << 30 | set << 5;
451 retval = arm946e_write_cp15(target, 0x3a, cp15_idx);
452 if (retval != ERROR_OK) {
453 LOG_DEBUG("ERROR writing index");
454 return retval;
455 }
456
457 /* Read itag */
458 arm946e_read_cp15(target, 0x17, (uint32_t *) &itag);
459
460 /* Check cache line VALID bit */
461 if (!(itag >> 4 & 0x1))
462 continue;
463
464 /* If line is valid and corresponds to affected address - invalidate it */
465 if (itag >> 5 == cur_addr >> 5) {
466 /* Flush I$ line */
467 retval = arm946e_write_cp15(target, 0x1d, 0x0);
468 if (retval != ERROR_OK) {
469 LOG_DEBUG("ERROR flushing cache line");
470 return retval;
471 }
472
473 break;
474 }
475 } /* way loop */
476 } /* addr loop */
477
478 return ERROR_OK;
479 }
480
481 /** Writes a buffer, in the specified word size, with current MMU settings. */
482 int arm946e_write_memory(struct target *target, uint32_t address,
483 uint32_t size, uint32_t count, const uint8_t *buffer)
484 {
485 int retval;
486
487 LOG_DEBUG("-");
488
489 struct arm946e_common *arm946e = target_to_arm946(target);
490 /* Invalidate D$ if it is ON */
491 if (!arm946e_preserve_cache && (arm946e->cp15_control_reg & CP15_CTL_DCACHE))
492 arm946e_invalidate_dcache(target, address, size, count);
493
494 /**
495 * Write memory
496 */
497 retval = arm7_9_write_memory(target, address, size, count, buffer);
498 if (retval != ERROR_OK)
499 return retval;
500
501 /* *
502 * Invalidate I$ if it is ON.
503 *
504 * D$ has been cleaned and flushed before mem write thus forcing it to behave like write-through,
505 * because arm7_9_write_memory() has seen non-valid bit in D$
506 * and wrote data into physical RAM (without touching or allocating the cache line).
507 * From ARM946ES Technical Reference Manual we can see that it uses "allocate on read-miss"
508 * policy for both I$ and D$ (Chapter 3.2 and 3.3)
509 *
510 * Explanation :
511 * "ARM system developer's guide: designing and optimizing system software" by
512 * Andrew N. Sloss, Dominic Symes and Chris Wright,
513 * Chapter 12.3.3 Allocating Policy on a Cache Miss :
514 * A read allocate on cache miss policy allocates a cache line only during a read from main memory.
515 * If the victim cache line contains valid data, then it is written to main memory before the cache line
516 * is filled with new data.
517 * Under this strategy, a write of new data to memory does not update the contents of the cache memory
518 * unless a cache line was allocated on a previous read from main memory.
519 * If the cache line contains valid data, then the write updates the cache and may update the main memory if
520 * the cache write policy is write-through.
521 * If the data is not in the cache, the controller writes to main memory only.
522 */
523 if (!arm946e_preserve_cache && (arm946e->cp15_control_reg & CP15_CTL_ICACHE))
524 arm946e_invalidate_icache(target, address, size, count);
525
526 return ERROR_OK;
527
528 }
529
530 int arm946e_read_memory(struct target *target, uint32_t address,
531 uint32_t size, uint32_t count, uint8_t *buffer)
532 {
533 int retval;
534
535 LOG_DEBUG("-");
536
537 retval = arm7_9_read_memory(target, address, size, count, buffer);
538 if (retval != ERROR_OK)
539 return retval;
540
541 return ERROR_OK;
542 }
543
544
545 COMMAND_HANDLER(arm946e_handle_cp15_command)
546 {
547 int retval;
548 struct target *target = get_current_target(CMD_CTX);
549 struct arm946e_common *arm946e = target_to_arm946(target);
550
551 retval = arm946e_verify_pointer(CMD_CTX, arm946e);
552 if (retval != ERROR_OK)
553 return retval;
554
555 if (target->state != TARGET_HALTED) {
556 command_print(CMD_CTX, "target must be stopped for \"%s\" command", CMD_NAME);
557 return ERROR_OK;
558 }
559
560 /* one or more argument, access a single register (write if second argument is given */
561 if (CMD_ARGC >= 1) {
562 uint32_t address;
563 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], address);
564
565 if (CMD_ARGC == 1) {
566 uint32_t value;
567 retval = arm946e_read_cp15(target, address, &value);
568 if (retval != ERROR_OK) {
569 command_print(CMD_CTX, "couldn't access reg %" PRIi32, address);
570 return ERROR_OK;
571 }
572 retval = jtag_execute_queue();
573 if (retval != ERROR_OK)
574 return retval;
575
576 command_print(CMD_CTX, "%" PRIi32 ": %8.8" PRIx32, address, value);
577 } else if (CMD_ARGC == 2) {
578 uint32_t value;
579 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], value);
580 retval = arm946e_write_cp15(target, address, value);
581 if (retval != ERROR_OK) {
582 command_print(CMD_CTX, "couldn't access reg %" PRIi32, address);
583 return ERROR_OK;
584 }
585 command_print(CMD_CTX, "%" PRIi32 ": %8.8" PRIx32, address, value);
586 }
587 }
588
589 return ERROR_OK;
590 }
591
592 static const struct command_registration arm946e_exec_command_handlers[] = {
593 {
594 .name = "cp15",
595 .handler = arm946e_handle_cp15_command,
596 .mode = COMMAND_EXEC,
597 .usage = "regnum [value]",
598 .help = "display/modify cp15 register",
599 },
600 COMMAND_REGISTRATION_DONE
601 };
602
603 const struct command_registration arm946e_command_handlers[] = {
604 {
605 .chain = arm9tdmi_command_handlers,
606 },
607 {
608 .name = "arm946e",
609 .mode = COMMAND_ANY,
610 .help = "arm946e command group",
611 .usage = "",
612 .chain = arm946e_exec_command_handlers,
613 },
614 COMMAND_REGISTRATION_DONE
615 };
616
617 /** Holds methods for ARM946 targets. */
618 struct target_type arm946e_target = {
619 .name = "arm946e",
620
621 .poll = arm7_9_poll,
622 .arch_state = arm_arch_state,
623
624 .target_request_data = arm7_9_target_request_data,
625
626 .halt = arm7_9_halt,
627 .resume = arm7_9_resume,
628 .step = arm7_9_step,
629
630 .assert_reset = arm7_9_assert_reset,
631 .deassert_reset = arm7_9_deassert_reset,
632 .soft_reset_halt = arm7_9_soft_reset_halt,
633
634 .get_gdb_reg_list = arm_get_gdb_reg_list,
635
636 /* .read_memory = arm7_9_read_memory, */
637 /* .write_memory = arm7_9_write_memory, */
638 .read_memory = arm946e_read_memory,
639 .write_memory = arm946e_write_memory,
640
641 .bulk_write_memory = arm7_9_bulk_write_memory,
642
643 .checksum_memory = arm_checksum_memory,
644 .blank_check_memory = arm_blank_check_memory,
645
646 .run_algorithm = armv4_5_run_algorithm,
647
648 .add_breakpoint = arm7_9_add_breakpoint,
649 .remove_breakpoint = arm7_9_remove_breakpoint,
650 /* .add_breakpoint = arm946e_add_breakpoint, */
651 /* .remove_breakpoint = arm946e_remove_breakpoint, */
652
653 .add_watchpoint = arm7_9_add_watchpoint,
654 .remove_watchpoint = arm7_9_remove_watchpoint,
655
656 .commands = arm946e_command_handlers,
657 .target_create = arm946e_target_create,
658 .init_target = arm9tdmi_init_target,
659 .examine = arm7_9_examine,
660 .check_reset = arm7_9_check_reset,
661 };

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)