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