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