754fe064b05ea4bd4db6b4b9df816c138dc8362f
[fw/openocd] / src / rtos / uCOS-III.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2
3 /***************************************************************************
4  *   Copyright (C) 2017 by Square, Inc.                                    *
5  *   Steven Stallion <stallion@squareup.com>                               *
6  ***************************************************************************/
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11
12 #include <helper/log.h>
13 #include <helper/time_support.h>
14 #include <helper/types.h>
15 #include <rtos/rtos.h>
16 #include <target/target.h>
17 #include <target/target_type.h>
18
19 #include "rtos_ucos_iii_stackings.h"
20
21 #ifndef UCOS_III_MAX_STRLEN
22 #define UCOS_III_MAX_STRLEN 64
23 #endif
24
25 #ifndef UCOS_III_MAX_THREADS
26 #define UCOS_III_MAX_THREADS 256
27 #endif
28
29 struct ucos_iii_params {
30         const char *target_name;
31         const unsigned char pointer_width;
32         symbol_address_t thread_stack_offset;
33         symbol_address_t thread_name_offset;
34         symbol_address_t thread_state_offset;
35         symbol_address_t thread_priority_offset;
36         symbol_address_t thread_prev_offset;
37         symbol_address_t thread_next_offset;
38         bool thread_offsets_updated;
39         size_t threadid_start;
40         const struct rtos_register_stacking *stacking_info;
41         size_t num_threads;
42         symbol_address_t threads[];
43 };
44
45 static const struct ucos_iii_params ucos_iii_params_list[] = {
46         {
47                 "cortex_m",                                                     /* target_name */
48                 sizeof(uint32_t),                                       /* pointer_width */
49                 0,                                                                      /* thread_stack_offset */
50                 0,                                                                      /* thread_name_offset */
51                 0,                                                                      /* thread_state_offset */
52                 0,                                                                      /* thread_priority_offset */
53                 0,                                                                      /* thread_prev_offset */
54                 0,                                                                      /* thread_next_offset */
55                 false,                                                          /* thread_offsets_updated */
56                 1,                                                                      /* threadid_start */
57                 &rtos_ucos_iii_cortex_m_stacking,       /* stacking_info */
58                 0,                                                                      /* num_threads */
59         },
60         {
61                 "esirisc",                                                      /* target_name */
62                 sizeof(uint32_t),                                       /* pointer_width */
63                 0,                                                                      /* thread_stack_offset */
64                 0,                                                                      /* thread_name_offset */
65                 0,                                                                      /* thread_state_offset */
66                 0,                                                                      /* thread_priority_offset */
67                 0,                                                                      /* thread_prev_offset */
68                 0,                                                                      /* thread_next_offset */
69                 false,                                                          /* thread_offsets_updated */
70                 1,                                                                      /* threadid_start */
71                 &rtos_ucos_iii_esi_risc_stacking,       /* stacking_info */
72                 0,                                                                      /* num_threads */
73         },
74 };
75
76 static const char * const ucos_iii_symbol_list[] = {
77         "OSRunning",
78         "OSTCBCurPtr",
79         "OSTaskDbgListPtr",
80         "OSTaskQty",
81
82         /* also see: contrib/rtos-helpers/uCOS-III-openocd.c */
83         "openocd_OS_TCB_StkPtr_offset",
84         "openocd_OS_TCB_NamePtr_offset",
85         "openocd_OS_TCB_TaskState_offset",
86         "openocd_OS_TCB_Prio_offset",
87         "openocd_OS_TCB_DbgPrevPtr_offset",
88         "openocd_OS_TCB_DbgNextPtr_offset",
89         NULL
90 };
91
92 enum ucos_iii_symbol_values {
93         UCOS_III_VAL_OS_RUNNING,
94         UCOS_III_VAL_OS_TCB_CUR_PTR,
95         UCOS_III_VAL_OS_TASK_DBG_LIST_PTR,
96         UCOS_III_VAL_OS_TASK_QTY,
97
98         /* also see: contrib/rtos-helpers/uCOS-III-openocd.c */
99         UCOS_III_VAL_OS_TCB_STK_PTR_OFFSET,
100         UCOS_III_VAL_OS_TCB_NAME_PTR_OFFSET,
101         UCOS_III_VAL_OS_TCB_TASK_STATE_OFFSET,
102         UCOS_III_VAL_OS_TCB_PRIO_OFFSET,
103         UCOS_III_VAL_OS_TCB_DBG_PREV_PTR_OFFSET,
104         UCOS_III_VAL_OS_TCB_DBG_NEXT_PTR_OFFSET,
105 };
106
107 static const char * const ucos_iii_thread_state_list[] = {
108         "Ready",
109         "Delay",
110         "Pend",
111         "Pend Timeout",
112         "Suspended",
113         "Delay Suspended",
114         "Pend Suspended",
115         "Pend Timeout Suspended",
116 };
117
118 static int ucos_iii_find_or_create_thread(struct rtos *rtos, symbol_address_t thread_address,
119                 threadid_t *threadid)
120 {
121         struct ucos_iii_params *params = rtos->rtos_specific_params;
122         size_t thread_index;
123
124         for (thread_index = 0; thread_index < params->num_threads; thread_index++)
125                 if (params->threads[thread_index] == thread_address)
126                         goto found;
127
128         if (params->num_threads == UCOS_III_MAX_THREADS) {
129                 LOG_WARNING("uCOS-III: too many threads; increase UCOS_III_MAX_THREADS");
130                 return ERROR_FAIL;
131         }
132
133         params->threads[thread_index] = thread_address;
134         params->num_threads++;
135 found:
136         *threadid = thread_index + params->threadid_start;
137         return ERROR_OK;
138 }
139
140 static int ucos_iii_find_thread_address(struct rtos *rtos, threadid_t threadid,
141                 symbol_address_t *thread_address)
142 {
143         struct ucos_iii_params *params = rtos->rtos_specific_params;
144         size_t thread_index;
145
146         thread_index = threadid - params->threadid_start;
147         if (thread_index >= params->num_threads) {
148                 LOG_ERROR("uCOS-III: failed to find thread address");
149                 return ERROR_FAIL;
150         }
151
152         *thread_address = params->threads[thread_index];
153         return ERROR_OK;
154 }
155
156 static int ucos_iii_find_last_thread_address(struct rtos *rtos, symbol_address_t *thread_address)
157 {
158         struct ucos_iii_params *params = rtos->rtos_specific_params;
159         int retval;
160
161         /* read the thread list head */
162         symbol_address_t thread_list_address = 0;
163
164         retval = target_read_memory(rtos->target,
165                         rtos->symbols[UCOS_III_VAL_OS_TASK_DBG_LIST_PTR].address,
166                         params->pointer_width,
167                         1,
168                         (void *)&thread_list_address);
169         if (retval != ERROR_OK) {
170                 LOG_ERROR("uCOS-III: failed to read thread list address");
171                 return retval;
172         }
173
174         /* advance to end of thread list */
175         do {
176                 *thread_address = thread_list_address;
177
178                 retval = target_read_memory(rtos->target,
179                                 thread_list_address + params->thread_next_offset,
180                                 params->pointer_width,
181                                 1,
182                                 (void *)&thread_list_address);
183                 if (retval != ERROR_OK) {
184                         LOG_ERROR("uCOS-III: failed to read next thread address");
185                         return retval;
186                 }
187         } while (thread_list_address != 0);
188
189         return ERROR_OK;
190 }
191
192 static int ucos_iii_update_thread_offsets(struct rtos *rtos)
193 {
194         struct ucos_iii_params *params = rtos->rtos_specific_params;
195
196         if (params->thread_offsets_updated)
197                 return ERROR_OK;
198
199         const struct thread_offset_map {
200                 enum ucos_iii_symbol_values symbol_value;
201                 symbol_address_t *thread_offset;
202         } thread_offset_maps[] = {
203                 {
204                         UCOS_III_VAL_OS_TCB_STK_PTR_OFFSET,
205                         &params->thread_stack_offset,
206                 },
207                 {
208                         UCOS_III_VAL_OS_TCB_NAME_PTR_OFFSET,
209                         &params->thread_name_offset,
210                 },
211                 {
212                         UCOS_III_VAL_OS_TCB_TASK_STATE_OFFSET,
213                         &params->thread_state_offset,
214                 },
215                 {
216                         UCOS_III_VAL_OS_TCB_PRIO_OFFSET,
217                         &params->thread_priority_offset,
218                 },
219                 {
220                         UCOS_III_VAL_OS_TCB_DBG_PREV_PTR_OFFSET,
221                         &params->thread_prev_offset,
222                 },
223                 {
224                         UCOS_III_VAL_OS_TCB_DBG_NEXT_PTR_OFFSET,
225                         &params->thread_next_offset,
226                 },
227         };
228
229         for (size_t i = 0; i < ARRAY_SIZE(thread_offset_maps); i++) {
230                 const struct thread_offset_map *thread_offset_map = &thread_offset_maps[i];
231
232                 int retval = target_read_memory(rtos->target,
233                                 rtos->symbols[thread_offset_map->symbol_value].address,
234                                 params->pointer_width,
235                                 1,
236                                 (void *)thread_offset_map->thread_offset);
237                 if (retval != ERROR_OK) {
238                         LOG_ERROR("uCOS-III: failed to read thread offset");
239                         return retval;
240                 }
241         }
242
243         params->thread_offsets_updated = true;
244         return ERROR_OK;
245 }
246
247 static bool ucos_iii_detect_rtos(struct target *target)
248 {
249         return target->rtos->symbols &&
250                         target->rtos->symbols[UCOS_III_VAL_OS_RUNNING].address != 0;
251 }
252
253 static int ucos_iii_reset_handler(struct target *target, enum target_reset_mode reset_mode, void *priv)
254 {
255         struct ucos_iii_params *params = target->rtos->rtos_specific_params;
256
257         params->thread_offsets_updated = false;
258         params->num_threads = 0;
259
260         return ERROR_OK;
261 }
262
263 static int ucos_iii_create(struct target *target)
264 {
265         struct ucos_iii_params *params;
266
267         for (size_t i = 0; i < ARRAY_SIZE(ucos_iii_params_list); i++)
268                 if (strcmp(ucos_iii_params_list[i].target_name, target->type->name) == 0) {
269                         params = malloc(sizeof(*params) + (UCOS_III_MAX_THREADS * sizeof(*params->threads)));
270                         if (!params) {
271                                 LOG_ERROR("uCOS-III: out of memory");
272                                 return ERROR_FAIL;
273                         }
274
275                         memcpy(params, &ucos_iii_params_list[i], sizeof(ucos_iii_params_list[i]));
276                         target->rtos->rtos_specific_params = (void *)params;
277
278                         target_register_reset_callback(ucos_iii_reset_handler, NULL);
279
280                         return ERROR_OK;
281                 }
282
283         LOG_ERROR("uCOS-III: target not supported: %s", target->type->name);
284         return ERROR_FAIL;
285 }
286
287 static int ucos_iii_update_threads(struct rtos *rtos)
288 {
289         struct ucos_iii_params *params = rtos->rtos_specific_params;
290         int retval;
291
292         if (!rtos->symbols) {
293                 LOG_ERROR("uCOS-III: symbol list not loaded");
294                 return ERROR_FAIL;
295         }
296
297         /* free previous thread details */
298         rtos_free_threadlist(rtos);
299
300         /* verify RTOS is running */
301         uint8_t rtos_running;
302
303         retval = target_read_u8(rtos->target,
304                         rtos->symbols[UCOS_III_VAL_OS_RUNNING].address,
305                         &rtos_running);
306         if (retval != ERROR_OK) {
307                 LOG_ERROR("uCOS-III: failed to read RTOS running");
308                 return retval;
309         }
310
311         if (rtos_running != 1 && rtos_running != 0) {
312                 LOG_ERROR("uCOS-III: invalid RTOS running value");
313                 return ERROR_FAIL;
314         }
315
316         if (!rtos_running) {
317                 rtos->thread_details = calloc(1, sizeof(struct thread_detail));
318                 if (!rtos->thread_details) {
319                         LOG_ERROR("uCOS-III: out of memory");
320                         return ERROR_FAIL;
321                 }
322
323                 rtos->thread_count = 1;
324                 rtos->thread_details->threadid = 0;
325                 rtos->thread_details->exists = true;
326                 rtos->current_thread = 0;
327
328                 return ERROR_OK;
329         }
330
331         /* update thread offsets */
332         retval = ucos_iii_update_thread_offsets(rtos);
333         if (retval != ERROR_OK) {
334                 LOG_ERROR("uCOS-III: failed to update thread offsets");
335                 return retval;
336         }
337
338         /* read current thread address */
339         symbol_address_t current_thread_address = 0;
340
341         retval = target_read_memory(rtos->target,
342                         rtos->symbols[UCOS_III_VAL_OS_TCB_CUR_PTR].address,
343                         params->pointer_width,
344                         1,
345                         (void *)&current_thread_address);
346         if (retval != ERROR_OK) {
347                 LOG_ERROR("uCOS-III: failed to read current thread address");
348                 return retval;
349         }
350
351         /* read number of tasks */
352         retval = target_read_u16(rtos->target,
353                         rtos->symbols[UCOS_III_VAL_OS_TASK_QTY].address,
354                         (void *)&rtos->thread_count);
355         if (retval != ERROR_OK) {
356                 LOG_ERROR("uCOS-III: failed to read thread count");
357                 return retval;
358         }
359
360         rtos->thread_details = calloc(rtos->thread_count, sizeof(struct thread_detail));
361         if (!rtos->thread_details) {
362                 LOG_ERROR("uCOS-III: out of memory");
363                 return ERROR_FAIL;
364         }
365
366         /*
367          * uC/OS-III adds tasks in LIFO order; advance to the end of the
368          * list and work backwards to preserve the intended order.
369          */
370         symbol_address_t thread_address = 0;
371
372         retval = ucos_iii_find_last_thread_address(rtos, &thread_address);
373         if (retval != ERROR_OK) {
374                 LOG_ERROR("uCOS-III: failed to find last thread address");
375                 return retval;
376         }
377
378         for (int i = 0; i < rtos->thread_count; i++) {
379                 struct thread_detail *thread_detail = &rtos->thread_details[i];
380                 char thread_str_buffer[UCOS_III_MAX_STRLEN + 1];
381
382                 /* find or create new threadid */
383                 retval = ucos_iii_find_or_create_thread(rtos, thread_address, &thread_detail->threadid);
384                 if (retval != ERROR_OK) {
385                         LOG_ERROR("uCOS-III: failed to find or create thread");
386                         return retval;
387                 }
388
389                 if (thread_address == current_thread_address)
390                         rtos->current_thread = thread_detail->threadid;
391
392                 thread_detail->exists = true;
393
394                 /* read thread name */
395                 symbol_address_t thread_name_address = 0;
396
397                 retval = target_read_memory(rtos->target,
398                                 thread_address + params->thread_name_offset,
399                                 params->pointer_width,
400                                 1,
401                                 (void *)&thread_name_address);
402                 if (retval != ERROR_OK) {
403                         LOG_ERROR("uCOS-III: failed to name address");
404                         return retval;
405                 }
406
407                 retval = target_read_buffer(rtos->target,
408                                 thread_name_address,
409                                 sizeof(thread_str_buffer),
410                                 (void *)thread_str_buffer);
411                 if (retval != ERROR_OK) {
412                         LOG_ERROR("uCOS-III: failed to read thread name");
413                         return retval;
414                 }
415
416                 thread_str_buffer[sizeof(thread_str_buffer) - 1] = '\0';
417                 thread_detail->thread_name_str = strdup(thread_str_buffer);
418
419                 /* read thread extra info */
420                 uint8_t thread_state;
421                 uint8_t thread_priority;
422
423                 retval = target_read_u8(rtos->target,
424                                 thread_address + params->thread_state_offset,
425                                 &thread_state);
426                 if (retval != ERROR_OK) {
427                         LOG_ERROR("uCOS-III: failed to read thread state");
428                         return retval;
429                 }
430
431                 retval = target_read_u8(rtos->target,
432                                 thread_address + params->thread_priority_offset,
433                                 &thread_priority);
434                 if (retval != ERROR_OK) {
435                         LOG_ERROR("uCOS-III: failed to read thread priority");
436                         return retval;
437                 }
438
439                 const char *thread_state_str;
440
441                 if (thread_state < ARRAY_SIZE(ucos_iii_thread_state_list))
442                         thread_state_str = ucos_iii_thread_state_list[thread_state];
443                 else
444                         thread_state_str = "Unknown";
445
446                 snprintf(thread_str_buffer, sizeof(thread_str_buffer), "State: %s, Priority: %d",
447                                 thread_state_str, thread_priority);
448                 thread_detail->extra_info_str = strdup(thread_str_buffer);
449
450                 /* read previous thread address */
451                 retval = target_read_memory(rtos->target,
452                                 thread_address + params->thread_prev_offset,
453                                 params->pointer_width,
454                                 1,
455                                 (void *)&thread_address);
456                 if (retval != ERROR_OK) {
457                         LOG_ERROR("uCOS-III: failed to read previous thread address");
458                         return retval;
459                 }
460         }
461
462         return ERROR_OK;
463 }
464
465 static int ucos_iii_get_thread_reg_list(struct rtos *rtos, threadid_t threadid,
466                 struct rtos_reg **reg_list, int *num_regs)
467 {
468         struct ucos_iii_params *params = rtos->rtos_specific_params;
469         int retval;
470
471         /* find thread address for threadid */
472         symbol_address_t thread_address = 0;
473
474         retval = ucos_iii_find_thread_address(rtos, threadid, &thread_address);
475         if (retval != ERROR_OK) {
476                 LOG_ERROR("uCOS-III: failed to find thread address");
477                 return retval;
478         }
479
480         /* read thread stack address */
481         symbol_address_t stack_address = 0;
482
483         retval = target_read_memory(rtos->target,
484                         thread_address + params->thread_stack_offset,
485                         params->pointer_width,
486                         1,
487                         (void *)&stack_address);
488         if (retval != ERROR_OK) {
489                 LOG_ERROR("uCOS-III: failed to read stack address");
490                 return retval;
491         }
492
493         return rtos_generic_stack_read(rtos->target,
494                         params->stacking_info,
495                         stack_address,
496                         reg_list,
497                         num_regs);
498 }
499
500 static int ucos_iii_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[])
501 {
502         *symbol_list = calloc(ARRAY_SIZE(ucos_iii_symbol_list), sizeof(struct symbol_table_elem));
503         if (!*symbol_list) {
504                 LOG_ERROR("uCOS-III: out of memory");
505                 return ERROR_FAIL;
506         }
507
508         for (size_t i = 0; i < ARRAY_SIZE(ucos_iii_symbol_list); i++)
509                 (*symbol_list)[i].symbol_name = ucos_iii_symbol_list[i];
510
511         return ERROR_OK;
512 }
513
514 const struct rtos_type ucos_iii_rtos = {
515         .name = "uCOS-III",
516         .detect_rtos = ucos_iii_detect_rtos,
517         .create = ucos_iii_create,
518         .update_threads = ucos_iii_update_threads,
519         .get_thread_reg_list = ucos_iii_get_thread_reg_list,
520         .get_symbol_list_to_lookup = ucos_iii_get_symbol_list_to_lookup,
521 };