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

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)