c838b493c7d5f965bab2a2cfe6ee72de61e241d2
[fw/openocd] / src / rtos / ChibiOS.c
1 /***************************************************************************
2  *   Copyright (C) 2012 by Matthias Blaicher                               *
3  *   Matthias Blaicher - matthias@blaicher.com                             *
4  *                                                                         *
5  *   Copyright (C) 2011 by Broadcom Corporation                            *
6  *   Evan Hunter - ehunter@broadcom.com                                    *
7  *                                                                         *
8  *   This program is free software; you can redistribute it and/or modify  *
9  *   it under the terms of the GNU General Public License as published by  *
10  *   the Free Software Foundation; either version 2 of the License, or     *
11  *   (at your option) any later version.                                   *
12  *                                                                         *
13  *   This program is distributed in the hope that it will be useful,       *
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
16  *   GNU General Public License for more details.                          *
17  *                                                                         *
18  *   You should have received a copy of the GNU General Public License     *
19  *   along with this program; if not, write to the                         *
20  *   Free Software Foundation, Inc.,                                       *
21  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
22  ***************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <helper/time_support.h>
29 #include <jtag/jtag.h>
30 #include "target/target.h"
31 #include "target/target_type.h"
32 #include "rtos.h"
33 #include "helper/log.h"
34 #include "helper/types.h"
35 #include "rtos_chibios_stackings.h"
36
37
38 /**
39  * @brief   ChibiOS/RT memory signature record.
40  *
41  * @details Definition copied from os/kernel/include/chregistry.h of ChibiOS/RT.
42  */
43 struct ChibiOS_chdebug {
44         char      ch_identifier[4];       /**< @brief Always set to "main".       */
45         uint8_t   ch_zero;                /**< @brief Must be zero.               */
46         uint8_t   ch_size;                /**< @brief Size of this structure.     */
47         uint16_t  ch_version;             /**< @brief Encoded ChibiOS/RT version. */
48         uint8_t   ch_ptrsize;             /**< @brief Size of a pointer.          */
49         uint8_t   ch_timesize;            /**< @brief Size of a @p systime_t.     */
50         uint8_t   ch_threadsize;          /**< @brief Size of a @p Thread struct. */
51         uint8_t   cf_off_prio;            /**< @brief Offset of @p p_prio field.  */
52         uint8_t   cf_off_ctx;             /**< @brief Offset of @p p_ctx field.   */
53         uint8_t   cf_off_newer;           /**< @brief Offset of @p p_newer field. */
54         uint8_t   cf_off_older;           /**< @brief Offset of @p p_older field. */
55         uint8_t   cf_off_name;            /**< @brief Offset of @p p_name field.  */
56         uint8_t   cf_off_stklimit;        /**< @brief Offset of @p p_stklimit
57                                                                                                 field.                        */
58         uint8_t   cf_off_state;           /**< @brief Offset of @p p_state field. */
59         uint8_t   cf_off_flags;           /**< @brief Offset of @p p_flags field. */
60         uint8_t   cf_off_refs;            /**< @brief Offset of @p p_refs field.  */
61         uint8_t   cf_off_preempt;         /**< @brief Offset of @p p_preempt
62                                                                                                 field.                        */
63         uint8_t   cf_off_time;            /**< @brief Offset of @p p_time field.  */
64 };
65
66 #define GET_CH_KERNEL_MAJOR(codedVersion) ((codedVersion >> 11) & 0x1f)
67 #define GET_CH_KERNEL_MINOR(codedVersion) ((codedVersion >> 6) & 0x1f)
68 #define GET_CH_KERNEL_PATCH(codedVersion) ((codedVersion >> 0) & 0x3f)
69
70 /**
71  * @brief ChibiOS thread states.
72  */
73 const char *ChibiOS_thread_states[] = {
74         "READY", "CURRENT", "SUSPENDED", "WTSEM", "WTMTX", "WTCOND", "SLEEPING",
75         "WTEXIT", "WTOREVT", "WTANDEVT", "SNDMSGQ", "SNDMSG", "WTMSG", "WTQUEUE",
76         "FINAL"
77 };
78
79 #define CHIBIOS_NUM_STATES (sizeof(ChibiOS_thread_states)/sizeof(char *))
80
81 /* Maximum ChibiOS thread name. There is no real limit set by ChibiOS but 64
82  * chars ought to be enough.
83  */
84 #define CHIBIOS_THREAD_NAME_STR_SIZE (64)
85
86 struct ChibiOS_params {
87         const char *target_name;
88
89         struct ChibiOS_chdebug *signature;
90         const struct rtos_register_stacking *stacking_info;
91 };
92
93 struct ChibiOS_params ChibiOS_params_list[] = {
94         {
95         "cortex_m3",                                                    /* target_name */
96         0,
97         &rtos_chibios_arm_v7m_stacking,         /* stacking_info */
98         },
99         {
100         "stm32_stlink",                                                 /* target_name */
101         0,
102         &rtos_chibios_arm_v7m_stacking,         /* stacking_info */
103         }
104 };
105 #define CHIBIOS_NUM_PARAMS ((int)(sizeof(ChibiOS_params_list)/sizeof(struct ChibiOS_params)))
106
107 static int ChibiOS_detect_rtos(struct target *target);
108 static int ChibiOS_create(struct target *target);
109 static int ChibiOS_update_threads(struct rtos *rtos);
110 static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list);
111 static int ChibiOS_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]);
112
113 struct rtos_type ChibiOS_rtos = {
114         .name = "ChibiOS",
115
116         .detect_rtos = ChibiOS_detect_rtos,
117         .create = ChibiOS_create,
118         .update_threads = ChibiOS_update_threads,
119         .get_thread_reg_list = ChibiOS_get_thread_reg_list,
120         .get_symbol_list_to_lookup = ChibiOS_get_symbol_list_to_lookup,
121 };
122
123 enum ChibiOS_symbol_values {
124         ChibiOS_VAL_rlist = 0,
125         ChibiOS_VAL_ch_debug = 1,
126         ChibiOS_VAL_chSysInit = 2
127 };
128
129 static char *ChibiOS_symbol_list[] = {
130         "rlist",                /* Thread ready list*/
131         "ch_debug",             /* Memory Signatur containing offsets of fields in rlist*/
132         "chSysInit",    /* Necessary part of API, used for ChibiOS detection*/
133         NULL
134 };
135
136 static int ChibiOS_update_memory_signature(struct rtos *rtos)
137 {
138         int retval;
139         struct ChibiOS_params *param;
140         struct ChibiOS_chdebug *signature;
141
142         param = (struct ChibiOS_params *) rtos->rtos_specific_params;
143
144         /* Free existing memory description.*/
145         if (param->signature) {
146                 free(param->signature);
147                 param->signature = 0;
148         }
149
150         signature = malloc(sizeof(*signature));
151         if (!signature) {
152                 LOG_ERROR("Could not allocate space for ChibiOS/RT memory signature");
153                 return -1;
154         }
155
156         retval = target_read_buffer(rtos->target,
157                                                                 rtos->symbols[ChibiOS_VAL_ch_debug].address,
158                                                                 sizeof(*signature),
159                                                                 (uint8_t *) signature);
160         if (retval != ERROR_OK) {
161                 LOG_ERROR("Could not read ChibiOS/RT memory signature from target");
162                 goto errfree;
163         }
164
165         if (strncmp(signature->ch_identifier, "main", 4) != 0) {
166                 LOG_ERROR("Memory signature identifier does not contain magic bytes.");
167                 goto errfree;
168         }
169
170         if (signature->ch_size < sizeof(*signature)) {
171                 LOG_ERROR("ChibiOS/RT memory signature claims to be smaller "
172                                 "than expected");
173                 goto errfree;
174         }
175
176         if (signature->ch_size > sizeof(*signature)) {
177                 LOG_WARNING("ChibiOS/RT memory signature claims to be bigger than"
178                                         " expected. Assuming compatibility...");
179         }
180
181         /* Convert endianness of version field */
182         const uint8_t *versionTarget = (const uint8_t *)
183                                                                                 &signature->ch_version;
184         signature->ch_version = rtos->target->endianness == TARGET_LITTLE_ENDIAN ?
185                         le_to_h_u32(versionTarget) : be_to_h_u32(versionTarget);
186
187         const uint16_t ch_version = signature->ch_version;
188         LOG_INFO("Successfully loaded memory map of ChibiOS/RT target "
189                         "running version %i.%i.%i", GET_CH_KERNEL_MAJOR(ch_version),
190                         GET_CH_KERNEL_MINOR(ch_version), GET_CH_KERNEL_PATCH(ch_version));
191
192         param->signature = signature;
193         return 0;
194
195 errfree:
196         /* Error reading the ChibiOS memory structure */
197         free(signature);
198         param->signature = 0;
199         return -1;
200 }
201
202
203 static int ChibiOS_update_stacking(struct rtos *rtos)
204 {
205         /* Sometimes the stacking can not be determined only by looking at the
206          * target name but only a runtime.
207          *
208          * For example, this is the case for cortex-m4 targets and ChibiOS which
209          * only stack the FPU registers if it is enabled during ChibiOS build.
210          *
211          * Terminating which stacking is used is target depending.
212          *
213          * Assumptions:
214          *  - Once ChibiOS is actually initialized, the stacking is fixed.
215          *  - During startup code, the FPU might not be initialized and the
216          *    detection might fail.
217          *  - Since no threads are running during startup, the problem is solved
218          *    by delaying stacking detection until there are more threads
219          *    available than the current execution. In which case
220          *    ChibiOS_get_thread_reg_list is called.
221          */
222
223         /* TODO: Add actual detection, currently it will not work  with FPU enabled.*/
224         return -1;
225 }
226
227 static int ChibiOS_update_threads(struct rtos *rtos)
228 {
229         int retval;
230         const struct ChibiOS_params *param;
231         int tasks_found = 0;
232         int rtos_valid = -1;
233
234         if (!rtos->rtos_specific_params)
235                 return -1;
236
237         if (!rtos->symbols) {
238                 LOG_ERROR("No symbols for ChibiOS");
239                 return -3;
240         }
241
242         param = (const struct ChibiOS_params *) rtos->rtos_specific_params;
243         /* Update the memory signature saved in the target memory */
244         if (!param->signature) {
245                 retval = ChibiOS_update_memory_signature(rtos);
246                 if (retval != ERROR_OK) {
247                         LOG_ERROR("Reading the memory signature of ChibiOS/RT failed");
248                         return retval;
249                 }
250         }
251
252         /* wipe out previous thread details if any */
253         int j;
254         if (rtos->thread_details) {
255                 for (j = 0; j < rtos->thread_count; j++) {
256                         struct thread_detail *current_thread = &rtos->thread_details[j];
257                         if (current_thread->display_str != NULL)
258                                 free(current_thread->display_str);
259                         if (current_thread->thread_name_str != NULL)
260                                 free(current_thread->thread_name_str);
261                         if (current_thread->extra_info_str != NULL)
262                                 free(current_thread->extra_info_str);
263                 }
264                 free(rtos->thread_details);
265                 rtos->thread_details = NULL;
266                 rtos->thread_count = 0;
267         }
268         /* ChibiOS does not save the current thread count. We have to first
269          * parse the double linked thread list to check for errors and the number of
270          * threads. */
271         uint32_t rlist;
272         uint32_t current;
273         uint32_t previous;
274         uint32_t older;
275
276         retval = target_read_buffer(rtos->target,
277                 rtos->symbols[ChibiOS_VAL_rlist].address,
278                 param->signature->ch_ptrsize,
279                 (uint8_t *)&rlist);
280         if (retval != ERROR_OK) {
281                 LOG_ERROR("Could not read ChibiOS ReadyList from target");
282                 return retval;
283         }
284         current = rlist;
285         previous = rlist;
286         while (1) {
287                 retval = target_read_buffer(rtos->target,
288                         current + param->signature->cf_off_newer,
289                         param->signature->ch_ptrsize,
290                         (uint8_t *)&current);
291                 if (retval != ERROR_OK) {
292                         LOG_ERROR("Could not read next ChibiOS thread");
293                         return retval;
294                 }
295                 /* Could be NULL if the kernel is not initialized yet or if the
296                  * registry is corrupted. */
297                 if (current == 0) {
298                         LOG_ERROR("ChibiOS registry integrity check failed, NULL pointer");
299
300                         rtos_valid = 0;
301                         break;
302                 }
303                 /* Fetch previous thread in the list as a integrity check. */
304                 retval = target_read_buffer(rtos->target,
305                   current + param->signature->cf_off_older,
306                   param->signature->ch_ptrsize,
307                   (uint8_t *)&older);
308                 if ((retval != ERROR_OK) || (older == 0) || (older != previous)) {
309                         LOG_ERROR("ChibiOS registry integrity check failed, "
310                                                 "double linked list violation");
311                         rtos_valid = 0;
312                         break;
313                 }
314                 /* Check for full iteration of the linked list. */
315                 if (current == rlist)
316                         break;
317                 tasks_found++;
318                 previous = current;
319         }
320         if (!rtos_valid) {
321                 /* No RTOS, there is always at least the current execution, though */
322                 LOG_INFO("Only showing current execution because of a broken "
323                                 "ChibiOS thread registry.");
324
325                 const char tmp_thread_name[] = "Current Execution";
326                 const char tmp_thread_extra_info[] = "No RTOS thread";
327
328                 rtos->thread_details = (struct thread_detail *) malloc(
329                                 sizeof(struct thread_detail));
330                 rtos->thread_details->threadid = 1;
331                 rtos->thread_details->exists = true;
332                 rtos->thread_details->display_str = NULL;
333
334                 rtos->thread_details->extra_info_str = (char *) malloc(
335                                 sizeof(tmp_thread_extra_info));
336                 strcpy(rtos->thread_details->extra_info_str, tmp_thread_extra_info);
337
338                 rtos->thread_details->thread_name_str = (char *) malloc(
339                                 sizeof(tmp_thread_name));
340                 strcpy(rtos->thread_details->thread_name_str, tmp_thread_name);
341
342                 rtos->current_thread = 1;
343                 rtos->thread_count = 1;
344                 return ERROR_OK;
345         }
346
347         /* create space for new thread details */
348         rtos->thread_details = (struct thread_detail *) malloc(
349                         sizeof(struct thread_detail) * tasks_found);
350         if (!rtos->thread_details) {
351                 LOG_ERROR("Could not allocate space for thread details");
352                 return -1;
353         }
354
355         rtos->thread_count = tasks_found;
356         /* Loop through linked list. */
357         struct thread_detail *curr_thrd_details = rtos->thread_details;
358         while (curr_thrd_details < rtos->thread_details + tasks_found) {
359                 uint32_t name_ptr = 0;
360                 char tmp_str[CHIBIOS_THREAD_NAME_STR_SIZE];
361
362                 retval = target_read_buffer(rtos->target,
363                                                                         current + param->signature->cf_off_newer,
364                                                                         param->signature->ch_ptrsize,
365                                                                         (uint8_t *)&current);
366                 if (retval != ERROR_OK) {
367                         LOG_ERROR("Could not read next ChibiOS thread");
368                         return -6;
369                 }
370
371                 /* Check for full iteration of the linked list. */
372                 if (current == rlist)
373                         break;
374
375                 /* Save the thread pointer */
376                 curr_thrd_details->threadid = current;
377
378                 /* read the name pointer */
379                 retval = target_read_buffer(rtos->target,
380                                                                         current + param->signature->cf_off_name,
381                                                                         param->signature->ch_ptrsize,
382                                                                         (uint8_t *)&name_ptr);
383                 if (retval != ERROR_OK) {
384                         LOG_ERROR("Could not read ChibiOS thread name pointer from target");
385                         return retval;
386                 }
387
388                 /* Read the thread name */
389                 retval = target_read_buffer(rtos->target, name_ptr,
390                                                                         CHIBIOS_THREAD_NAME_STR_SIZE,
391                                                                         (uint8_t *)&tmp_str);
392                 if (retval != ERROR_OK) {
393                         LOG_ERROR("Error reading thread name from ChibiOS target");
394                         return retval;
395                 }
396                 tmp_str[CHIBIOS_THREAD_NAME_STR_SIZE - 1] = '\x00';
397
398                 if (tmp_str[0] == '\x00')
399                         strcpy(tmp_str, "No Name");
400
401                 curr_thrd_details->thread_name_str = (char *)malloc(
402                                 strlen(tmp_str) + 1);
403                 strcpy(curr_thrd_details->thread_name_str, tmp_str);
404
405                 /* State info */
406                 uint8_t threadState;
407                 const char *state_desc;
408
409                 retval = target_read_buffer(rtos->target,
410                                                                         current + param->signature->cf_off_state,
411                                                                         1, &threadState);
412                 if (retval != ERROR_OK) {
413                         LOG_ERROR("Error reading thread state from ChibiOS target");
414                         return retval;
415                 }
416
417
418                 if (threadState < CHIBIOS_NUM_STATES)
419                         state_desc = ChibiOS_thread_states[threadState];
420                 else
421                         state_desc = "Unknown state";
422
423                 curr_thrd_details->extra_info_str = (char *)malloc(strlen(
424                                         state_desc)+1);
425                 strcpy(curr_thrd_details->extra_info_str, state_desc);
426
427                 curr_thrd_details->exists = true;
428                 curr_thrd_details->display_str = NULL;
429
430                 curr_thrd_details++;
431         }
432         /* NOTE: By design, cf_off_name equals readylist_current_offset */
433         retval = target_read_buffer(rtos->target,
434                                                                 rlist + param->signature->cf_off_name,
435                                                                 param->signature->ch_ptrsize,
436                                                                 (uint8_t *)&rtos->current_thread);
437         if (retval != ERROR_OK) {
438                 LOG_ERROR("Could not read current Thread from ChibiOS target");
439                 return retval;
440         }
441
442         return 0;
443 }
444
445 static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list)
446 {
447         int retval;
448         const struct ChibiOS_params *param;
449         int64_t stack_ptr = 0;
450
451         *hex_reg_list = NULL;
452         if ((rtos == NULL) || (thread_id == 0) ||
453                         (rtos->rtos_specific_params == NULL))
454                 return -1;
455
456         param = (const struct ChibiOS_params *) rtos->rtos_specific_params;
457
458         if (!param->signature)
459                 return -1;
460
461         /* Update stacking if it can only be determined from runtime information */
462         if ((param->stacking_info == 0) &&
463                 (ChibiOS_update_stacking(rtos) != ERROR_OK)) {
464                 LOG_ERROR("Failed to determine exact stacking for the target type %s", rtos->target->type->name);
465                 return -1;
466         }
467
468         /* Read the stack pointer */
469         retval = target_read_buffer(rtos->target,
470                         thread_id + param->signature->cf_off_ctx,
471                         param->signature->ch_ptrsize,
472                         (uint8_t *)&stack_ptr);
473         if (retval != ERROR_OK) {
474                 LOG_ERROR("Error reading stack frame from ChibiOS thread");
475                 return retval;
476         }
477
478         return rtos_generic_stack_read(rtos->target, param->stacking_info, stack_ptr, hex_reg_list);
479 }
480
481 static int ChibiOS_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[])
482 {
483         unsigned int i;
484         *symbol_list = (symbol_table_elem_t *) malloc(
485                         sizeof(symbol_table_elem_t) * ARRAY_SIZE(ChibiOS_symbol_list));
486
487         for (i = 0; i < ARRAY_SIZE(ChibiOS_symbol_list); i++)
488                 (*symbol_list)[i].symbol_name = ChibiOS_symbol_list[i];
489
490         return 0;
491 }
492
493 static int ChibiOS_detect_rtos(struct target *target)
494 {
495         if ((target->rtos->symbols != NULL) &&
496                         (target->rtos->symbols[ChibiOS_VAL_rlist].address != 0) &&
497                         (target->rtos->symbols[ChibiOS_VAL_chSysInit].address != 0)) {
498
499                 if (target->rtos->symbols[ChibiOS_VAL_ch_debug].address == 0) {
500                         LOG_INFO("It looks like the target is running ChibiOS without "
501                                         "ch_debug.");
502                         return 0;
503                 }
504
505                 /* looks like ChibiOS with memory map enabled.*/
506                 return 1;
507         }
508
509         return 0;
510 }
511
512 static int ChibiOS_create(struct target *target)
513 {
514         int i = 0;
515         while ((i < CHIBIOS_NUM_PARAMS) &&
516                         (0 != strcmp(ChibiOS_params_list[i].target_name, target->type->name))) {
517                 i++;
518         }
519         if (i >= CHIBIOS_NUM_PARAMS) {
520                 LOG_WARNING("Could not find target \"%s\" in ChibiOS compatibility "
521                                 "list", target->type->name);
522                 return -1;
523         }
524
525         target->rtos->rtos_specific_params = (void *) &ChibiOS_params_list[i];
526         return 0;
527 }