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

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)