FreeRTOS: Fix current thread ID when no threads are active
[openocd.git] / src / rtos / FreeRTOS.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 <helper/time_support.h>
13 #include <jtag/jtag.h>
14 #include "target/target.h"
15 #include "target/target_type.h"
16 #include "rtos.h"
17 #include "helper/log.h"
18 #include "helper/types.h"
19 #include "rtos_standard_stackings.h"
20 #include "target/armv7m.h"
21 #include "target/cortex_m.h"
22
23 #define FREERTOS_MAX_PRIORITIES 63
24
25 /* FIXME: none of the _width parameters are actually observed properly!
26 * you WILL need to edit more if you actually attempt to target a 8/16/64
27 * bit target!
28 */
29
30 struct freertos_params {
31 const char *target_name;
32 const unsigned char thread_count_width;
33 const unsigned char pointer_width;
34 const unsigned char list_next_offset;
35 const unsigned char list_width;
36 const unsigned char list_elem_next_offset;
37 const unsigned char list_elem_content_offset;
38 const unsigned char thread_stack_offset;
39 const unsigned char thread_name_offset;
40 const struct rtos_register_stacking *stacking_info_cm3;
41 const struct rtos_register_stacking *stacking_info_cm4f;
42 const struct rtos_register_stacking *stacking_info_cm4f_fpu;
43 };
44
45 static const struct freertos_params freertos_params_list[] = {
46 {
47 "cortex_m", /* target_name */
48 4, /* thread_count_width; */
49 4, /* pointer_width; */
50 16, /* list_next_offset; */
51 20, /* list_width; */
52 8, /* list_elem_next_offset; */
53 12, /* list_elem_content_offset */
54 0, /* thread_stack_offset; */
55 52, /* thread_name_offset; */
56 &rtos_standard_cortex_m3_stacking, /* stacking_info */
57 &rtos_standard_cortex_m4f_stacking,
58 &rtos_standard_cortex_m4f_fpu_stacking,
59 },
60 {
61 "hla_target", /* target_name */
62 4, /* thread_count_width; */
63 4, /* pointer_width; */
64 16, /* list_next_offset; */
65 20, /* list_width; */
66 8, /* list_elem_next_offset; */
67 12, /* list_elem_content_offset */
68 0, /* thread_stack_offset; */
69 52, /* thread_name_offset; */
70 &rtos_standard_cortex_m3_stacking, /* stacking_info */
71 &rtos_standard_cortex_m4f_stacking,
72 &rtos_standard_cortex_m4f_fpu_stacking,
73 },
74 {
75 "nds32_v3", /* target_name */
76 4, /* thread_count_width; */
77 4, /* pointer_width; */
78 16, /* list_next_offset; */
79 20, /* list_width; */
80 8, /* list_elem_next_offset; */
81 12, /* list_elem_content_offset */
82 0, /* thread_stack_offset; */
83 52, /* thread_name_offset; */
84 &rtos_standard_nds32_n1068_stacking, /* stacking_info */
85 &rtos_standard_cortex_m4f_stacking,
86 &rtos_standard_cortex_m4f_fpu_stacking,
87 },
88 };
89
90 static bool freertos_detect_rtos(struct target *target);
91 static int freertos_create(struct target *target);
92 static int freertos_update_threads(struct rtos *rtos);
93 static int freertos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
94 struct rtos_reg **reg_list, int *num_regs);
95 static int freertos_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[]);
96
97 struct rtos_type freertos_rtos = {
98 .name = "FreeRTOS",
99
100 .detect_rtos = freertos_detect_rtos,
101 .create = freertos_create,
102 .update_threads = freertos_update_threads,
103 .get_thread_reg_list = freertos_get_thread_reg_list,
104 .get_symbol_list_to_lookup = freertos_get_symbol_list_to_lookup,
105 };
106
107 enum freertos_symbol_values {
108 FREERTOS_VAL_PX_CURRENT_TCB = 0,
109 FREERTOS_VAL_PX_READY_TASKS_LISTS = 1,
110 FREERTOS_VAL_X_DELAYED_TASK_LIST1 = 2,
111 FREERTOS_VAL_X_DELAYED_TASK_LIST2 = 3,
112 FREERTOS_VAL_PX_DELAYED_TASK_LIST = 4,
113 FREERTOS_VAL_PX_OVERFLOW_DELAYED_TASK_LIST = 5,
114 FREERTOS_VAL_X_PENDING_READY_LIST = 6,
115 FREERTOS_VAL_X_TASKS_WAITING_TERMINATION = 7,
116 FREERTOS_VAL_X_SUSPENDED_TASK_LIST = 8,
117 FREERTOS_VAL_UX_CURRENT_NUMBER_OF_TASKS = 9,
118 FREERTOS_VAL_UX_TOP_USED_PRIORITY = 10,
119 };
120
121 struct symbols {
122 const char *name;
123 bool optional;
124 };
125
126 static const struct symbols freertos_symbol_list[] = {
127 { "pxCurrentTCB", false },
128 { "pxReadyTasksLists", false },
129 { "xDelayedTaskList1", false },
130 { "xDelayedTaskList2", false },
131 { "pxDelayedTaskList", false },
132 { "pxOverflowDelayedTaskList", false },
133 { "xPendingReadyList", false },
134 { "xTasksWaitingTermination", true }, /* Only if INCLUDE_vTaskDelete */
135 { "xSuspendedTaskList", true }, /* Only if INCLUDE_vTaskSuspend */
136 { "uxCurrentNumberOfTasks", false },
137 { "uxTopUsedPriority", true }, /* Unavailable since v7.5.3 */
138 { NULL, false }
139 };
140
141 /* TODO: */
142 /* this is not safe for little endian yet */
143 /* may be problems reading if sizes are not 32 bit long integers. */
144 /* test mallocs for failure */
145
146 static int freertos_update_threads(struct rtos *rtos)
147 {
148 int retval;
149 unsigned int tasks_found = 0;
150 const struct freertos_params *param;
151
152 if (!rtos->rtos_specific_params)
153 return -1;
154
155 param = (const struct freertos_params *) rtos->rtos_specific_params;
156
157 if (!rtos->symbols) {
158 LOG_ERROR("No symbols for FreeRTOS");
159 return -3;
160 }
161
162 if (rtos->symbols[FREERTOS_VAL_UX_CURRENT_NUMBER_OF_TASKS].address == 0) {
163 LOG_ERROR("Don't have the number of threads in FreeRTOS");
164 return -2;
165 }
166
167 uint32_t thread_list_size = 0;
168 retval = target_read_u32(rtos->target,
169 rtos->symbols[FREERTOS_VAL_UX_CURRENT_NUMBER_OF_TASKS].address,
170 &thread_list_size);
171 LOG_DEBUG("FreeRTOS: Read uxCurrentNumberOfTasks at 0x%" PRIx64 ", value %" PRIu32,
172 rtos->symbols[FREERTOS_VAL_UX_CURRENT_NUMBER_OF_TASKS].address,
173 thread_list_size);
174
175 if (retval != ERROR_OK) {
176 LOG_ERROR("Could not read FreeRTOS thread count from target");
177 return retval;
178 }
179
180 /* wipe out previous thread details if any */
181 rtos_free_threadlist(rtos);
182
183 /* read the current thread */
184 uint32_t pointer_casts_are_bad;
185 retval = target_read_u32(rtos->target,
186 rtos->symbols[FREERTOS_VAL_PX_CURRENT_TCB].address,
187 &pointer_casts_are_bad);
188 if (retval != ERROR_OK) {
189 LOG_ERROR("Error reading current thread in FreeRTOS thread list");
190 return retval;
191 }
192 rtos->current_thread = pointer_casts_are_bad;
193 LOG_DEBUG("FreeRTOS: Read pxCurrentTCB at 0x%" PRIx64 ", value 0x%" PRIx64,
194 rtos->symbols[FREERTOS_VAL_PX_CURRENT_TCB].address,
195 rtos->current_thread);
196
197 if ((thread_list_size == 0) || (rtos->current_thread == 0)) {
198 /* Either : No RTOS threads - there is always at least the current execution though */
199 /* OR : No current thread - all threads suspended - show the current execution
200 * of idling */
201 char tmp_str[] = "Current Execution";
202 thread_list_size++;
203 tasks_found++;
204 rtos->thread_details = malloc(
205 sizeof(struct thread_detail) * thread_list_size);
206 if (!rtos->thread_details) {
207 LOG_ERROR("Error allocating memory for %d threads", thread_list_size);
208 return ERROR_FAIL;
209 }
210 rtos->current_thread = 1;
211 rtos->thread_details->threadid = rtos->current_thread;
212 rtos->thread_details->exists = true;
213 rtos->thread_details->extra_info_str = NULL;
214 rtos->thread_details->thread_name_str = malloc(sizeof(tmp_str));
215 strcpy(rtos->thread_details->thread_name_str, tmp_str);
216
217 if (thread_list_size == 1) {
218 rtos->thread_count = 1;
219 return ERROR_OK;
220 }
221 } else {
222 /* create space for new thread details */
223 rtos->thread_details = malloc(
224 sizeof(struct thread_detail) * thread_list_size);
225 if (!rtos->thread_details) {
226 LOG_ERROR("Error allocating memory for %d threads", thread_list_size);
227 return ERROR_FAIL;
228 }
229 }
230
231 /* Find out how many lists are needed to be read from pxReadyTasksLists, */
232 if (rtos->symbols[FREERTOS_VAL_UX_TOP_USED_PRIORITY].address == 0) {
233 LOG_ERROR("FreeRTOS: uxTopUsedPriority is not defined, consult the OpenOCD manual for a work-around");
234 return ERROR_FAIL;
235 }
236 uint32_t top_used_priority = 0;
237 retval = target_read_u32(rtos->target,
238 rtos->symbols[FREERTOS_VAL_UX_TOP_USED_PRIORITY].address,
239 &top_used_priority);
240 if (retval != ERROR_OK)
241 return retval;
242 LOG_DEBUG("FreeRTOS: Read uxTopUsedPriority at 0x%" PRIx64 ", value %" PRIu32,
243 rtos->symbols[FREERTOS_VAL_UX_TOP_USED_PRIORITY].address,
244 top_used_priority);
245 if (top_used_priority > FREERTOS_MAX_PRIORITIES) {
246 LOG_ERROR("FreeRTOS top used priority is unreasonably big, not proceeding: %" PRIu32,
247 top_used_priority);
248 return ERROR_FAIL;
249 }
250
251 /* uxTopUsedPriority was defined as configMAX_PRIORITIES - 1
252 * in old FreeRTOS versions (before V7.5.3)
253 * Use contrib/rtos-helpers/FreeRTOS-openocd.c to get compatible symbol
254 * in newer FreeRTOS versions.
255 * Here we restore the original configMAX_PRIORITIES value */
256 unsigned int config_max_priorities = top_used_priority + 1;
257
258 symbol_address_t *list_of_lists =
259 malloc(sizeof(symbol_address_t) * (config_max_priorities + 5));
260 if (!list_of_lists) {
261 LOG_ERROR("Error allocating memory for %u priorities", config_max_priorities);
262 return ERROR_FAIL;
263 }
264
265 unsigned int num_lists;
266 for (num_lists = 0; num_lists < config_max_priorities; num_lists++)
267 list_of_lists[num_lists] = rtos->symbols[FREERTOS_VAL_PX_READY_TASKS_LISTS].address +
268 num_lists * param->list_width;
269
270 list_of_lists[num_lists++] = rtos->symbols[FREERTOS_VAL_X_DELAYED_TASK_LIST1].address;
271 list_of_lists[num_lists++] = rtos->symbols[FREERTOS_VAL_X_DELAYED_TASK_LIST2].address;
272 list_of_lists[num_lists++] = rtos->symbols[FREERTOS_VAL_X_PENDING_READY_LIST].address;
273 list_of_lists[num_lists++] = rtos->symbols[FREERTOS_VAL_X_SUSPENDED_TASK_LIST].address;
274 list_of_lists[num_lists++] = rtos->symbols[FREERTOS_VAL_X_TASKS_WAITING_TERMINATION].address;
275
276 for (unsigned int i = 0; i < num_lists; i++) {
277 if (list_of_lists[i] == 0)
278 continue;
279
280 /* Read the number of threads in this list */
281 uint32_t list_thread_count = 0;
282 retval = target_read_u32(rtos->target,
283 list_of_lists[i],
284 &list_thread_count);
285 if (retval != ERROR_OK) {
286 LOG_ERROR("Error reading number of threads in FreeRTOS thread list");
287 free(list_of_lists);
288 return retval;
289 }
290 LOG_DEBUG("FreeRTOS: Read thread count for list %u at 0x%" PRIx64 ", value %" PRIu32,
291 i, list_of_lists[i], list_thread_count);
292
293 if (list_thread_count == 0)
294 continue;
295
296 /* Read the location of first list item */
297 uint32_t prev_list_elem_ptr = -1;
298 uint32_t list_elem_ptr = 0;
299 retval = target_read_u32(rtos->target,
300 list_of_lists[i] + param->list_next_offset,
301 &list_elem_ptr);
302 if (retval != ERROR_OK) {
303 LOG_ERROR("Error reading first thread item location in FreeRTOS thread list");
304 free(list_of_lists);
305 return retval;
306 }
307 LOG_DEBUG("FreeRTOS: Read first item for list %u at 0x%" PRIx64 ", value 0x%" PRIx32,
308 i, list_of_lists[i] + param->list_next_offset, list_elem_ptr);
309
310 while ((list_thread_count > 0) && (list_elem_ptr != 0) &&
311 (list_elem_ptr != prev_list_elem_ptr) &&
312 (tasks_found < thread_list_size)) {
313 /* Get the location of the thread structure. */
314 rtos->thread_details[tasks_found].threadid = 0;
315 retval = target_read_u32(rtos->target,
316 list_elem_ptr + param->list_elem_content_offset,
317 &pointer_casts_are_bad);
318 if (retval != ERROR_OK) {
319 LOG_ERROR("Error reading thread list item object in FreeRTOS thread list");
320 free(list_of_lists);
321 return retval;
322 }
323 rtos->thread_details[tasks_found].threadid = pointer_casts_are_bad;
324 LOG_DEBUG("FreeRTOS: Read Thread ID at 0x%" PRIx32 ", value 0x%" PRIx64,
325 list_elem_ptr + param->list_elem_content_offset,
326 rtos->thread_details[tasks_found].threadid);
327
328 /* get thread name */
329
330 #define FREERTOS_THREAD_NAME_STR_SIZE (200)
331 char tmp_str[FREERTOS_THREAD_NAME_STR_SIZE];
332
333 /* Read the thread name */
334 retval = target_read_buffer(rtos->target,
335 rtos->thread_details[tasks_found].threadid + param->thread_name_offset,
336 FREERTOS_THREAD_NAME_STR_SIZE,
337 (uint8_t *)&tmp_str);
338 if (retval != ERROR_OK) {
339 LOG_ERROR("Error reading first thread item location in FreeRTOS thread list");
340 free(list_of_lists);
341 return retval;
342 }
343 tmp_str[FREERTOS_THREAD_NAME_STR_SIZE-1] = '\x00';
344 LOG_DEBUG("FreeRTOS: Read Thread Name at 0x%" PRIx64 ", value '%s'",
345 rtos->thread_details[tasks_found].threadid + param->thread_name_offset,
346 tmp_str);
347
348 if (tmp_str[0] == '\x00')
349 strcpy(tmp_str, "No Name");
350
351 rtos->thread_details[tasks_found].thread_name_str =
352 malloc(strlen(tmp_str)+1);
353 strcpy(rtos->thread_details[tasks_found].thread_name_str, tmp_str);
354 rtos->thread_details[tasks_found].exists = true;
355
356 if (rtos->thread_details[tasks_found].threadid == rtos->current_thread) {
357 char running_str[] = "State: Running";
358 rtos->thread_details[tasks_found].extra_info_str = malloc(
359 sizeof(running_str));
360 strcpy(rtos->thread_details[tasks_found].extra_info_str,
361 running_str);
362 } else
363 rtos->thread_details[tasks_found].extra_info_str = NULL;
364
365 tasks_found++;
366 list_thread_count--;
367
368 prev_list_elem_ptr = list_elem_ptr;
369 list_elem_ptr = 0;
370 retval = target_read_u32(rtos->target,
371 prev_list_elem_ptr + param->list_elem_next_offset,
372 &list_elem_ptr);
373 if (retval != ERROR_OK) {
374 LOG_ERROR("Error reading next thread item location in FreeRTOS thread list");
375 free(list_of_lists);
376 return retval;
377 }
378 LOG_DEBUG("FreeRTOS: Read next thread location at 0x%" PRIx32 ", value 0x%" PRIx32,
379 prev_list_elem_ptr + param->list_elem_next_offset,
380 list_elem_ptr);
381 }
382 }
383
384 free(list_of_lists);
385 rtos->thread_count = tasks_found;
386 return 0;
387 }
388
389 static int freertos_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
390 struct rtos_reg **reg_list, int *num_regs)
391 {
392 int retval;
393 const struct freertos_params *param;
394 int64_t stack_ptr = 0;
395
396 if (!rtos)
397 return -1;
398
399 if (thread_id == 0)
400 return -2;
401
402 if (!rtos->rtos_specific_params)
403 return -1;
404
405 param = (const struct freertos_params *) rtos->rtos_specific_params;
406
407 /* Read the stack pointer */
408 uint32_t pointer_casts_are_bad;
409 retval = target_read_u32(rtos->target,
410 thread_id + param->thread_stack_offset,
411 &pointer_casts_are_bad);
412 if (retval != ERROR_OK) {
413 LOG_ERROR("Error reading stack frame from FreeRTOS thread");
414 return retval;
415 }
416 stack_ptr = pointer_casts_are_bad;
417 LOG_DEBUG("FreeRTOS: Read stack pointer at 0x%" PRIx64 ", value 0x%" PRIx64,
418 thread_id + param->thread_stack_offset,
419 stack_ptr);
420
421 /* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4F */
422 int cm4_fpu_enabled = 0;
423 struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target);
424 if (is_armv7m(armv7m_target)) {
425 if (armv7m_target->fp_feature == FPV4_SP) {
426 /* Found ARM v7m target which includes a FPU */
427 uint32_t cpacr;
428
429 retval = target_read_u32(rtos->target, FPU_CPACR, &cpacr);
430 if (retval != ERROR_OK) {
431 LOG_ERROR("Could not read CPACR register to check FPU state");
432 return -1;
433 }
434
435 /* Check if CP10 and CP11 are set to full access. */
436 if (cpacr & 0x00F00000) {
437 /* Found target with enabled FPU */
438 cm4_fpu_enabled = 1;
439 }
440 }
441 }
442
443 if (cm4_fpu_enabled == 1) {
444 /* Read the LR to decide between stacking with or without FPU */
445 uint32_t lr_svc = 0;
446 retval = target_read_u32(rtos->target,
447 stack_ptr + 0x20,
448 &lr_svc);
449 if (retval != ERROR_OK) {
450 LOG_OUTPUT("Error reading stack frame from FreeRTOS thread");
451 return retval;
452 }
453 if ((lr_svc & 0x10) == 0)
454 return rtos_generic_stack_read(rtos->target, param->stacking_info_cm4f_fpu, stack_ptr, reg_list, num_regs);
455 else
456 return rtos_generic_stack_read(rtos->target, param->stacking_info_cm4f, stack_ptr, reg_list, num_regs);
457 } else
458 return rtos_generic_stack_read(rtos->target, param->stacking_info_cm3, stack_ptr, reg_list, num_regs);
459 }
460
461 static int freertos_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[])
462 {
463 unsigned int i;
464 *symbol_list = calloc(
465 ARRAY_SIZE(freertos_symbol_list), sizeof(struct symbol_table_elem));
466
467 for (i = 0; i < ARRAY_SIZE(freertos_symbol_list); i++) {
468 (*symbol_list)[i].symbol_name = freertos_symbol_list[i].name;
469 (*symbol_list)[i].optional = freertos_symbol_list[i].optional;
470 }
471
472 return 0;
473 }
474
475 #if 0
476
477 static int freertos_set_current_thread(struct rtos *rtos, threadid_t thread_id)
478 {
479 return 0;
480 }
481
482 static int freertos_get_thread_ascii_info(struct rtos *rtos, threadid_t thread_id, char **info)
483 {
484 int retval;
485 const struct freertos_params *param;
486
487 if (!rtos)
488 return -1;
489
490 if (thread_id == 0)
491 return -2;
492
493 if (!rtos->rtos_specific_params)
494 return -3;
495
496 param = (const struct freertos_params *) rtos->rtos_specific_params;
497
498 #define FREERTOS_THREAD_NAME_STR_SIZE (200)
499 char tmp_str[FREERTOS_THREAD_NAME_STR_SIZE];
500
501 /* Read the thread name */
502 retval = target_read_buffer(rtos->target,
503 thread_id + param->thread_name_offset,
504 FREERTOS_THREAD_NAME_STR_SIZE,
505 (uint8_t *)&tmp_str);
506 if (retval != ERROR_OK) {
507 LOG_ERROR("Error reading first thread item location in FreeRTOS thread list");
508 return retval;
509 }
510 tmp_str[FREERTOS_THREAD_NAME_STR_SIZE-1] = '\x00';
511
512 if (tmp_str[0] == '\x00')
513 strcpy(tmp_str, "No Name");
514
515 *info = malloc(strlen(tmp_str)+1);
516 strcpy(*info, tmp_str);
517 return 0;
518 }
519
520 #endif
521
522 static bool freertos_detect_rtos(struct target *target)
523 {
524 if ((target->rtos->symbols) &&
525 (target->rtos->symbols[FREERTOS_VAL_PX_READY_TASKS_LISTS].address != 0)) {
526 /* looks like FreeRTOS */
527 return true;
528 }
529 return false;
530 }
531
532 static int freertos_create(struct target *target)
533 {
534 for (unsigned int i = 0; i < ARRAY_SIZE(freertos_params_list); i++)
535 if (strcmp(freertos_params_list[i].target_name, target->type->name) == 0) {
536 target->rtos->rtos_specific_params = (void *)&freertos_params_list[i];
537 return 0;
538 }
539
540 LOG_ERROR("Could not find target in FreeRTOS compatibility list");
541 return -1;
542 }

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)