1 /***************************************************************************
2 * Copyright (C) 2012 by Matthias Blaicher *
3 * Matthias Blaicher - matthias@blaicher.com *
5 * Copyright (C) 2011 by Broadcom Corporation *
6 * Evan Hunter - ehunter@broadcom.com *
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. *
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. *
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 ***************************************************************************/
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"
33 #include "helper/log.h"
34 #include "helper/types.h"
35 #include "rtos_chibios_stackings.h"
38 * @brief ChibiOS/RT memory signature record.
40 * @details Definition copied from os/kernel/include/chregistry.h of ChibiOS/RT.
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
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
62 uint8_t cf_off_time; /**< @brief Offset of @p p_time field. */
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)
70 * @brief ChibiOS thread states.
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"
77 #define CHIBIOS_NUM_STATES (sizeof(chibios_thread_states)/sizeof(char *))
79 /* Maximum ChibiOS thread name. There is no real limit set by ChibiOS but 64
80 * chars ought to be enough.
82 #define CHIBIOS_THREAD_NAME_STR_SIZE (64)
84 struct chibios_params {
85 const char *target_name;
87 struct chibios_chdebug *signature;
88 const struct rtos_register_stacking *stacking_info;
91 static struct chibios_params chibios_params_list[] = {
93 "cortex_m", /* target_name */
95 NULL, /* stacking_info */
98 "hla_target", /* target_name */
100 NULL, /* stacking_info */
103 #define CHIBIOS_NUM_PARAMS ((int)(sizeof(chibios_params_list)/sizeof(struct chibios_params)))
105 static bool chibios_detect_rtos(struct target *target);
106 static int chibios_create(struct target *target);
107 static int chibios_update_threads(struct rtos *rtos);
108 static int chibios_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
109 struct rtos_reg **reg_list, int *num_regs);
110 static int chibios_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]);
112 struct rtos_type chibios_rtos = {
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,
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.
128 enum chibios_symbol_values {
129 CHIBIOS_VAL_RLIST = 0,
131 CHIBIOS_VAL_CH_DEBUG = 2
134 static symbol_table_elem_t chibios_symbol_list[] = {
135 { "rlist", 0, true}, /* Thread ready list */
136 { "ch", 0, true}, /* System data structure */
137 { "ch_debug", 0, false}, /* Memory Signature containing offsets of fields in rlist */
141 /* Offset of the rlist structure within the system data structure (ch) */
142 #define CH_RLIST_OFFSET 0x00
144 static int chibios_update_memory_signature(struct rtos *rtos)
147 struct chibios_params *param;
148 struct chibios_chdebug *signature;
150 param = (struct chibios_params *) rtos->rtos_specific_params;
152 /* Free existing memory description.*/
153 free(param->signature);
154 param->signature = NULL;
156 signature = malloc(sizeof(*signature));
158 LOG_ERROR("Could not allocate space for ChibiOS/RT memory signature");
162 retval = target_read_buffer(rtos->target,
163 rtos->symbols[CHIBIOS_VAL_CH_DEBUG].address,
165 (uint8_t *) signature);
166 if (retval != ERROR_OK) {
167 LOG_ERROR("Could not read ChibiOS/RT memory signature from target");
171 if (strncmp(signature->ch_identifier, "main", 4) != 0) {
172 LOG_ERROR("Memory signature identifier does not contain magic bytes.");
176 if (signature->ch_size < sizeof(*signature)) {
177 LOG_ERROR("ChibiOS/RT memory signature claims to be smaller "
182 if (signature->ch_size > sizeof(*signature)) {
183 LOG_WARNING("ChibiOS/RT memory signature claims to be bigger than"
184 " expected. Assuming compatibility...");
187 /* Convert endianness of version field */
188 const uint8_t *versionTarget = (const uint8_t *)
189 &signature->ch_version;
190 signature->ch_version = rtos->target->endianness == TARGET_LITTLE_ENDIAN ?
191 le_to_h_u32(versionTarget) : be_to_h_u32(versionTarget);
193 const uint16_t ch_version = signature->ch_version;
194 LOG_INFO("Successfully loaded memory map of ChibiOS/RT target "
195 "running version %i.%i.%i", GET_CH_KERNEL_MAJOR(ch_version),
196 GET_CH_KERNEL_MINOR(ch_version), GET_CH_KERNEL_PATCH(ch_version));
198 /* Currently, we have the inherent assumption that all address pointers
199 * are 32 bit wide. */
200 if (signature->ch_ptrsize != sizeof(uint32_t)) {
201 LOG_ERROR("ChibiOS/RT target memory signature claims an address "
202 "width unequal to 32 bits!");
207 param->signature = signature;
211 /* Error reading the ChibiOS memory structure */
213 param->signature = 0;
218 static int chibios_update_stacking(struct rtos *rtos)
220 /* Sometimes the stacking can not be determined only by looking at the
221 * target name but only a runtime.
223 * For example, this is the case for Cortex-M4 targets and ChibiOS which
224 * only stack the FPU registers if it is enabled during ChibiOS build.
226 * Terminating which stacking is used is target depending.
229 * - Once ChibiOS is actually initialized, the stacking is fixed.
230 * - During startup code, the FPU might not be initialized and the
231 * detection might fail.
232 * - Since no threads are running during startup, the problem is solved
233 * by delaying stacking detection until there are more threads
234 * available than the current execution. In which case
235 * chibios_get_thread_reg_list is called.
239 if (!rtos->rtos_specific_params)
242 struct chibios_params *param;
243 param = (struct chibios_params *) rtos->rtos_specific_params;
245 /* Check for armv7m with *enabled* FPU, i.e. a Cortex-M4 */
246 struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target);
247 if (is_armv7m(armv7m_target)) {
248 if (armv7m_target->fp_feature != FP_NONE) {
249 /* Found ARM v7m target which includes a FPU */
252 retval = target_read_u32(rtos->target, FPU_CPACR, &cpacr);
253 if (retval != ERROR_OK) {
254 LOG_ERROR("Could not read CPACR register to check FPU state");
258 /* Check if CP10 and CP11 are set to full access.
259 * In ChibiOS this is done in ResetHandler() in crt0.c */
260 if (cpacr & 0x00F00000) {
261 LOG_DEBUG("Enabled FPU detected.");
262 param->stacking_info = &rtos_chibios_arm_v7m_stacking_w_fpu;
267 /* Found ARM v7m target with no or disabled FPU */
268 param->stacking_info = &rtos_chibios_arm_v7m_stacking;
275 static int chibios_update_threads(struct rtos *rtos)
278 const struct chibios_params *param;
282 if (!rtos->rtos_specific_params)
285 if (!rtos->symbols) {
286 LOG_ERROR("No symbols for ChibiOS");
290 param = (const struct chibios_params *) rtos->rtos_specific_params;
291 /* Update the memory signature saved in the target memory */
292 if (!param->signature) {
293 retval = chibios_update_memory_signature(rtos);
294 if (retval != ERROR_OK) {
295 LOG_ERROR("Reading the memory signature of ChibiOS/RT failed");
300 /* wipe out previous thread details if any */
301 rtos_free_threadlist(rtos);
303 /* ChibiOS does not save the current thread count. We have to first
304 * parse the double linked thread list to check for errors and the number of
306 const uint32_t rlist = rtos->symbols[CHIBIOS_VAL_RLIST].address ?
307 rtos->symbols[CHIBIOS_VAL_RLIST].address :
308 rtos->symbols[CHIBIOS_VAL_CH].address + CH_RLIST_OFFSET /* ChibiOS3 */;
309 const struct chibios_chdebug *signature = param->signature;
317 retval = target_read_u32(rtos->target,
318 current + signature->cf_off_newer, ¤t);
319 if (retval != ERROR_OK) {
320 LOG_ERROR("Could not read next ChibiOS thread");
323 /* Could be NULL if the kernel is not initialized yet or if the
324 * registry is corrupted. */
326 LOG_ERROR("ChibiOS registry integrity check failed, NULL pointer");
331 /* Fetch previous thread in the list as a integrity check. */
332 retval = target_read_u32(rtos->target,
333 current + signature->cf_off_older, &older);
334 if ((retval != ERROR_OK) || (older == 0) || (older != previous)) {
335 LOG_ERROR("ChibiOS registry integrity check failed, "
336 "double linked list violation");
340 /* Check for full iteration of the linked list. */
341 if (current == rlist)
347 /* No RTOS, there is always at least the current execution, though */
348 LOG_INFO("Only showing current execution because of a broken "
349 "ChibiOS thread registry.");
351 const char tmp_thread_name[] = "Current Execution";
352 const char tmp_thread_extra_info[] = "No RTOS thread";
354 rtos->thread_details = malloc(
355 sizeof(struct thread_detail));
356 rtos->thread_details->threadid = 1;
357 rtos->thread_details->exists = true;
359 rtos->thread_details->extra_info_str = malloc(
360 sizeof(tmp_thread_extra_info));
361 strcpy(rtos->thread_details->extra_info_str, tmp_thread_extra_info);
363 rtos->thread_details->thread_name_str = malloc(
364 sizeof(tmp_thread_name));
365 strcpy(rtos->thread_details->thread_name_str, tmp_thread_name);
367 rtos->current_thread = 1;
368 rtos->thread_count = 1;
372 /* create space for new thread details */
373 rtos->thread_details = malloc(
374 sizeof(struct thread_detail) * tasks_found);
375 if (!rtos->thread_details) {
376 LOG_ERROR("Could not allocate space for thread details");
380 rtos->thread_count = tasks_found;
381 /* Loop through linked list. */
382 struct thread_detail *curr_thrd_details = rtos->thread_details;
383 while (curr_thrd_details < rtos->thread_details + tasks_found) {
384 uint32_t name_ptr = 0;
385 char tmp_str[CHIBIOS_THREAD_NAME_STR_SIZE];
387 retval = target_read_u32(rtos->target,
388 current + signature->cf_off_newer, ¤t);
389 if (retval != ERROR_OK) {
390 LOG_ERROR("Could not read next ChibiOS thread");
394 /* Check for full iteration of the linked list. */
395 if (current == rlist)
398 /* Save the thread pointer */
399 curr_thrd_details->threadid = current;
401 /* read the name pointer */
402 retval = target_read_u32(rtos->target,
403 current + signature->cf_off_name, &name_ptr);
404 if (retval != ERROR_OK) {
405 LOG_ERROR("Could not read ChibiOS thread name pointer from target");
409 /* Read the thread name */
410 retval = target_read_buffer(rtos->target, name_ptr,
411 CHIBIOS_THREAD_NAME_STR_SIZE,
412 (uint8_t *)&tmp_str);
413 if (retval != ERROR_OK) {
414 LOG_ERROR("Error reading thread name from ChibiOS target");
417 tmp_str[CHIBIOS_THREAD_NAME_STR_SIZE - 1] = '\x00';
419 if (tmp_str[0] == '\x00')
420 strcpy(tmp_str, "No Name");
422 curr_thrd_details->thread_name_str = malloc(
423 strlen(tmp_str) + 1);
424 strcpy(curr_thrd_details->thread_name_str, tmp_str);
427 uint8_t thread_state;
428 const char *state_desc;
430 retval = target_read_u8(rtos->target,
431 current + signature->cf_off_state, &thread_state);
432 if (retval != ERROR_OK) {
433 LOG_ERROR("Error reading thread state from ChibiOS target");
438 if (thread_state < CHIBIOS_NUM_STATES)
439 state_desc = chibios_thread_states[thread_state];
441 state_desc = "Unknown";
443 curr_thrd_details->extra_info_str = malloc(strlen(
445 sprintf(curr_thrd_details->extra_info_str, "State: %s", state_desc);
447 curr_thrd_details->exists = true;
452 uint32_t current_thrd;
453 /* NOTE: By design, cf_off_name equals readylist_current_offset */
454 retval = target_read_u32(rtos->target,
455 rlist + signature->cf_off_name,
457 if (retval != ERROR_OK) {
458 LOG_ERROR("Could not read current Thread from ChibiOS target");
461 rtos->current_thread = current_thrd;
466 static int chibios_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
467 struct rtos_reg **reg_list, int *num_regs)
470 const struct chibios_params *param;
471 uint32_t stack_ptr = 0;
473 if ((rtos == NULL) || (thread_id == 0) ||
474 (rtos->rtos_specific_params == NULL))
477 param = (const struct chibios_params *) rtos->rtos_specific_params;
479 if (!param->signature)
482 /* Update stacking if it can only be determined from runtime information */
483 if ((param->stacking_info == 0) &&
484 (chibios_update_stacking(rtos) != ERROR_OK)) {
485 LOG_ERROR("Failed to determine exact stacking for the target type %s", rtos->target->type->name);
489 /* Read the stack pointer */
490 retval = target_read_u32(rtos->target,
491 thread_id + param->signature->cf_off_ctx, &stack_ptr);
492 if (retval != ERROR_OK) {
493 LOG_ERROR("Error reading stack frame from ChibiOS thread");
497 return rtos_generic_stack_read(rtos->target, param->stacking_info, stack_ptr, reg_list, num_regs);
500 static int chibios_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[])
502 *symbol_list = malloc(sizeof(chibios_symbol_list));
504 if (*symbol_list == NULL)
507 memcpy(*symbol_list, chibios_symbol_list, sizeof(chibios_symbol_list));
511 static bool chibios_detect_rtos(struct target *target)
513 if ((target->rtos->symbols != NULL) &&
514 ((target->rtos->symbols[CHIBIOS_VAL_RLIST].address != 0) ||
515 (target->rtos->symbols[CHIBIOS_VAL_CH].address != 0))) {
517 if (target->rtos->symbols[CHIBIOS_VAL_CH_DEBUG].address == 0) {
518 LOG_INFO("It looks like the target may be running ChibiOS "
519 "without ch_debug.");
523 /* looks like ChibiOS with memory map enabled.*/
530 static int chibios_create(struct target *target)
533 while ((i < CHIBIOS_NUM_PARAMS) &&
534 (0 != strcmp(chibios_params_list[i].target_name, target->type->name))) {
537 if (i >= CHIBIOS_NUM_PARAMS) {
538 LOG_WARNING("Could not find target \"%s\" in ChibiOS compatibility "
539 "list", target->type->name);
543 target->rtos->rtos_specific_params = (void *) &chibios_params_list[i];