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

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)