/*************************************************************************** * Copyright (C) 2012 by Matthias Blaicher * * Matthias Blaicher - matthias@blaicher.com * * * * Copyright (C) 2011 by Broadcom Corporation * * Evan Hunter - ehunter@broadcom.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "target/target.h" #include "target/target_type.h" #include "target/armv7m.h" #include "target/cortex_m.h" #include "rtos.h" #include "helper/log.h" #include "helper/types.h" #include "rtos_chibios_stackings.h" /** * @brief ChibiOS/RT memory signature record. * * @details Definition copied from os/kernel/include/chregistry.h of ChibiOS/RT. */ struct ChibiOS_chdebug { char ch_identifier[4]; /**< @brief Always set to "main". */ uint8_t ch_zero; /**< @brief Must be zero. */ uint8_t ch_size; /**< @brief Size of this structure. */ uint16_t ch_version; /**< @brief Encoded ChibiOS/RT version. */ uint8_t ch_ptrsize; /**< @brief Size of a pointer. */ uint8_t ch_timesize; /**< @brief Size of a @p systime_t. */ uint8_t ch_threadsize; /**< @brief Size of a @p Thread struct. */ uint8_t cf_off_prio; /**< @brief Offset of @p p_prio field. */ uint8_t cf_off_ctx; /**< @brief Offset of @p p_ctx field. */ uint8_t cf_off_newer; /**< @brief Offset of @p p_newer field. */ uint8_t cf_off_older; /**< @brief Offset of @p p_older field. */ uint8_t cf_off_name; /**< @brief Offset of @p p_name field. */ uint8_t cf_off_stklimit; /**< @brief Offset of @p p_stklimit field. */ uint8_t cf_off_state; /**< @brief Offset of @p p_state field. */ uint8_t cf_off_flags; /**< @brief Offset of @p p_flags field. */ uint8_t cf_off_refs; /**< @brief Offset of @p p_refs field. */ uint8_t cf_off_preempt; /**< @brief Offset of @p p_preempt field. */ uint8_t cf_off_time; /**< @brief Offset of @p p_time field. */ }; #define GET_CH_KERNEL_MAJOR(codedVersion) ((codedVersion >> 11) & 0x1f) #define GET_CH_KERNEL_MINOR(codedVersion) ((codedVersion >> 6) & 0x1f) #define GET_CH_KERNEL_PATCH(codedVersion) ((codedVersion >> 0) & 0x3f) /** * @brief ChibiOS thread states. */ const char *ChibiOS_thread_states[] = { "READY", "CURRENT", "SUSPENDED", "WTSEM", "WTMTX", "WTCOND", "SLEEPING", "WTEXIT", "WTOREVT", "WTANDEVT", "SNDMSGQ", "SNDMSG", "WTMSG", "WTQUEUE", "FINAL" }; #define CHIBIOS_NUM_STATES (sizeof(ChibiOS_thread_states)/sizeof(char *)) /* Maximum ChibiOS thread name. There is no real limit set by ChibiOS but 64 * chars ought to be enough. */ #define CHIBIOS_THREAD_NAME_STR_SIZE (64) struct ChibiOS_params { const char *target_name; struct ChibiOS_chdebug *signature; const struct rtos_register_stacking *stacking_info; }; struct ChibiOS_params ChibiOS_params_list[] = { { "cortex_m", /* target_name */ 0, NULL, /* stacking_info */ }, { "hla_target", /* target_name */ 0, NULL, /* stacking_info */ } }; #define CHIBIOS_NUM_PARAMS ((int)(sizeof(ChibiOS_params_list)/sizeof(struct ChibiOS_params))) static int ChibiOS_detect_rtos(struct target *target); static int ChibiOS_create(struct target *target); static int ChibiOS_update_threads(struct rtos *rtos); static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); static int ChibiOS_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]); struct rtos_type ChibiOS_rtos = { .name = "ChibiOS", .detect_rtos = ChibiOS_detect_rtos, .create = ChibiOS_create, .update_threads = ChibiOS_update_threads, .get_thread_reg_list = ChibiOS_get_thread_reg_list, .get_symbol_list_to_lookup = ChibiOS_get_symbol_list_to_lookup, }; enum ChibiOS_symbol_values { ChibiOS_VAL_rlist = 0, ChibiOS_VAL_ch_debug = 1, ChibiOS_VAL_chSysInit = 2 }; static char *ChibiOS_symbol_list[] = { "rlist", /* Thread ready list*/ "ch_debug", /* Memory Signatur containing offsets of fields in rlist*/ "chSysInit", /* Necessary part of API, used for ChibiOS detection*/ NULL }; static int ChibiOS_update_memory_signature(struct rtos *rtos) { int retval; struct ChibiOS_params *param; struct ChibiOS_chdebug *signature; param = (struct ChibiOS_params *) rtos->rtos_specific_params; /* Free existing memory description.*/ if (param->signature) { free(param->signature); param->signature = 0; } signature = malloc(sizeof(*signature)); if (!signature) { LOG_ERROR("Could not allocate space for ChibiOS/RT memory signature"); return -1; } retval = target_read_buffer(rtos->target, rtos->symbols[ChibiOS_VAL_ch_debug].address, sizeof(*signature), (uint8_t *) signature); if (retval != ERROR_OK) { LOG_ERROR("Could not read ChibiOS/RT memory signature from target"); goto errfree; } if (strncmp(signature->ch_identifier, "main", 4) != 0) { LOG_ERROR("Memory signature identifier does not contain magic bytes."); goto errfree; } if (signature->ch_size < sizeof(*signature)) { LOG_ERROR("ChibiOS/RT memory signature claims to be smaller " "than expected"); goto errfree; } if (signature->ch_size > sizeof(*signature)) { LOG_WARNING("ChibiOS/RT memory signature claims to be bigger than" " expected. Assuming compatibility..."); } /* Convert endianness of version field */ const uint8_t *versionTarget = (const uint8_t *) &signature->ch_version; signature->ch_version = rtos->target->endianness == TARGET_LITTLE_ENDIAN ? le_to_h_u32(versionTarget) : be_to_h_u32(versionTarget); const uint16_t ch_version = signature->ch_version; LOG_INFO("Successfully loaded memory map of ChibiOS/RT target " "running version %i.%i.%i", GET_CH_KERNEL_MAJOR(ch_version), GET_CH_KERNEL_MINOR(ch_version), GET_CH_KERNEL_PATCH(ch_version)); /* Currently, we have the inherent assumption that all address pointers * are 32 bit wide. */ if (signature->ch_ptrsize != sizeof(uint32_t)) { LOG_ERROR("ChibiOS/RT target memory signature claims an address" "width unequal to 32 bits!"); free(signature); return -1; } param->signature = signature; return 0; errfree: /* Error reading the ChibiOS memory structure */ free(signature); param->signature = 0; return -1; } static int ChibiOS_update_stacking(struct rtos *rtos) { /* Sometimes the stacking can not be determined only by looking at the * target name but only a runtime. * * For example, this is the case for cortex-m4 targets and ChibiOS which * only stack the FPU registers if it is enabled during ChibiOS build. * * Terminating which stacking is used is target depending. * * Assumptions: * - Once ChibiOS is actually initialized, the stacking is fixed. * - During startup code, the FPU might not be initialized and the * detection might fail. * - Since no threads are running during startup, the problem is solved * by delaying stacking detection until there are more threads * available than the current execution. In which case * ChibiOS_get_thread_reg_list is called. */ int retval; if (!rtos->rtos_specific_params) return -1; struct ChibiOS_params *param; param = (struct ChibiOS_params *) rtos->rtos_specific_params; /* Check for armv7m with *enabled* FPU, i.e. a Cortex M4 */ struct armv7m_common *armv7m_target = target_to_armv7m(rtos->target); if (is_armv7m(armv7m_target)) { if (armv7m_target->fp_feature == FPv4_SP) { /* Found ARM v7m target which includes a FPU */ uint32_t cpacr; retval = target_read_u32(rtos->target, FPU_CPACR, &cpacr); if (retval != ERROR_OK) { LOG_ERROR("Could not read CPACR register to check FPU state"); return -1; } /* Check if CP10 and CP11 are set to full access. * In ChibiOS this is done in ResetHandler() in crt0.c */ if (cpacr & 0x00F00000) { /* Found target with enabled FPU */ /* FIXME: Need to figure out how to specify the FPU registers */ LOG_ERROR("ChibiOS ARM v7m targets with enabled FPU " " are NOT supported"); return -1; } } /* Found ARM v7m target with no or disabled FPU */ param->stacking_info = &rtos_chibios_arm_v7m_stacking; return 0; } return -1; } static int ChibiOS_update_threads(struct rtos *rtos) { int retval; const struct ChibiOS_params *param; int tasks_found = 0; int rtos_valid = -1; if (!rtos->rtos_specific_params) return -1; if (!rtos->symbols) { LOG_ERROR("No symbols for ChibiOS"); return -3; } param = (const struct ChibiOS_params *) rtos->rtos_specific_params; /* Update the memory signature saved in the target memory */ if (!param->signature) { retval = ChibiOS_update_memory_signature(rtos); if (retval != ERROR_OK) { LOG_ERROR("Reading the memory signature of ChibiOS/RT failed"); return retval; } } /* wipe out previous thread details if any */ rtos_free_threadlist(rtos); /* ChibiOS does not save the current thread count. We have to first * parse the double linked thread list to check for errors and the number of * threads. */ const uint32_t rlist = rtos->symbols[ChibiOS_VAL_rlist].address; const struct ChibiOS_chdebug *signature = param->signature; uint32_t current; uint32_t previous; uint32_t older; current = rlist; previous = rlist; while (1) { retval = target_read_u32(rtos->target, current + signature->cf_off_newer, ¤t); if (retval != ERROR_OK) { LOG_ERROR("Could not read next ChibiOS thread"); return retval; } /* Could be NULL if the kernel is not initialized yet or if the * registry is corrupted. */ if (current == 0) { LOG_ERROR("ChibiOS registry integrity check failed, NULL pointer"); rtos_valid = 0; break; } /* Fetch previous thread in the list as a integrity check. */ retval = target_read_u32(rtos->target, current + signature->cf_off_older, &older); if ((retval != ERROR_OK) || (older == 0) || (older != previous)) { LOG_ERROR("ChibiOS registry integrity check failed, " "double linked list violation"); rtos_valid = 0; break; } /* Check for full iteration of the linked list. */ if (current == rlist) break; tasks_found++; previous = current; } if (!rtos_valid) { /* No RTOS, there is always at least the current execution, though */ LOG_INFO("Only showing current execution because of a broken " "ChibiOS thread registry."); const char tmp_thread_name[] = "Current Execution"; const char tmp_thread_extra_info[] = "No RTOS thread"; rtos->thread_details = (struct thread_detail *) malloc( sizeof(struct thread_detail)); rtos->thread_details->threadid = 1; rtos->thread_details->exists = true; rtos->thread_details->display_str = NULL; rtos->thread_details->extra_info_str = (char *) malloc( sizeof(tmp_thread_extra_info)); strcpy(rtos->thread_details->extra_info_str, tmp_thread_extra_info); rtos->thread_details->thread_name_str = (char *) malloc( sizeof(tmp_thread_name)); strcpy(rtos->thread_details->thread_name_str, tmp_thread_name); rtos->current_thread = 1; rtos->thread_count = 1; return ERROR_OK; } /* create space for new thread details */ rtos->thread_details = (struct thread_detail *) malloc( sizeof(struct thread_detail) * tasks_found); if (!rtos->thread_details) { LOG_ERROR("Could not allocate space for thread details"); return -1; } rtos->thread_count = tasks_found; /* Loop through linked list. */ struct thread_detail *curr_thrd_details = rtos->thread_details; while (curr_thrd_details < rtos->thread_details + tasks_found) { uint32_t name_ptr = 0; char tmp_str[CHIBIOS_THREAD_NAME_STR_SIZE]; retval = target_read_u32(rtos->target, current + signature->cf_off_newer, ¤t); if (retval != ERROR_OK) { LOG_ERROR("Could not read next ChibiOS thread"); return -6; } /* Check for full iteration of the linked list. */ if (current == rlist) break; /* Save the thread pointer */ curr_thrd_details->threadid = current; /* read the name pointer */ retval = target_read_u32(rtos->target, current + signature->cf_off_name, &name_ptr); if (retval != ERROR_OK) { LOG_ERROR("Could not read ChibiOS thread name pointer from target"); return retval; } /* Read the thread name */ retval = target_read_buffer(rtos->target, name_ptr, CHIBIOS_THREAD_NAME_STR_SIZE, (uint8_t *)&tmp_str); if (retval != ERROR_OK) { LOG_ERROR("Error reading thread name from ChibiOS target"); return retval; } tmp_str[CHIBIOS_THREAD_NAME_STR_SIZE - 1] = '\x00'; if (tmp_str[0] == '\x00') strcpy(tmp_str, "No Name"); curr_thrd_details->thread_name_str = (char *)malloc( strlen(tmp_str) + 1); strcpy(curr_thrd_details->thread_name_str, tmp_str); /* State info */ uint8_t threadState; const char *state_desc; retval = target_read_u8(rtos->target, current + signature->cf_off_state, &threadState); if (retval != ERROR_OK) { LOG_ERROR("Error reading thread state from ChibiOS target"); return retval; } if (threadState < CHIBIOS_NUM_STATES) state_desc = ChibiOS_thread_states[threadState]; else state_desc = "Unknown state"; curr_thrd_details->extra_info_str = (char *)malloc(strlen( state_desc)+1); strcpy(curr_thrd_details->extra_info_str, state_desc); curr_thrd_details->exists = true; curr_thrd_details->display_str = NULL; curr_thrd_details++; } uint32_t current_thrd; /* NOTE: By design, cf_off_name equals readylist_current_offset */ retval = target_read_u32(rtos->target, rlist + signature->cf_off_name, ¤t_thrd); if (retval != ERROR_OK) { LOG_ERROR("Could not read current Thread from ChibiOS target"); return retval; } rtos->current_thread = current_thrd; return 0; } static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list) { int retval; const struct ChibiOS_params *param; uint32_t stack_ptr = 0; *hex_reg_list = NULL; if ((rtos == NULL) || (thread_id == 0) || (rtos->rtos_specific_params == NULL)) return -1; param = (const struct ChibiOS_params *) rtos->rtos_specific_params; if (!param->signature) return -1; /* Update stacking if it can only be determined from runtime information */ if ((param->stacking_info == 0) && (ChibiOS_update_stacking(rtos) != ERROR_OK)) { LOG_ERROR("Failed to determine exact stacking for the target type %s", rtos->target->type->name); return -1; } /* Read the stack pointer */ retval = target_read_u32(rtos->target, thread_id + param->signature->cf_off_ctx, &stack_ptr); if (retval != ERROR_OK) { LOG_ERROR("Error reading stack frame from ChibiOS thread"); return retval; } return rtos_generic_stack_read(rtos->target, param->stacking_info, stack_ptr, hex_reg_list); } static int ChibiOS_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) { unsigned int i; *symbol_list = (symbol_table_elem_t *) malloc( sizeof(symbol_table_elem_t) * ARRAY_SIZE(ChibiOS_symbol_list)); for (i = 0; i < ARRAY_SIZE(ChibiOS_symbol_list); i++) (*symbol_list)[i].symbol_name = ChibiOS_symbol_list[i]; return 0; } static int ChibiOS_detect_rtos(struct target *target) { if ((target->rtos->symbols != NULL) && (target->rtos->symbols[ChibiOS_VAL_rlist].address != 0) && (target->rtos->symbols[ChibiOS_VAL_chSysInit].address != 0)) { if (target->rtos->symbols[ChibiOS_VAL_ch_debug].address == 0) { LOG_INFO("It looks like the target is running ChibiOS without " "ch_debug."); return 0; } /* looks like ChibiOS with memory map enabled.*/ return 1; } return 0; } static int ChibiOS_create(struct target *target) { int i = 0; while ((i < CHIBIOS_NUM_PARAMS) && (0 != strcmp(ChibiOS_params_list[i].target_name, target->type->name))) { i++; } if (i >= CHIBIOS_NUM_PARAMS) { LOG_WARNING("Could not find target \"%s\" in ChibiOS compatibility " "list", target->type->name); return -1; } target->rtos->rtos_specific_params = &ChibiOS_params_list[i]; return 0; }