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