rtos: Fold is_symbol_mandatory into rtos_qsymbol(..)
[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 /* rtos_qsymbol() processes and replies to all qSymbol packets from GDB.
202 *
203 * GDB sends a qSymbol:: packet (empty address, empty name) to notify
204 * that it can now answer qSymbol::hexcodedname queries, to look up symbols.
205 *
206 * If the qSymbol packet has no address that means GDB did not find the
207 * symbol, in which case auto-detect will move on to try the next RTOS.
208 *
209 * rtos_qsymbol() then calls the next_symbol() helper function, which
210 * iterates over symbol names for the current RTOS until it finds the
211 * symbol in the received GDB packet, and then returns the next entry
212 * in the list of symbols.
213 *
214 * If GDB replied about the last symbol for the RTOS and the RTOS was
215 * specified explicitly, then no further symbol lookup is done. When
216 * auto-detecting, the RTOS driver _detect() function must return success.
217 *
218 * rtos_qsymbol() returns 1 if an RTOS has been detected, or 0 otherwise.
219 */
220 int rtos_qsymbol(struct connection *connection, char const *packet, int packet_size)
221 {
222 int rtos_detected = 0;
223 uint64_t addr = 0;
224 size_t reply_len;
225 char reply[GDB_BUFFER_SIZE + 1], cur_sym[GDB_BUFFER_SIZE / 2 + 1] = ""; /* Extra byte for null-termination */
226 struct symbol_table_elem *next_sym;
227 struct target *target = get_target_from_connection(connection);
228 struct rtos *os = target->rtos;
229
230 reply_len = sprintf(reply, "OK");
231
232 if (!os)
233 goto done;
234
235 /* Decode any symbol name in the packet*/
236 size_t len = unhexify((uint8_t *)cur_sym, strchr(packet + 8, ':') + 1, strlen(strchr(packet + 8, ':') + 1));
237 cur_sym[len] = 0;
238
239 if ((strcmp(packet, "qSymbol::") != 0) && /* GDB is not offering symbol lookup for the first time */
240 (!sscanf(packet, "qSymbol:%" SCNx64 ":", &addr))) { /* GDB did not find an address for a symbol */
241
242 /* GDB could not find an address for the previous symbol */
243 struct symbol_table_elem *sym = find_symbol(os, cur_sym);
244
245 if (sym && !sym->optional) { /* the symbol is mandatory for this RTOS */
246 if (!target->rtos_auto_detect) {
247 LOG_WARNING("RTOS %s not detected. (GDB could not find symbol \'%s\')", os->type->name, cur_sym);
248 goto done;
249 } else {
250 /* Autodetecting RTOS - try next RTOS */
251 if (!rtos_try_next(target)) {
252 LOG_WARNING("No RTOS could be auto-detected!");
253 goto done;
254 }
255
256 /* Next RTOS selected - invalidate current symbol */
257 cur_sym[0] = '\x00';
258 }
259 }
260 }
261
262 LOG_DEBUG("RTOS: Address of symbol '%s' is 0x%" PRIx64, cur_sym, addr);
263
264 next_sym = next_symbol(os, cur_sym, addr);
265
266 /* Should never happen unless the debugger misbehaves */
267 if (!next_sym) {
268 LOG_WARNING("RTOS: Debugger sent us qSymbol with '%s' that we did not ask for", cur_sym);
269 goto done;
270 }
271
272 if (!next_sym->symbol_name) {
273 /* No more symbols need looking up */
274
275 if (!target->rtos_auto_detect) {
276 rtos_detected = 1;
277 goto done;
278 }
279
280 if (os->type->detect_rtos(target)) {
281 LOG_INFO("Auto-detected RTOS: %s", os->type->name);
282 rtos_detected = 1;
283 goto done;
284 } else {
285 LOG_WARNING("No RTOS could be auto-detected!");
286 goto done;
287 }
288 }
289
290 if (8 + (strlen(next_sym->symbol_name) * 2) + 1 > sizeof(reply)) {
291 LOG_ERROR("ERROR: RTOS symbol '%s' name is too long for GDB!", next_sym->symbol_name);
292 goto done;
293 }
294
295 LOG_DEBUG("RTOS: Requesting symbol lookup of '%s' from the debugger", next_sym->symbol_name);
296
297 reply_len = snprintf(reply, sizeof(reply), "qSymbol:");
298 reply_len += hexify(reply + reply_len,
299 (const uint8_t *)next_sym->symbol_name, strlen(next_sym->symbol_name),
300 sizeof(reply) - reply_len);
301
302 done:
303 gdb_put_packet(connection, reply, reply_len);
304 return rtos_detected;
305 }
306
307 int rtos_thread_packet(struct connection *connection, char const *packet, int packet_size)
308 {
309 struct target *target = get_target_from_connection(connection);
310
311 if (strncmp(packet, "qThreadExtraInfo,", 17) == 0) {
312 if ((target->rtos) && (target->rtos->thread_details) &&
313 (target->rtos->thread_count != 0)) {
314 threadid_t threadid = 0;
315 int found = -1;
316 sscanf(packet, "qThreadExtraInfo,%" SCNx64, &threadid);
317
318 if ((target->rtos) && (target->rtos->thread_details)) {
319 int thread_num;
320 for (thread_num = 0; thread_num < target->rtos->thread_count; thread_num++) {
321 if (target->rtos->thread_details[thread_num].threadid == threadid) {
322 if (target->rtos->thread_details[thread_num].exists)
323 found = thread_num;
324 }
325 }
326 }
327 if (found == -1) {
328 gdb_put_packet(connection, "E01", 3); /* thread not found */
329 return ERROR_OK;
330 }
331
332 struct thread_detail *detail = &target->rtos->thread_details[found];
333
334 int str_size = 0;
335 if (detail->thread_name_str)
336 str_size += strlen(detail->thread_name_str);
337 if (detail->extra_info_str)
338 str_size += strlen(detail->extra_info_str);
339
340 char *tmp_str = calloc(str_size + 9, sizeof(char));
341 char *tmp_str_ptr = tmp_str;
342
343 if (detail->thread_name_str)
344 tmp_str_ptr += sprintf(tmp_str_ptr, "Name: %s", detail->thread_name_str);
345 if (detail->extra_info_str) {
346 if (tmp_str_ptr != tmp_str)
347 tmp_str_ptr += sprintf(tmp_str_ptr, ", ");
348 tmp_str_ptr += sprintf(tmp_str_ptr, "%s", detail->extra_info_str);
349 }
350
351 assert(strlen(tmp_str) ==
352 (size_t) (tmp_str_ptr - tmp_str));
353
354 char *hex_str = malloc(strlen(tmp_str) * 2 + 1);
355 size_t pkt_len = hexify(hex_str, (const uint8_t *)tmp_str,
356 strlen(tmp_str), strlen(tmp_str) * 2 + 1);
357
358 gdb_put_packet(connection, hex_str, pkt_len);
359 free(hex_str);
360 free(tmp_str);
361 return ERROR_OK;
362
363 }
364 gdb_put_packet(connection, "", 0);
365 return ERROR_OK;
366 } else if (strncmp(packet, "qSymbol", 7) == 0) {
367 if (rtos_qsymbol(connection, packet, packet_size) == 1) {
368 if (target->rtos_auto_detect == true) {
369 target->rtos_auto_detect = false;
370 target->rtos->type->create(target);
371 }
372 target->rtos->type->update_threads(target->rtos);
373 }
374 return ERROR_OK;
375 } else if (strncmp(packet, "qfThreadInfo", 12) == 0) {
376 int i;
377 if (target->rtos) {
378 if (target->rtos->thread_count == 0) {
379 gdb_put_packet(connection, "l", 1);
380 } else {
381 /*thread id are 16 char +1 for ',' */
382 char *out_str = malloc(17 * target->rtos->thread_count + 1);
383 char *tmp_str = out_str;
384 for (i = 0; i < target->rtos->thread_count; i++) {
385 tmp_str += sprintf(tmp_str, "%c%016" PRIx64, i == 0 ? 'm' : ',',
386 target->rtos->thread_details[i].threadid);
387 }
388 gdb_put_packet(connection, out_str, strlen(out_str));
389 free(out_str);
390 }
391 } else
392 gdb_put_packet(connection, "l", 1);
393
394 return ERROR_OK;
395 } else if (strncmp(packet, "qsThreadInfo", 12) == 0) {
396 gdb_put_packet(connection, "l", 1);
397 return ERROR_OK;
398 } else if (strncmp(packet, "qAttached", 9) == 0) {
399 gdb_put_packet(connection, "1", 1);
400 return ERROR_OK;
401 } else if (strncmp(packet, "qOffsets", 8) == 0) {
402 char offsets[] = "Text=0;Data=0;Bss=0";
403 gdb_put_packet(connection, offsets, sizeof(offsets)-1);
404 return ERROR_OK;
405 } else if (strncmp(packet, "qCRC:", 5) == 0) {
406 /* make sure we check this before "qC" packet below
407 * otherwise it gets incorrectly handled */
408 return GDB_THREAD_PACKET_NOT_CONSUMED;
409 } else if (strncmp(packet, "qC", 2) == 0) {
410 if (target->rtos) {
411 char buffer[19];
412 int size;
413 size = snprintf(buffer, 19, "QC%016" PRIx64, target->rtos->current_thread);
414 gdb_put_packet(connection, buffer, size);
415 } else
416 gdb_put_packet(connection, "QC0", 3);
417 return ERROR_OK;
418 } else if (packet[0] == 'T') { /* Is thread alive? */
419 threadid_t threadid;
420 int found = -1;
421 sscanf(packet, "T%" SCNx64, &threadid);
422 if ((target->rtos) && (target->rtos->thread_details)) {
423 int thread_num;
424 for (thread_num = 0; thread_num < target->rtos->thread_count; thread_num++) {
425 if (target->rtos->thread_details[thread_num].threadid == threadid) {
426 if (target->rtos->thread_details[thread_num].exists)
427 found = thread_num;
428 }
429 }
430 }
431 if (found != -1)
432 gdb_put_packet(connection, "OK", 2); /* thread alive */
433 else
434 gdb_put_packet(connection, "E01", 3); /* thread not found */
435 return ERROR_OK;
436 } else if (packet[0] == 'H') { /* Set current thread ( 'c' for step and continue, 'g' for
437 * all other operations ) */
438 if ((packet[1] == 'g') && (target->rtos)) {
439 threadid_t threadid;
440 sscanf(packet, "Hg%16" SCNx64, &threadid);
441 LOG_DEBUG("RTOS: GDB requested to set current thread to 0x%" PRIx64, threadid);
442 /* threadid of 0 indicates target should choose */
443 if (threadid == 0)
444 target->rtos->current_threadid = target->rtos->current_thread;
445 else
446 target->rtos->current_threadid = threadid;
447 }
448 gdb_put_packet(connection, "OK", 2);
449 return ERROR_OK;
450 }
451
452 return GDB_THREAD_PACKET_NOT_CONSUMED;
453 }
454
455 static int rtos_put_gdb_reg_list(struct connection *connection,
456 struct rtos_reg *reg_list, int num_regs)
457 {
458 size_t num_bytes = 1; /* NUL */
459 for (int i = 0; i < num_regs; ++i)
460 num_bytes += DIV_ROUND_UP(reg_list[i].size, 8) * 2;
461
462 char *hex = malloc(num_bytes);
463 char *hex_p = hex;
464
465 for (int i = 0; i < num_regs; ++i) {
466 size_t count = DIV_ROUND_UP(reg_list[i].size, 8);
467 size_t n = hexify(hex_p, reg_list[i].value, count, num_bytes);
468 hex_p += n;
469 num_bytes -= n;
470 }
471
472 gdb_put_packet(connection, hex, strlen(hex));
473 free(hex);
474
475 return ERROR_OK;
476 }
477
478 /** Look through all registers to find this register. */
479 int rtos_get_gdb_reg(struct connection *connection, int reg_num)
480 {
481 struct target *target = get_target_from_connection(connection);
482 int64_t current_threadid = target->rtos->current_threadid;
483 if ((target->rtos) && (current_threadid != -1) &&
484 (current_threadid != 0) &&
485 ((current_threadid != target->rtos->current_thread) ||
486 (target->smp))) { /* in smp several current thread are possible */
487 struct rtos_reg *reg_list;
488 int num_regs;
489
490 LOG_DEBUG("getting register %d for thread 0x%" PRIx64
491 ", target->rtos->current_thread=0x%" PRIx64,
492 reg_num,
493 current_threadid,
494 target->rtos->current_thread);
495
496 int retval;
497 if (target->rtos->type->get_thread_reg) {
498 reg_list = calloc(1, sizeof(*reg_list));
499 num_regs = 1;
500 retval = target->rtos->type->get_thread_reg(target->rtos,
501 current_threadid, reg_num, &reg_list[0]);
502 if (retval != ERROR_OK) {
503 LOG_ERROR("RTOS: failed to get register %d", reg_num);
504 return retval;
505 }
506 } else {
507 retval = target->rtos->type->get_thread_reg_list(target->rtos,
508 current_threadid,
509 &reg_list,
510 &num_regs);
511 if (retval != ERROR_OK) {
512 LOG_ERROR("RTOS: failed to get register list");
513 return retval;
514 }
515 }
516
517 for (int i = 0; i < num_regs; ++i) {
518 if (reg_list[i].number == (uint32_t)reg_num) {
519 rtos_put_gdb_reg_list(connection, reg_list + i, 1);
520 free(reg_list);
521 return ERROR_OK;
522 }
523 }
524
525 free(reg_list);
526 }
527 return ERROR_FAIL;
528 }
529
530 /** Return a list of general registers. */
531 int rtos_get_gdb_reg_list(struct connection *connection)
532 {
533 struct target *target = get_target_from_connection(connection);
534 int64_t current_threadid = target->rtos->current_threadid;
535 if ((target->rtos) && (current_threadid != -1) &&
536 (current_threadid != 0) &&
537 ((current_threadid != target->rtos->current_thread) ||
538 (target->smp))) { /* in smp several current thread are possible */
539 struct rtos_reg *reg_list;
540 int num_regs;
541
542 LOG_DEBUG("RTOS: getting register list for thread 0x%" PRIx64
543 ", target->rtos->current_thread=0x%" PRIx64 "\r\n",
544 current_threadid,
545 target->rtos->current_thread);
546
547 int retval = target->rtos->type->get_thread_reg_list(target->rtos,
548 current_threadid,
549 &reg_list,
550 &num_regs);
551 if (retval != ERROR_OK) {
552 LOG_ERROR("RTOS: failed to get register list");
553 return retval;
554 }
555
556 rtos_put_gdb_reg_list(connection, reg_list, num_regs);
557 free(reg_list);
558
559 return ERROR_OK;
560 }
561 return ERROR_FAIL;
562 }
563
564 int rtos_set_reg(struct connection *connection, int reg_num,
565 uint8_t *reg_value)
566 {
567 struct target *target = get_target_from_connection(connection);
568 int64_t current_threadid = target->rtos->current_threadid;
569 if ((target->rtos) &&
570 (target->rtos->type->set_reg) &&
571 (current_threadid != -1) &&
572 (current_threadid != 0)) {
573 return target->rtos->type->set_reg(target->rtos, reg_num, reg_value);
574 }
575 return ERROR_FAIL;
576 }
577
578 int rtos_generic_stack_read(struct target *target,
579 const struct rtos_register_stacking *stacking,
580 int64_t stack_ptr,
581 struct rtos_reg **reg_list,
582 int *num_regs)
583 {
584 int retval;
585
586 if (stack_ptr == 0) {
587 LOG_ERROR("Error: null stack pointer in thread");
588 return -5;
589 }
590 /* Read the stack */
591 uint8_t *stack_data = malloc(stacking->stack_registers_size);
592 uint32_t address = stack_ptr;
593
594 if (stacking->stack_growth_direction == 1)
595 address -= stacking->stack_registers_size;
596 retval = target_read_buffer(target, address, stacking->stack_registers_size, stack_data);
597 if (retval != ERROR_OK) {
598 free(stack_data);
599 LOG_ERROR("Error reading stack frame from thread");
600 return retval;
601 }
602 LOG_DEBUG("RTOS: Read stack frame at 0x%" PRIx32, address);
603
604 #if 0
605 LOG_OUTPUT("Stack Data :");
606 for (i = 0; i < stacking->stack_registers_size; i++)
607 LOG_OUTPUT("%02X", stack_data[i]);
608 LOG_OUTPUT("\r\n");
609 #endif
610
611 target_addr_t new_stack_ptr;
612 if (stacking->calculate_process_stack) {
613 new_stack_ptr = stacking->calculate_process_stack(target,
614 stack_data, stacking, stack_ptr);
615 } else {
616 new_stack_ptr = stack_ptr - stacking->stack_growth_direction *
617 stacking->stack_registers_size;
618 }
619
620 *reg_list = calloc(stacking->num_output_registers, sizeof(struct rtos_reg));
621 *num_regs = stacking->num_output_registers;
622
623 for (int i = 0; i < stacking->num_output_registers; ++i) {
624 (*reg_list)[i].number = stacking->register_offsets[i].number;
625 (*reg_list)[i].size = stacking->register_offsets[i].width_bits;
626
627 int offset = stacking->register_offsets[i].offset;
628 if (offset == -2)
629 buf_cpy(&new_stack_ptr, (*reg_list)[i].value, (*reg_list)[i].size);
630 else if (offset != -1)
631 buf_cpy(stack_data + offset, (*reg_list)[i].value, (*reg_list)[i].size);
632 }
633
634 free(stack_data);
635 /* LOG_OUTPUT("Output register string: %s\r\n", *hex_reg_list); */
636 return ERROR_OK;
637 }
638
639 static int rtos_try_next(struct target *target)
640 {
641 struct rtos *os = target->rtos;
642 struct rtos_type **type = rtos_types;
643
644 if (!os)
645 return 0;
646
647 while (*type && os->type != *type)
648 type++;
649
650 if (!*type || !*(++type))
651 return 0;
652
653 os->type = *type;
654
655 free(os->symbols);
656 os->symbols = NULL;
657
658 return 1;
659 }
660
661 int rtos_update_threads(struct target *target)
662 {
663 if ((target->rtos) && (target->rtos->type))
664 target->rtos->type->update_threads(target->rtos);
665 return ERROR_OK;
666 }
667
668 void rtos_free_threadlist(struct rtos *rtos)
669 {
670 if (rtos->thread_details) {
671 int j;
672
673 for (j = 0; j < rtos->thread_count; j++) {
674 struct thread_detail *current_thread = &rtos->thread_details[j];
675 free(current_thread->thread_name_str);
676 free(current_thread->extra_info_str);
677 }
678 free(rtos->thread_details);
679 rtos->thread_details = NULL;
680 rtos->thread_count = 0;
681 rtos->current_threadid = -1;
682 rtos->current_thread = 0;
683 }
684 }
685
686 int rtos_read_buffer(struct target *target, target_addr_t address,
687 uint32_t size, uint8_t *buffer)
688 {
689 if (target->rtos->type->read_buffer)
690 return target->rtos->type->read_buffer(target->rtos, address, size, buffer);
691 return ERROR_NOT_IMPLEMENTED;
692 }
693
694 int rtos_write_buffer(struct target *target, target_addr_t address,
695 uint32_t size, const uint8_t *buffer)
696 {
697 if (target->rtos->type->write_buffer)
698 return target->rtos->type->write_buffer(target->rtos, address, size, buffer);
699 return ERROR_NOT_IMPLEMENTED;
700 }

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)