diff options
Diffstat (limited to 'src/rtos/ucos_iii.c')
-rw-r--r-- | src/rtos/ucos_iii.c | 508 |
1 files changed, 508 insertions, 0 deletions
diff --git a/src/rtos/ucos_iii.c b/src/rtos/ucos_iii.c new file mode 100644 index 0000000..f19d06e --- /dev/null +++ b/src/rtos/ucos_iii.c @@ -0,0 +1,508 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/*************************************************************************** + * Copyright (C) 2017 by Square, Inc. * + * Steven Stallion <stallion@squareup.com> * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <helper/log.h> +#include <helper/time_support.h> +#include <helper/types.h> +#include <rtos/rtos.h> +#include <target/target.h> + +#include "rtos_ucos_iii_stackings.h" + +#ifndef UCOS_III_MAX_STRLEN +#define UCOS_III_MAX_STRLEN 64 +#endif + +#ifndef UCOS_III_MAX_THREADS +#define UCOS_III_MAX_THREADS 256 +#endif + +struct ucos_iii_params { + const char *target_name; + const unsigned char pointer_width; + size_t threadid_start; + const struct rtos_register_stacking *stacking_info; +}; + +struct ucos_iii_private { + const struct ucos_iii_params *params; + symbol_address_t thread_stack_offset; + symbol_address_t thread_name_offset; + symbol_address_t thread_state_offset; + symbol_address_t thread_priority_offset; + symbol_address_t thread_prev_offset; + symbol_address_t thread_next_offset; + bool thread_offsets_updated; + size_t num_threads; + symbol_address_t threads[UCOS_III_MAX_THREADS]; +}; + +static const struct ucos_iii_params ucos_iii_params_list[] = { + { + .target_name = "cortex_m", + .pointer_width = sizeof(uint32_t), + .threadid_start = 1, + .stacking_info = &rtos_ucos_iii_cortex_m_stacking, + }, + { + .target_name = "esirisc", + .pointer_width = sizeof(uint32_t), + .threadid_start = 1, + .stacking_info = &rtos_ucos_iii_esi_risc_stacking, + }, +}; + +static const char * const ucos_iii_symbol_list[] = { + "OSRunning", + "OSTCBCurPtr", + "OSTaskDbgListPtr", + "OSTaskQty", + + /* also see: contrib/rtos-helpers/uCOS-III-openocd.c */ + "openocd_OS_TCB_StkPtr_offset", + "openocd_OS_TCB_NamePtr_offset", + "openocd_OS_TCB_TaskState_offset", + "openocd_OS_TCB_Prio_offset", + "openocd_OS_TCB_DbgPrevPtr_offset", + "openocd_OS_TCB_DbgNextPtr_offset", + NULL +}; + +enum ucos_iii_symbol_values { + UCOS_III_VAL_OS_RUNNING, + UCOS_III_VAL_OS_TCB_CUR_PTR, + UCOS_III_VAL_OS_TASK_DBG_LIST_PTR, + UCOS_III_VAL_OS_TASK_QTY, + + /* also see: contrib/rtos-helpers/uCOS-III-openocd.c */ + UCOS_III_VAL_OS_TCB_STK_PTR_OFFSET, + UCOS_III_VAL_OS_TCB_NAME_PTR_OFFSET, + UCOS_III_VAL_OS_TCB_TASK_STATE_OFFSET, + UCOS_III_VAL_OS_TCB_PRIO_OFFSET, + UCOS_III_VAL_OS_TCB_DBG_PREV_PTR_OFFSET, + UCOS_III_VAL_OS_TCB_DBG_NEXT_PTR_OFFSET, +}; + +static const char * const ucos_iii_thread_state_list[] = { + "Ready", + "Delay", + "Pend", + "Pend Timeout", + "Suspended", + "Delay Suspended", + "Pend Suspended", + "Pend Timeout Suspended", +}; + +static int ucos_iii_find_or_create_thread(struct rtos *rtos, symbol_address_t thread_address, + threadid_t *threadid) +{ + struct ucos_iii_private *params = rtos->rtos_specific_params; + size_t thread_index; + + for (thread_index = 0; thread_index < params->num_threads; thread_index++) + if (params->threads[thread_index] == thread_address) + goto found; + + if (params->num_threads == UCOS_III_MAX_THREADS) { + LOG_WARNING("uCOS-III: too many threads; increase UCOS_III_MAX_THREADS"); + return ERROR_FAIL; + } + + params->threads[thread_index] = thread_address; + params->num_threads++; +found: + *threadid = thread_index + params->params->threadid_start; + return ERROR_OK; +} + +static int ucos_iii_find_thread_address(struct rtos *rtos, threadid_t threadid, + symbol_address_t *thread_address) +{ + struct ucos_iii_private *params = rtos->rtos_specific_params; + size_t thread_index; + + thread_index = threadid - params->params->threadid_start; + if (thread_index >= params->num_threads) { + LOG_ERROR("uCOS-III: failed to find thread address"); + return ERROR_FAIL; + } + + *thread_address = params->threads[thread_index]; + return ERROR_OK; +} + +static int ucos_iii_find_last_thread_address(struct rtos *rtos, symbol_address_t *thread_address) +{ + struct ucos_iii_private *params = rtos->rtos_specific_params; + int retval; + + /* read the thread list head */ + symbol_address_t thread_list_address = 0; + + retval = target_read_memory(rtos->target, + rtos->symbols[UCOS_III_VAL_OS_TASK_DBG_LIST_PTR].address, + params->params->pointer_width, + 1, + (void *)&thread_list_address); + if (retval != ERROR_OK) { + LOG_ERROR("uCOS-III: failed to read thread list address"); + return retval; + } + + /* advance to end of thread list */ + do { + *thread_address = thread_list_address; + + retval = target_read_memory(rtos->target, + thread_list_address + params->thread_next_offset, + params->params->pointer_width, + 1, + (void *)&thread_list_address); + if (retval != ERROR_OK) { + LOG_ERROR("uCOS-III: failed to read next thread address"); + return retval; + } + } while (thread_list_address != 0); + + return ERROR_OK; +} + +static int ucos_iii_update_thread_offsets(struct rtos *rtos) +{ + struct ucos_iii_private *params = rtos->rtos_specific_params; + + if (params->thread_offsets_updated) + return ERROR_OK; + + const struct thread_offset_map { + enum ucos_iii_symbol_values symbol_value; + symbol_address_t *thread_offset; + } thread_offset_maps[] = { + { + UCOS_III_VAL_OS_TCB_STK_PTR_OFFSET, + ¶ms->thread_stack_offset, + }, + { + UCOS_III_VAL_OS_TCB_NAME_PTR_OFFSET, + ¶ms->thread_name_offset, + }, + { + UCOS_III_VAL_OS_TCB_TASK_STATE_OFFSET, + ¶ms->thread_state_offset, + }, + { + UCOS_III_VAL_OS_TCB_PRIO_OFFSET, + ¶ms->thread_priority_offset, + }, + { + UCOS_III_VAL_OS_TCB_DBG_PREV_PTR_OFFSET, + ¶ms->thread_prev_offset, + }, + { + UCOS_III_VAL_OS_TCB_DBG_NEXT_PTR_OFFSET, + ¶ms->thread_next_offset, + }, + }; + + for (size_t i = 0; i < ARRAY_SIZE(thread_offset_maps); i++) { + const struct thread_offset_map *thread_offset_map = &thread_offset_maps[i]; + + int retval = target_read_memory(rtos->target, + rtos->symbols[thread_offset_map->symbol_value].address, + params->params->pointer_width, + 1, + (void *)thread_offset_map->thread_offset); + if (retval != ERROR_OK) { + LOG_ERROR("uCOS-III: failed to read thread offset"); + return retval; + } + } + + params->thread_offsets_updated = true; + return ERROR_OK; +} + +static bool ucos_iii_detect_rtos(struct target *target) +{ + return target->rtos->symbols && + target->rtos->symbols[UCOS_III_VAL_OS_RUNNING].address != 0; +} + +static int ucos_iii_reset_handler(struct target *target, enum target_reset_mode reset_mode, void *priv) +{ + struct ucos_iii_private *params = target->rtos->rtos_specific_params; + + params->thread_offsets_updated = false; + params->num_threads = 0; + + return ERROR_OK; +} + +static int ucos_iii_create(struct target *target) +{ + struct ucos_iii_private *params; + + for (size_t i = 0; i < ARRAY_SIZE(ucos_iii_params_list); i++) + if (strcmp(ucos_iii_params_list[i].target_name, target_type_name(target)) == 0) { + params = calloc(1, sizeof(*params)); + if (!params) { + LOG_ERROR("uCOS-III: out of memory"); + return ERROR_FAIL; + } + + params->params = &ucos_iii_params_list[i]; + target->rtos->rtos_specific_params = (void *)params; + + target_register_reset_callback(ucos_iii_reset_handler, NULL); + + return ERROR_OK; + } + + LOG_ERROR("uCOS-III: target not supported: %s", target_type_name(target)); + return ERROR_FAIL; +} + +static int ucos_iii_update_threads(struct rtos *rtos) +{ + struct ucos_iii_private *params = rtos->rtos_specific_params; + int retval; + + if (!rtos->symbols) { + LOG_ERROR("uCOS-III: symbol list not loaded"); + return ERROR_FAIL; + } + + /* free previous thread details */ + rtos_free_threadlist(rtos); + + /* verify RTOS is running */ + uint8_t rtos_running; + + retval = target_read_u8(rtos->target, + rtos->symbols[UCOS_III_VAL_OS_RUNNING].address, + &rtos_running); + if (retval != ERROR_OK) { + LOG_ERROR("uCOS-III: failed to read RTOS running"); + return retval; + } + + if (rtos_running != 1 && rtos_running != 0) { + LOG_ERROR("uCOS-III: invalid RTOS running value"); + return ERROR_FAIL; + } + + if (!rtos_running) { + rtos->thread_details = calloc(1, sizeof(struct thread_detail)); + if (!rtos->thread_details) { + LOG_ERROR("uCOS-III: out of memory"); + return ERROR_FAIL; + } + + rtos->thread_count = 1; + rtos->thread_details->threadid = 0; + rtos->thread_details->exists = true; + rtos->current_thread = 0; + + return ERROR_OK; + } + + /* update thread offsets */ + retval = ucos_iii_update_thread_offsets(rtos); + if (retval != ERROR_OK) { + LOG_ERROR("uCOS-III: failed to update thread offsets"); + return retval; + } + + /* read current thread address */ + symbol_address_t current_thread_address = 0; + + retval = target_read_memory(rtos->target, + rtos->symbols[UCOS_III_VAL_OS_TCB_CUR_PTR].address, + params->params->pointer_width, + 1, + (void *)¤t_thread_address); + if (retval != ERROR_OK) { + LOG_ERROR("uCOS-III: failed to read current thread address"); + return retval; + } + + /* read number of tasks */ + retval = target_read_u16(rtos->target, + rtos->symbols[UCOS_III_VAL_OS_TASK_QTY].address, + (void *)&rtos->thread_count); + if (retval != ERROR_OK) { + LOG_ERROR("uCOS-III: failed to read thread count"); + return retval; + } + + rtos->thread_details = calloc(rtos->thread_count, sizeof(struct thread_detail)); + if (!rtos->thread_details) { + LOG_ERROR("uCOS-III: out of memory"); + return ERROR_FAIL; + } + + /* + * uC/OS-III adds tasks in LIFO order; advance to the end of the + * list and work backwards to preserve the intended order. + */ + symbol_address_t thread_address = 0; + + retval = ucos_iii_find_last_thread_address(rtos, &thread_address); + if (retval != ERROR_OK) { + LOG_ERROR("uCOS-III: failed to find last thread address"); + return retval; + } + + for (int i = 0; i < rtos->thread_count; i++) { + struct thread_detail *thread_detail = &rtos->thread_details[i]; + char thread_str_buffer[UCOS_III_MAX_STRLEN + 1]; + + /* find or create new threadid */ + retval = ucos_iii_find_or_create_thread(rtos, thread_address, &thread_detail->threadid); + if (retval != ERROR_OK) { + LOG_ERROR("uCOS-III: failed to find or create thread"); + return retval; + } + + if (thread_address == current_thread_address) + rtos->current_thread = thread_detail->threadid; + + thread_detail->exists = true; + + /* read thread name */ + symbol_address_t thread_name_address = 0; + + retval = target_read_memory(rtos->target, + thread_address + params->thread_name_offset, + params->params->pointer_width, + 1, + (void *)&thread_name_address); + if (retval != ERROR_OK) { + LOG_ERROR("uCOS-III: failed to name address"); + return retval; + } + + retval = target_read_buffer(rtos->target, + thread_name_address, + sizeof(thread_str_buffer), + (void *)thread_str_buffer); + if (retval != ERROR_OK) { + LOG_ERROR("uCOS-III: failed to read thread name"); + return retval; + } + + thread_str_buffer[sizeof(thread_str_buffer) - 1] = '\0'; + thread_detail->thread_name_str = strdup(thread_str_buffer); + + /* read thread extra info */ + uint8_t thread_state; + uint8_t thread_priority; + + retval = target_read_u8(rtos->target, + thread_address + params->thread_state_offset, + &thread_state); + if (retval != ERROR_OK) { + LOG_ERROR("uCOS-III: failed to read thread state"); + return retval; + } + + retval = target_read_u8(rtos->target, + thread_address + params->thread_priority_offset, + &thread_priority); + if (retval != ERROR_OK) { + LOG_ERROR("uCOS-III: failed to read thread priority"); + return retval; + } + + const char *thread_state_str; + + if (thread_state < ARRAY_SIZE(ucos_iii_thread_state_list)) + thread_state_str = ucos_iii_thread_state_list[thread_state]; + else + thread_state_str = "Unknown"; + + snprintf(thread_str_buffer, sizeof(thread_str_buffer), "State: %s, Priority: %d", + thread_state_str, thread_priority); + thread_detail->extra_info_str = strdup(thread_str_buffer); + + /* read previous thread address */ + retval = target_read_memory(rtos->target, + thread_address + params->thread_prev_offset, + params->params->pointer_width, + 1, + (void *)&thread_address); + if (retval != ERROR_OK) { + LOG_ERROR("uCOS-III: failed to read previous thread address"); + return retval; + } + } + + return ERROR_OK; +} + +static int ucos_iii_get_thread_reg_list(struct rtos *rtos, threadid_t threadid, + struct rtos_reg **reg_list, int *num_regs) +{ + struct ucos_iii_private *params = rtos->rtos_specific_params; + int retval; + + /* find thread address for threadid */ + symbol_address_t thread_address = 0; + + retval = ucos_iii_find_thread_address(rtos, threadid, &thread_address); + if (retval != ERROR_OK) { + LOG_ERROR("uCOS-III: failed to find thread address"); + return retval; + } + + /* read thread stack address */ + symbol_address_t stack_address = 0; + + retval = target_read_memory(rtos->target, + thread_address + params->thread_stack_offset, + params->params->pointer_width, + 1, + (void *)&stack_address); + if (retval != ERROR_OK) { + LOG_ERROR("uCOS-III: failed to read stack address"); + return retval; + } + + return rtos_generic_stack_read(rtos->target, + params->params->stacking_info, + stack_address, + reg_list, + num_regs); +} + +static int ucos_iii_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[]) +{ + *symbol_list = calloc(ARRAY_SIZE(ucos_iii_symbol_list), sizeof(struct symbol_table_elem)); + if (!*symbol_list) { + LOG_ERROR("uCOS-III: out of memory"); + return ERROR_FAIL; + } + + for (size_t i = 0; i < ARRAY_SIZE(ucos_iii_symbol_list); i++) + (*symbol_list)[i].symbol_name = ucos_iii_symbol_list[i]; + + return ERROR_OK; +} + +const struct rtos_type ucos_iii_rtos = { + .name = "uCOS-III", + .detect_rtos = ucos_iii_detect_rtos, + .create = ucos_iii_create, + .update_threads = ucos_iii_update_threads, + .get_thread_reg_list = ucos_iii_get_thread_reg_list, + .get_symbol_list_to_lookup = ucos_iii_get_symbol_list_to_lookup, +}; |