97ce255b924c386ed82458c8cb8421bce307585c
[openocd.git] / src / rtos / rtos.c
1 /***************************************************************************
2 * Copyright (C) 2011 by Broadcom Corporation *
3 * Evan Hunter - ehunter@broadcom.com *
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, see <http://www.gnu.org/licenses/>. *
17 ***************************************************************************/
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "rtos.h"
24 #include "target/target.h"
25 #include "helper/log.h"
26 #include "helper/binarybuffer.h"
27 #include "server/gdb_server.h"
28
29 /* RTOSs */
30 extern struct rtos_type FreeRTOS_rtos;
31 extern struct rtos_type ThreadX_rtos;
32 extern struct rtos_type eCos_rtos;
33 extern struct rtos_type Linux_os;
34 extern struct rtos_type chibios_rtos;
35 extern struct rtos_type chromium_ec_rtos;
36 extern struct rtos_type embKernel_rtos;
37 extern struct rtos_type mqx_rtos;
38 extern struct rtos_type uCOS_III_rtos;
39 extern struct rtos_type nuttx_rtos;
40 extern struct rtos_type hwthread_rtos;
41 extern struct rtos_type riot_rtos;
42
43 static struct rtos_type *rtos_types[] = {
44 &ThreadX_rtos,
45 &FreeRTOS_rtos,
46 &eCos_rtos,
47 &Linux_os,
48 &chibios_rtos,
49 &chromium_ec_rtos,
50 &embKernel_rtos,
51 &mqx_rtos,
52 &uCOS_III_rtos,
53 &nuttx_rtos,
54 &riot_rtos,
55 /* keep this as last, as it always matches with rtos auto */
56 &hwthread_rtos,
57 NULL
58 };
59
60 int rtos_thread_packet(struct connection *connection, const char *packet, int packet_size);
61
62 int rtos_smp_init(struct target *target)
63 {
64 if (target->rtos->type->smp_init)
65 return target->rtos->type->smp_init(target);
66 return ERROR_TARGET_INIT_FAILED;
67 }
68
69 static int rtos_target_for_threadid(struct connection *connection, int64_t threadid, struct target **t)
70 {
71 struct target *curr = get_target_from_connection(connection);
72 if (t)
73 *t = curr;
74
75 return ERROR_OK;
76 }
77
78 static int os_alloc(struct target *target, struct rtos_type *ostype)
79 {
80 struct rtos *os = target->rtos = calloc(1, sizeof(struct rtos));
81
82 if (!os)
83 return JIM_ERR;
84
85 os->type = ostype;
86 os->current_threadid = -1;
87 os->current_thread = 0;
88 os->symbols = NULL;
89 os->target = target;
90
91 /* RTOS drivers can override the packet handler in _create(). */
92 os->gdb_thread_packet = rtos_thread_packet;
93 os->gdb_target_for_threadid = rtos_target_for_threadid;
94
95 return JIM_OK;
96 }
97
98 static void os_free(struct target *target)
99 {
100 if (!target->rtos)
101 return;
102
103 if (target->rtos->symbols)
104 free(target->rtos->symbols);
105
106 free(target->rtos);
107 target->rtos = NULL;
108 }
109
110 static int os_alloc_create(struct target *target, struct rtos_type *ostype)
111 {
112 int ret = os_alloc(target, ostype);
113
114 if (JIM_OK == ret) {
115 ret = target->rtos->type->create(target);
116 if (ret != JIM_OK)
117 os_free(target);
118 }
119
120 return ret;
121 }
122
123 int rtos_create(Jim_GetOptInfo *goi, struct target *target)
124 {
125 int x;
126 const char *cp;
127 Jim_Obj *res;
128 int e;
129
130 if (!goi->isconfigure && goi->argc != 0) {
131 Jim_WrongNumArgs(goi->interp, goi->argc, goi->argv, "NO PARAMS");
132 return JIM_ERR;
133 }
134
135 os_free(target);
136
137 e = Jim_GetOpt_String(goi, &cp, NULL);
138 if (e != JIM_OK)
139 return e;
140
141 if (0 == strcmp(cp, "auto")) {
142 /* Auto detect tries to look up all symbols for each RTOS,
143 * and runs the RTOS driver's _detect() function when GDB
144 * finds all symbols for any RTOS. See rtos_qsymbol(). */
145 target->rtos_auto_detect = true;
146
147 /* rtos_qsymbol() will iterate over all RTOSes. Allocate
148 * target->rtos here, and set it to the first RTOS type. */
149 return os_alloc(target, rtos_types[0]);
150 }
151
152 for (x = 0; rtos_types[x]; x++)
153 if (0 == strcmp(cp, rtos_types[x]->name))
154 return os_alloc_create(target, rtos_types[x]);
155
156 Jim_SetResultFormatted(goi->interp, "Unknown RTOS type %s, try one of: ", cp);
157 res = Jim_GetResult(goi->interp);
158 for (x = 0; rtos_types[x]; x++)
159 Jim_AppendStrings(goi->interp, res, rtos_types[x]->name, ", ", NULL);
160 Jim_AppendStrings(goi->interp, res, " or auto", NULL);
161
162 return JIM_ERR;
163 }
164
165 void rtos_destroy(struct target *target)
166 {
167 os_free(target);
168 }
169
170 int gdb_thread_packet(struct connection *connection, char const *packet, int packet_size)
171 {
172 struct target *target = get_target_from_connection(connection);
173 if (target->rtos == NULL)
174 return rtos_thread_packet(connection, packet, packet_size); /* thread not
175 *found*/
176 return target->rtos->gdb_thread_packet(connection, packet, packet_size);
177 }
178
179 static symbol_table_elem_t *next_symbol(struct rtos *os, char *cur_symbol, uint64_t cur_addr)
180 {
181 symbol_table_elem_t *s;
182
183 if (!os->symbols)
184 os->type->get_symbol_list_to_lookup(&os->symbols);
185
186 if (!cur_symbol[0])
187 return &os->symbols[0];
188
189 for (s = os->symbols; s->symbol_name; s++)
190 if (!strcmp(s->symbol_name, cur_symbol)) {
191 s->address = cur_addr;
192 s++;
193 return s;
194 }
195
196 return NULL;
197 }
198
199 /* searches for 'symbol' in the lookup table for 'os' and returns TRUE,
200 * if 'symbol' is not declared optional */
201 static bool is_symbol_mandatory(const struct rtos *os, const char *symbol)
202 {
203 for (symbol_table_elem_t *s = os->symbols; s->symbol_name; ++s) {
204 if (!strcmp(s->symbol_name, symbol))
205 return !s->optional;
206 }
207 return false;
208 }
209
210 /* rtos_qsymbol() processes and replies to all qSymbol packets from GDB.
211 *
212 * GDB sends a qSymbol:: packet (empty address, empty name) to notify
213 * that it can now answer qSymbol::hexcodedname queries, to look up symbols.
214 *
215 * If the qSymbol packet has no address that means GDB did not find the
216 * symbol, in which case auto-detect will move on to try the next RTOS.
217 *
218 * rtos_qsymbol() then calls the next_symbol() helper function, which
219 * iterates over symbol names for the current RTOS until it finds the
220 * symbol in the received GDB packet, and then returns the next entry
221 * in the list of symbols.
222 *
223 * If GDB replied about the last symbol for the RTOS and the RTOS was
224 * specified explicitly, then no further symbol lookup is done. When
225 * auto-detecting, the RTOS driver _detect() function must return success.
226 *
227 * rtos_qsymbol() returns 1 if an RTOS has been detected, or 0 otherwise.
228 */
229 int rtos_qsymbol(struct connection *connection, char const *packet, int packet_size)
230 {
231 int rtos_detected = 0;
232 uint64_t addr = 0;
233 size_t reply_len;
234 char reply[GDB_BUFFER_SIZE + 1], cur_sym[GDB_BUFFER_SIZE / 2 + 1] = ""; /* Extra byte for null-termination */
235 symbol_table_elem_t *next_sym = NULL;
236 struct target *target = get_target_from_connection(connection);
237 struct rtos *os = target->rtos;
238
239 reply_len = sprintf(reply, "OK");
240
241 if (!os)
242 goto done;
243
244 /* Decode any symbol name in the packet*/
245 size_t len = unhexify((uint8_t *)cur_sym, strchr(packet + 8, ':') + 1, strlen(strchr(packet + 8, ':') + 1));
246 cur_sym[len] = 0;
247
248 if ((strcmp(packet, "qSymbol::") != 0) && /* GDB is not offering symbol lookup for the first time */
249 (!sscanf(packet, "qSymbol:%" SCNx64 ":", &addr)) && /* GDB did not find an address for a symbol */
250 is_symbol_mandatory(os, cur_sym)) { /* the symbol is mandatory for this RTOS */
251
252 /* GDB could not find an address for the previous symbol */
253 if (!target->rtos_auto_detect) {
254 LOG_WARNING("RTOS %s not detected. (GDB could not find symbol \'%s\')", os->type->name, cur_sym);
255 goto done;
256 } else {
257 /* Autodetecting RTOS - try next RTOS */
258 if (!rtos_try_next(target)) {
259 LOG_WARNING("No RTOS could be auto-detected!");
260 goto done;
261 }
262
263 /* Next RTOS selected - invalidate current symbol */
264 cur_sym[0] = '\x00';
265 }
266 }
267 next_sym = next_symbol(os, cur_sym, addr);
268
269 if (!next_sym->symbol_name) {
270 /* No more symbols need looking up */
271
272 if (!target->rtos_auto_detect) {
273 rtos_detected = 1;
274 goto done;
275 }
276
277 if (os->type->detect_rtos(target)) {
278 LOG_INFO("Auto-detected RTOS: %s", os->type->name);
279 rtos_detected = 1;
280 goto done;
281 } else {
282 LOG_WARNING("No RTOS could be auto-detected!");
283 goto done;
284 }
285 }
286
287 if (8 + (strlen(next_sym->symbol_name) * 2) + 1 > sizeof(reply)) {
288 LOG_ERROR("ERROR: RTOS symbol '%s' name is too long for GDB!", next_sym->symbol_name);
289 goto done;
290 }
291
292 reply_len = snprintf(reply, sizeof(reply), "qSymbol:");
293 reply_len += hexify(reply + reply_len,
294 (const uint8_t *)next_sym->symbol_name, strlen(next_sym->symbol_name),
295 sizeof(reply) - reply_len);
296
297 done:
298 gdb_put_packet(connection, reply, reply_len);
299 return rtos_detected;
300 }
301
302 int rtos_thread_packet(struct connection *connection, char const *packet, int packet_size)
303 {
304 struct target *target = get_target_from_connection(connection);
305
306 if (strncmp(packet, "qThreadExtraInfo,", 17) == 0) {
307 if ((target->rtos != NULL) && (target->rtos->thread_details != NULL) &&
308 (target->rtos->thread_count != 0)) {
309 threadid_t threadid = 0;
310 int found = -1;
311 sscanf(packet, "qThreadExtraInfo,%" SCNx64, &threadid);
312
313 if ((target->rtos != NULL) && (target->rtos->thread_details != NULL)) {
314 int thread_num;
315 for (thread_num = 0; thread_num < target->rtos->thread_count; thread_num++) {
316 if (target->rtos->thread_details[thread_num].threadid == threadid) {
317 if (target->rtos->thread_details[thread_num].exists)
318 found = thread_num;
319 }
320 }
321 }
322 if (found == -1) {
323 gdb_put_packet(connection, "E01", 3); /* thread not found */
324 return ERROR_OK;
325 }
326
327 struct thread_detail *detail = &target->rtos->thread_details[found];
328
329 int str_size = 0;
330 if (detail->thread_name_str != NULL)
331 str_size += strlen(detail->thread_name_str);
332 if (detail->extra_info_str != NULL)
333 str_size += strlen(detail->extra_info_str);
334
335 char *tmp_str = calloc(str_size + 9, sizeof(char));
336 char *tmp_str_ptr = tmp_str;
337
338 if (detail->thread_name_str != NULL)
339 tmp_str_ptr += sprintf(tmp_str_ptr, "Name: %s", detail->thread_name_str);
340 if (detail->extra_info_str != NULL) {
341 if (tmp_str_ptr != tmp_str)
342 tmp_str_ptr += sprintf(tmp_str_ptr, ", ");
343 tmp_str_ptr += sprintf(tmp_str_ptr, "%s", detail->extra_info_str);
344 }
345
346 assert(strlen(tmp_str) ==
347 (size_t) (tmp_str_ptr - tmp_str));
348
349 char *hex_str = malloc(strlen(tmp_str) * 2 + 1);
350 size_t pkt_len = hexify(hex_str, (const uint8_t *)tmp_str,
351 strlen(tmp_str), strlen(tmp_str) * 2 + 1);
352
353 gdb_put_packet(connection, hex_str, pkt_len);
354 free(hex_str);
355 free(tmp_str);
356 return ERROR_OK;
357
358 }
359 gdb_put_packet(connection, "", 0);
360 return ERROR_OK;
361 } else if (strncmp(packet, "qSymbol", 7) == 0) {
362 if (rtos_qsymbol(connection, packet, packet_size) == 1) {
363 if (target->rtos_auto_detect == true) {
364 target->rtos_auto_detect = false;
365 target->rtos->type->create(target);
366 }
367 target->rtos->type->update_threads(target->rtos);
368 }
369 return ERROR_OK;
370 } else if (strncmp(packet, "qfThreadInfo", 12) == 0) {
371 int i;
372 if (target->rtos != NULL) {
373 if (target->rtos->thread_count == 0) {
374 gdb_put_packet(connection, "l", 1);
375 } else {
376 /*thread id are 16 char +1 for ',' */
377 char *out_str = malloc(17 * target->rtos->thread_count + 1);
378 char *tmp_str = out_str;
379 for (i = 0; i < target->rtos->thread_count; i++) {
380 tmp_str += sprintf(tmp_str, "%c%016" PRIx64, i == 0 ? 'm' : ',',
381 target->rtos->thread_details[i].threadid);
382 }
383 gdb_put_packet(connection, out_str, strlen(out_str));
384 free(out_str);
385 }
386 } else
387 gdb_put_packet(connection, "l", 1);
388
389 return ERROR_OK;
390 } else if (strncmp(packet, "qsThreadInfo", 12) == 0) {
391 gdb_put_packet(connection, "l", 1);
392 return ERROR_OK;
393 } else if (strncmp(packet, "qAttached", 9) == 0) {
394 gdb_put_packet(connection, "1", 1);
395 return ERROR_OK;
396 } else if (strncmp(packet, "qOffsets", 8) == 0) {
397 char offsets[] = "Text=0;Data=0;Bss=0";
398 gdb_put_packet(connection, offsets, sizeof(offsets)-1);
399 return ERROR_OK;
400 } else if (strncmp(packet, "qCRC:", 5) == 0) {
401 /* make sure we check this before "qC" packet below
402 * otherwise it gets incorrectly handled */
403 return GDB_THREAD_PACKET_NOT_CONSUMED;
404 } else if (strncmp(packet, "qC", 2) == 0) {
405 if (target->rtos != NULL) {
406 char buffer[19];
407 int size;
408 size = snprintf(buffer, 19, "QC%016" PRIx64, target->rtos->current_thread);
409 gdb_put_packet(connection, buffer, size);
410 } else
411 gdb_put_packet(connection, "QC0", 3);
412 return ERROR_OK;
413 } else if (packet[0] == 'T') { /* Is thread alive? */
414 threadid_t threadid;
415 int found = -1;
416 sscanf(packet, "T%" SCNx64, &threadid);
417 if ((target->rtos != NULL) && (target->rtos->thread_details != NULL)) {
418 int thread_num;
419 for (thread_num = 0; thread_num < target->rtos->thread_count; thread_num++) {
420 if (target->rtos->thread_details[thread_num].threadid == threadid) {
421 if (target->rtos->thread_details[thread_num].exists)
422 found = thread_num;
423 }
424 }
425 }
426 if (found != -1)
427 gdb_put_packet(connection, "OK", 2); /* thread alive */
428 else
429 gdb_put_packet(connection, "E01", 3); /* thread not found */
430 return ERROR_OK;
431 } else if (packet[0] == 'H') { /* Set current thread ( 'c' for step and continue, 'g' for
432 * all other operations ) */
433 if ((packet[1] == 'g') && (target->rtos != NULL)) {
434 threadid_t threadid;
435 sscanf(packet, "Hg%16" SCNx64, &threadid);
436 LOG_DEBUG("RTOS: GDB requested to set current thread to 0x%" PRIx64, threadid);
437 /* threadid of 0 indicates target should choose */
438 if (threadid == 0)
439 target->rtos->current_threadid = target->rtos->current_thread;
440 else
441 target->rtos->current_threadid = threadid;
442 }
443 gdb_put_packet(connection, "OK", 2);
444 return ERROR_OK;
445 }
446
447 return GDB_THREAD_PACKET_NOT_CONSUMED;
448 }
449
450 static int rtos_put_gdb_reg_list(struct connection *connection,
451 struct rtos_reg *reg_list, int num_regs)
452 {
453 size_t num_bytes = 1; /* NUL */
454 for (int i = 0; i < num_regs; ++i)
455 num_bytes += DIV_ROUND_UP(reg_list[i].size, 8) * 2;
456
457 char *hex = malloc(num_bytes);
458 char *hex_p = hex;
459
460 for (int i = 0; i < num_regs; ++i) {
461 size_t count = DIV_ROUND_UP(reg_list[i].size, 8);
462 size_t n = hexify(hex_p, reg_list[i].value, count, num_bytes);
463 hex_p += n;
464 num_bytes -= n;
465 }
466
467 gdb_put_packet(connection, hex, strlen(hex));
468 free(hex);
469
470 return ERROR_OK;
471 }
472
473 /** Look through all registers to find this register. */
474 int rtos_get_gdb_reg(struct connection *connection, int reg_num)
475 {
476 struct target *target = get_target_from_connection(connection);
477 int64_t current_threadid = target->rtos->current_threadid;
478 if ((target->rtos != NULL) && (current_threadid != -1) &&
479 (current_threadid != 0) &&
480 ((current_threadid != target->rtos->current_thread) ||
481 (target->smp))) { /* in smp several current thread are possible */
482 struct rtos_reg *reg_list;
483 int num_regs;
484
485 LOG_DEBUG("getting register %d for thread 0x%" PRIx64
486 ", target->rtos->current_thread=0x%" PRIx64,
487 reg_num,
488 current_threadid,
489 target->rtos->current_thread);
490
491 int retval;
492 if (target->rtos->type->get_thread_reg) {
493 reg_list = calloc(1, sizeof(*reg_list));
494 num_regs = 1;
495 retval = target->rtos->type->get_thread_reg(target->rtos,
496 current_threadid, reg_num, &reg_list[0]);
497 if (retval != ERROR_OK) {
498 LOG_ERROR("RTOS: failed to get register %d", reg_num);
499 return retval;
500 }
501 } else {
502 retval = target->rtos->type->get_thread_reg_list(target->rtos,
503 current_threadid,
504 &reg_list,
505 &num_regs);
506 if (retval != ERROR_OK) {
507 LOG_ERROR("RTOS: failed to get register list");
508 return retval;
509 }
510 }
511
512 for (int i = 0; i < num_regs; ++i) {
513 if (reg_list[i].number == (uint32_t)reg_num) {
514 rtos_put_gdb_reg_list(connection, reg_list + i, 1);
515 free(reg_list);
516 return ERROR_OK;
517 }
518 }
519
520 free(reg_list);
521 }
522 return ERROR_FAIL;
523 }
524
525 /** Return a list of general registers. */
526 int rtos_get_gdb_reg_list(struct connection *connection)
527 {
528 struct target *target = get_target_from_connection(connection);
529 int64_t current_threadid = target->rtos->current_threadid;
530 if ((target->rtos != NULL) && (current_threadid != -1) &&
531 (current_threadid != 0) &&
532 ((current_threadid != target->rtos->current_thread) ||
533 (target->smp))) { /* in smp several current thread are possible */
534 struct rtos_reg *reg_list;
535 int num_regs;
536
537 LOG_DEBUG("RTOS: getting register list for thread 0x%" PRIx64
538 ", target->rtos->current_thread=0x%" PRIx64 "\r\n",
539 current_threadid,
540 target->rtos->current_thread);
541
542 int retval = target->rtos->type->get_thread_reg_list(target->rtos,
543 current_threadid,
544 &reg_list,
545 &num_regs);
546 if (retval != ERROR_OK) {
547 LOG_ERROR("RTOS: failed to get register list");
548 return retval;
549 }
550
551 rtos_put_gdb_reg_list(connection, reg_list, num_regs);
552 free(reg_list);
553
554 return ERROR_OK;
555 }
556 return ERROR_FAIL;
557 }
558
559 int rtos_set_reg(struct connection *connection, int reg_num,
560 uint8_t *reg_value)
561 {
562 struct target *target = get_target_from_connection(connection);
563 int64_t current_threadid = target->rtos->current_threadid;
564 if ((target->rtos != NULL) &&
565 (target->rtos->type->set_reg != NULL) &&
566 (current_threadid != -1) &&
567 (current_threadid != 0)) {
568 return target->rtos->type->set_reg(target->rtos, reg_num, reg_value);
569 }
570 return ERROR_FAIL;
571 }
572
573 int rtos_generic_stack_read(struct target *target,
574 const struct rtos_register_stacking *stacking,
575 int64_t stack_ptr,
576 struct rtos_reg **reg_list,
577 int *num_regs)
578 {
579 int retval;
580
581 if (stack_ptr == 0) {
582 LOG_ERROR("Error: null stack pointer in thread");
583 return -5;
584 }
585 /* Read the stack */
586 uint8_t *stack_data = malloc(stacking->stack_registers_size);
587 uint32_t address = stack_ptr;
588
589 if (stacking->stack_growth_direction == 1)
590 address -= stacking->stack_registers_size;
591 retval = target_read_buffer(target, address, stacking->stack_registers_size, stack_data);
592 if (retval != ERROR_OK) {
593 free(stack_data);
594 LOG_ERROR("Error reading stack frame from thread");
595 return retval;
596 }
597 LOG_DEBUG("RTOS: Read stack frame at 0x%" PRIx32, address);
598
599 #if 0
600 LOG_OUTPUT("Stack Data :");
601 for (i = 0; i < stacking->stack_registers_size; i++)
602 LOG_OUTPUT("%02X", stack_data[i]);
603 LOG_OUTPUT("\r\n");
604 #endif
605
606 int64_t new_stack_ptr;
607 if (stacking->calculate_process_stack != NULL) {
608 new_stack_ptr = stacking->calculate_process_stack(target,
609 stack_data, stacking, stack_ptr);
610 } else {
611 new_stack_ptr = stack_ptr - stacking->stack_growth_direction *
612 stacking->stack_registers_size;
613 }
614
615 *reg_list = calloc(stacking->num_output_registers, sizeof(struct rtos_reg));
616 *num_regs = stacking->num_output_registers;
617
618 for (int i = 0; i < stacking->num_output_registers; ++i) {
619 (*reg_list)[i].number = stacking->register_offsets[i].number;
620 (*reg_list)[i].size = stacking->register_offsets[i].width_bits;
621
622 int offset = stacking->register_offsets[i].offset;
623 if (offset == -2)
624 buf_cpy(&new_stack_ptr, (*reg_list)[i].value, (*reg_list)[i].size);
625 else if (offset != -1)
626 buf_cpy(stack_data + offset, (*reg_list)[i].value, (*reg_list)[i].size);
627 }
628
629 free(stack_data);
630 /* LOG_OUTPUT("Output register string: %s\r\n", *hex_reg_list); */
631 return ERROR_OK;
632 }
633
634 int rtos_try_next(struct target *target)
635 {
636 struct rtos *os = target->rtos;
637 struct rtos_type **type = rtos_types;
638
639 if (!os)
640 return 0;
641
642 while (*type && os->type != *type)
643 type++;
644
645 if (!*type || !*(++type))
646 return 0;
647
648 os->type = *type;
649 if (os->symbols) {
650 free(os->symbols);
651 os->symbols = NULL;
652 }
653
654 return 1;
655 }
656
657 int rtos_update_threads(struct target *target)
658 {
659 if ((target->rtos != NULL) && (target->rtos->type != NULL))
660 target->rtos->type->update_threads(target->rtos);
661 return ERROR_OK;
662 }
663
664 void rtos_free_threadlist(struct rtos *rtos)
665 {
666 if (rtos->thread_details) {
667 int j;
668
669 for (j = 0; j < rtos->thread_count; j++) {
670 struct thread_detail *current_thread = &rtos->thread_details[j];
671 free(current_thread->thread_name_str);
672 free(current_thread->extra_info_str);
673 }
674 free(rtos->thread_details);
675 rtos->thread_details = NULL;
676 rtos->thread_count = 0;
677 rtos->current_threadid = -1;
678 rtos->current_thread = 0;
679 }
680 }

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)