This is an automated email from Gerrit. "Philip Kirkpatrick <p.kirkpatr...@reflexaerospace.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7683
-- gerrit commit 04dfc7f4d60cddbb714110d979aee704e7b55662 Author: Phil Kirkpatrick <p.kirkpatr...@reflexaerospace.com> Date: Tue May 9 11:57:59 2023 +0200 src/rtos/ : Add support for RTEMS Added support for task aware debugging of RTEMS. This currently supports Cortex-R4 and R5 processors. Designed and tested against TMS570LC4357 on a launchxl2-570lc43 board. This currently does NOT support SMP. Change-Id: I83c55bc095a0bf23420656628a2f9df3ec8add56 Signed-off-by: Phil Kirkpatrick <p.kirkpatr...@reflexaerospace.com> diff --git a/src/rtos/Makefile.am b/src/rtos/Makefile.am index 0796910de8..91cddc4577 100644 --- a/src/rtos/Makefile.am +++ b/src/rtos/Makefile.am @@ -11,6 +11,7 @@ noinst_LTLIBRARIES += %D%/librtos.la %D%/rtos_ucos_iii_stackings.c \ %D%/rtos_riot_stackings.c \ %D%/rtos_nuttx_stackings.c \ + %D%/rtos_rtems_stackings.c \ %D%/FreeRTOS.c \ %D%/ThreadX.c \ %D%/eCos.c \ @@ -26,6 +27,7 @@ noinst_LTLIBRARIES += %D%/librtos.la %D%/zephyr.c \ %D%/riot.c \ %D%/rtos.h \ + %D%/rtems.c \ %D%/rtos_standard_stackings.h \ %D%/rtos_ecos_stackings.h \ %D%/linux_header.h \ @@ -34,4 +36,5 @@ noinst_LTLIBRARIES += %D%/librtos.la %D%/rtos_mqx_stackings.h \ %D%/rtos_riot_stackings.h \ %D%/rtos_ucos_iii_stackings.h \ - %D%/rtos_nuttx_stackings.h + %D%/rtos_nuttx_stackings.h \ + %D%/rtos_rtems_stackings.h diff --git a/src/rtos/rtems.c b/src/rtos/rtems.c new file mode 100644 index 0000000000..2e8e2b8142 --- /dev/null +++ b/src/rtos/rtems.c @@ -0,0 +1,724 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/*************************************************************************** + * Copyright (C) 2023 by Reflex Aerospace * + * Philip Kirkpatrick - p.kirkpatr...@reflexaerospace.com * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <helper/time_support.h> +#include <jtag/jtag.h> +#include "target/target.h" +#include "target/target_type.h" +#include "rtos.h" +#include "helper/log.h" +#include "helper/types.h" +#include "helper/list.h" +#include "rtos_standard_stackings.h" +#include "rtos_rtems_stackings.h" +#include "target/armv7m.h" +#include "target/cortex_m.h" + +/* + * This file implements RTOS debugging of RTEMS. It implements api hooks to rtos.c in + * the rtos_type rtems_rtos struct below. These hooks implement extracting various task + * related data from the RTEMS kernel. + * The method of accessing the kernel's data was based on analysis of the following RTEMS source files: + * For getting a list of tasks: + * git://git.rtems.org/rtems.git/cpukit/score/src/threaditerate.c + * For getting data out of a TCB: + * git://git.rtems.org/rtems.git/cpukit/include/rtems/score/thread.h + * git://git.rtems.org/rtems.git/cpukit/score/cpu/arm/include/rtems/score/cpu.h + * For finding a task based on a task ID: + * git://git.rtems.org/rtems.git/cpukit/score/src/threadget.c + */ + +/* Some defines from RTEMS object.h */ +#define RTEMS_OBJECTS_API_START_BIT 24U +#define RTEMS_OBJECTS_API_VALID_BITS 0x00000007U +#define RTEMS_OBJECTS_INDEX_VALID_BITS 0x0000ffffU +#define RTEMS_OBJECTS_APIS_LAST 3U +/* Some defines from RTEMS objectimpl.h */ +#define RTEMS_OBJECTS_INDEX_MINIMUM 1U +/* Some defines from RTEMS sysstate.h */ +#define RTEMS_SYSTEM_STATE_BEFORE_INITIALIZATION 0U +#define RTEMS_SYSTEM_STATE_BEFORE_MULTITASKING 1U +#define RTEMS_SYSTEM_STATE_UP 2U +#define RTEMS_SYSTEM_STATE_TERMINATED 3U + +// This structure contains the offsets needed to extract task data from the various RTEMS structs +// The offsets may vary depending on the specific target port. +struct rtems_params { + const char *target_name; + const unsigned char pointer_width; + const unsigned char sizeof_objects_information; + const unsigned char sizeof_objects_id; + const unsigned char sizeof_objects_control; + const unsigned char sizeof_chain_node; + const unsigned char offsetof_tcb_registers; + const unsigned char offsetof_tcb_is_fp; + const unsigned char offsetof_tcb_current_state; + const unsigned char offsetof_per_cpu_executing; + const struct rtos_register_stacking *stacking_info; + const struct rtos_register_stacking *stacking_info_fpu; +}; + +// The actual implemenations of supported targets' offsets +static const struct rtems_params rtems_params_list[] = { + { + "cortex_r4", /* target_name */ + 4, /* pointer_width; */ + 48, /* RTEMS sizeof(Objects_Information) */ + 4, /* RTEMS sizeof(Objects_Id) */ + 16, /* RTEMS sizeof(Objects_Control) */ + 8, /* RTEMS sizeof(Chain_Node) */ + 136, /* RTEMS offsetof(_Thread_Control, Registers) */ + 250, /* RTEMS offsetof(_Thread_Control, is_fp) */ + 28, /* RTEMS offsetof(_Thread_Control, current_state) */ + 24, /* RTEMS offsetof(Per_CPU_Control, executing) */ + &rtos_rtems_arm_v7r_stacking, + &rtos_rtems_arm_v7r_stacking_fpu + }, +}; + +/* + * The following implement the API to rtos.c + * rtems_detect_rtos : Checks for RTEMS symbols to detect the RTOS + * rtems_create : Checks the rtems_params_list to load the correct offsets for the running target + * rtems_update_threads : Gets a list of all created tasks, including name and state of the tasks + * rtems_get_thread_reg_list : Gets the registers for a specified sleeping task + * rtems_get_symbol_list_to_lookup : Reports what RTOS symbols are needed + */ +static bool rtems_detect_rtos(struct target *target); +static int rtems_create(struct target *target); +static int rtems_update_threads(struct rtos *rtos); +static int rtems_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs); +static int rtems_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[]); + +const struct rtos_type rtems_rtos = { + .name = "RTEMS", + + .detect_rtos = rtems_detect_rtos, + .create = rtems_create, + .update_threads = rtems_update_threads, + .get_thread_reg_list = rtems_get_thread_reg_list, + .get_symbol_list_to_lookup = rtems_get_symbol_list_to_lookup, +}; + +enum rtems_symbol_values { + RTEMS_VAL_CPU_INFO = 0, + RTEMS_VAL_OBJECTS_INFO = 1, + RTEMS_VAL_SYSTEM_STATE = 2, +}; + +static const struct symbol_table_elem rtems_symbol_list[] = { + /* Array of structs that contain info on the state of each CPU (currently only supporting 1 CPU) */ + { "_Per_CPU_Information", 0, false }, + /* This is the root data structure for accessing the list of tasks */ + { "_Objects_Information_table", 0, false }, + /* Contains the current kernel state */ + { "_System_state_Current", 0, false }, + { NULL, 0, false } +}; + +// This data structure with list.h supports storing treads in a link list +struct rtems_task_list { + struct list_head lh; + struct thread_detail thread_details; +}; + +// This is a clean up function used in rtems_update_threads() in certain error returns. +static void rtems_task_list_destroy(struct list_head *task_list) +{ + /* Cleanup any task list that did get allocated */ + struct rtems_task_list *tmp_task1; + struct rtems_task_list *tmp_task2; + list_for_each_entry_safe(tmp_task1, tmp_task2, task_list, lh) { + // Cleanup item + list_del(&tmp_task1->lh); + free(tmp_task1->thread_details.thread_name_str); + free(tmp_task1->thread_details.extra_info_str); + free(tmp_task1); + } +} + +// Conversion of state bits to strings +// No bits set is "Ready" +// This is derived from RTEMS statesimpl.h +static const char * const task_state_bits[] = { + "Wait_Mutex", /*0x00000001*/ + "Wait_Semaphore", /*0x00000002*/ + "Wait_Event", /*0x00000004*/ + "Wait_Sys_Event", /*0x00000008*/ + "Wait_Msg", /*0x00000010*/ + "Wait_Cond_Var", /*0x00000020*/ + "Wait_Futex", /*0x00000040*/ + "Wait_BSD_Wake", /*0x00000080*/ + "Wait_Time", /*0x00000100*/ + "Wait_Period", /*0x00000200*/ + "Wait_Signal", /*0x00000400*/ + "Wait_Barrier", /*0x00000800*/ + "Wait_RWLock", /*0x00001000*/ + "Wait_JoinExit", /*0x00002000*/ + "Wait_Join", /*0x00004000*/ + "Suspend", /*0x00008000*/ + "Wait_Segment", /*0x00010000*/ + "Life_Changing", /*0x00020000*/ + "Undef", /*0x00040000*/ + "Undef", /*0x00080000*/ + "Undef", /*0x00100000*/ + "Undef", /*0x00200000*/ + "Undef", /*0x00400000*/ + "Undef", /*0x00800000*/ + "Undef", /*0x01000000*/ + "Undef", /*0x02000000*/ + "Undef", /*0x04000000*/ + "Debugger", /*0x08000000*/ + "INT_Signal", /*0x10000000*/ + "Wait_RPC_Reply", /*0x20000000*/ + "Zombie", /*0x40000000*/ + "Dormant", /*0x80000000*/ +}; + + +/* TODO: */ +/* SMP is not supported */ +static int rtems_update_threads(struct rtos *rtos) +{ + int retval; + const struct rtems_params *param; + unsigned int tasks_found = 0; + target_addr_t tmp_address; + uint32_t pointer_casts_are_bad; + + if (!rtos->rtos_specific_params) + return -1; + + param = (const struct rtems_params *)rtos->rtos_specific_params; + + if (!rtos->symbols) { + LOG_ERROR("No symbols for RTEMS"); + return -3; + } + + /* wipe out previous thread details if any */ + rtos_free_threadlist(rtos); + + // Check if kernel is started + uint32_t kernel_state; + tmp_address = rtos->symbols[RTEMS_VAL_SYSTEM_STATE].address; + retval = target_read_u32(rtos->target, + tmp_address, + &kernel_state); + LOG_DEBUG("RTEMS: Read _System_state_Current at 0x%" PRIx64 ", value 0x%" PRIx32, + tmp_address, + kernel_state); + if (retval != ERROR_OK) { + LOG_ERROR("Could not read kernel state"); + return retval; + } + + if (kernel_state != RTEMS_SYSTEM_STATE_UP) { + // Handle kernel not running case + char tmp_str[] = "Current Execution"; + tasks_found++; + rtos->thread_details = malloc(sizeof(struct thread_detail)); + if (!rtos->thread_details) { + LOG_ERROR("Error allocating memory for 1 thread"); + return ERROR_FAIL; + } + rtos->current_thread = 1; + rtos->thread_details->threadid = rtos->current_thread; + rtos->thread_details->exists = true; + rtos->thread_details->extra_info_str = NULL; + rtos->thread_details->thread_name_str = malloc(sizeof(tmp_str)); + strcpy(rtos->thread_details->thread_name_str, tmp_str); + rtos->thread_count = 1; + return ERROR_OK; + } + + /* Get the TCB address of the running thread */ + target_addr_t executing_tcb_address; + + tmp_address = rtos->symbols[RTEMS_VAL_CPU_INFO].address + param->offsetof_per_cpu_executing; + retval = target_read_u32(rtos->target, + tmp_address, + &pointer_casts_are_bad); + executing_tcb_address = pointer_casts_are_bad; + LOG_DEBUG("RTEMS: Read executing TBC address at 0x%" PRIx64 ", value 0x%" PRIx32, + tmp_address, + pointer_casts_are_bad); + if (retval != ERROR_OK) { + LOG_ERROR("Could not read executing TBC address"); + return retval; + } + /* Get Thread ID */ + /* 2nd item, just after `Chain_Node Node` */ + tmp_address = executing_tcb_address + param->sizeof_chain_node; + retval = target_read_u32(rtos->target, + tmp_address, + &pointer_casts_are_bad); + rtos->current_thread = pointer_casts_are_bad; + LOG_DEBUG("RTEMS: Read Thread ID at 0x%" PRIx64 ", value 0x%" PRIx32, + tmp_address, + pointer_casts_are_bad); + if (retval != ERROR_OK) { + LOG_ERROR("Could not read executing thread ID"); + return retval; + } + + /* Figuring out how many task requires traversing various structs */ + /* that would have to be re-traversed when getting data on the tasks */ + /* Instead, use a list to get data for all of the tasks, then allocate */ + /* an array and copy the data to the array */ + LIST_HEAD(task_list); + + /* Find and iterate over each task. + Based on analyzing: + git://git.rtems.org/rtems.git/cpukit/score/src/threaditerate.c + Comments below in / *** *** / blocks reference code in threaditerate.c + */ + /* From this point on, task_list may contain malloced items + and must be destroyed with rtems_task_list_destroy() if returning an error + */ + uint32_t api_index; + for (api_index = 1; api_index <= RTEMS_OBJECTS_APIS_LAST; ++api_index) { + uint32_t task_index; + target_addr_t api_info_table_address; + target_addr_t class_info_table_address; + uint32_t maximim_index; + target_addr_t local_table_address; + + /*** Do: `information = _Objects_Information_table[ api_index ][ 1 ];` ***/ + /* Get the pointer to the API's object table */ + tmp_address = rtos->symbols[RTEMS_VAL_OBJECTS_INFO].address; + tmp_address += param->pointer_width * api_index; + + retval = target_read_u32(rtos->target, + tmp_address, + &pointer_casts_are_bad); + api_info_table_address = pointer_casts_are_bad; + LOG_DEBUG("RTEMS: Read api_info_table_address at 0x%" PRIx64 ", value 0x%" PRIx32, + tmp_address, + pointer_casts_are_bad); + + if (retval != ERROR_OK) { + LOG_ERROR("Could not read RTEMS api %d object table address", api_index); + rtems_task_list_destroy(&task_list); + return retval; + } + + /* Get the pointer to the API's first object */ + tmp_address = api_info_table_address + param->pointer_width; + + retval = target_read_u32(rtos->target, + tmp_address, + &pointer_casts_are_bad); + class_info_table_address = pointer_casts_are_bad; + LOG_DEBUG("RTEMS: Read class_info_table_address at 0x%" PRIx64 ", value 0x%" PRIx32, + tmp_address, + pointer_casts_are_bad); + + if (retval != ERROR_OK) { + LOG_ERROR("Could not read RTEMS class info table address"); + rtems_task_list_destroy(&task_list); + return retval; + } + + if (class_info_table_address == 0) + continue; + + /*** Do: `maximum = _Objects_Get_maximum_index( information );` ***/ + /* `maximum_id` is the first element in info table */ + tmp_address = class_info_table_address; + retval = target_read_u32(rtos->target, + tmp_address, + &maximim_index); + LOG_DEBUG("RTEMS: Read object maximim index at 0x%" PRIx64 ", value 0x%" PRIx32, + tmp_address, + maximim_index); + + if (retval != ERROR_OK) { + LOG_ERROR("Could not read object maximim index"); + rtems_task_list_destroy(&task_list); + return retval; + } + + // Get the index bits from `maximim_id` + maximim_index = maximim_index & RTEMS_OBJECTS_INDEX_VALID_BITS; + + /*** Prepare for `information->local_table[ index ]` in loop ***/ + /* Get pointer to `local_table` */ + /* 2nd item in info table */ + tmp_address = class_info_table_address + param->sizeof_objects_id; + retval = target_read_u32(rtos->target, + tmp_address, + &pointer_casts_are_bad); + local_table_address = pointer_casts_are_bad; + LOG_DEBUG("RTEMS: Read local table address at 0x%" PRIx64 ", value 0x%" PRIx32, + tmp_address, + pointer_casts_are_bad); + + if (retval != ERROR_OK) { + LOG_ERROR("Could not read local table address"); + rtems_task_list_destroy(&task_list); + return retval; + } + + for (task_index = 0; task_index < maximim_index; ++task_index) { + target_addr_t task_tcb_address; + + tmp_address = local_table_address + param->pointer_width * task_index; + retval = target_read_u32(rtos->target, + tmp_address, + &pointer_casts_are_bad); + task_tcb_address = pointer_casts_are_bad; + LOG_DEBUG("RTEMS: Read task tcb address at 0x%" PRIx64 ", value 0x%" PRIx32, + tmp_address, + pointer_casts_are_bad); + + if (retval != ERROR_OK) { + LOG_ERROR("Could not read task %d tcb address", task_index); + rtems_task_list_destroy(&task_list); + return retval; + } + + if (task_tcb_address != 0) { + /* Here we finally have a TCB */ + /* This section of code will iterate over all TCBs */ + /* Extract the needed info*/ + + struct rtems_task_list *new_task; + uint32_t name_u32; + + new_task = malloc(sizeof(struct rtems_task_list)); + if (!new_task) { + LOG_ERROR("Error allocating memory for thread"); + rtems_task_list_destroy(&task_list); + return ERROR_FAIL; + } + + new_task->thread_details.exists = true; + + /* Get Thread ID */ + /* 2nd item, just after `Chain_Node Node` */ + tmp_address = task_tcb_address + param->sizeof_chain_node; + retval = target_read_u32(rtos->target, + tmp_address, + &pointer_casts_are_bad); + new_task->thread_details.threadid = pointer_casts_are_bad; + LOG_DEBUG("RTEMS: Read Thread ID at 0x%" PRIx64 ", value 0x%" PRIx32, + tmp_address, + pointer_casts_are_bad); + if (retval != ERROR_OK) { + LOG_ERROR("Could not read thread ID"); + rtems_task_list_destroy(&task_list); + return retval; + } + + /* Get Thread Name */ + /* Want `name_u32`, just after Thread ID */ + tmp_address = task_tcb_address + + param->sizeof_chain_node + + 4 /* u32 id */; + retval = target_read_u32(rtos->target, + tmp_address, + &name_u32); + LOG_DEBUG("RTEMS: Read Thread Name at 0x%" PRIx64 ", value 0x%" PRIx32, + tmp_address, + name_u32); + if (retval != ERROR_OK) { + LOG_ERROR("Could not read thread name"); + rtems_task_list_destroy(&task_list); + return retval; + } + /* Decode u32 to string */ + new_task->thread_details.thread_name_str = malloc(4 + 1); + new_task->thread_details.thread_name_str[0] = (char)((name_u32 >> 24) & 0xFF); + new_task->thread_details.thread_name_str[1] = (char)((name_u32 >> 16) & 0xFF); + new_task->thread_details.thread_name_str[2] = (char)((name_u32 >> 8) & 0xFF); + new_task->thread_details.thread_name_str[3] = (char)((name_u32 >> 0) & 0xFF); + new_task->thread_details.thread_name_str[4] = 0x00; + LOG_DEBUG("RTEMS: Read Thread Name '%s'", + new_task->thread_details.thread_name_str); + + /* Get the task state */ + uint32_t task_state; + tmp_address = task_tcb_address + param->offsetof_tcb_current_state; + retval = target_read_u32(rtos->target, + tmp_address, + &task_state); + LOG_DEBUG("RTEMS: Read Thread state at 0x%" PRIx64 ", value 0x%" PRIx32, + tmp_address, + task_state); + if (retval != ERROR_OK) { + LOG_ERROR("Could not read thread state"); + rtems_task_list_destroy(&task_list); + return retval; + } + + /* Decode the state */ + if (task_state == 0) { + char ready_str[] = "State: Ready"; + new_task->thread_details.extra_info_str = malloc(sizeof(ready_str)); + strcpy(new_task->thread_details.extra_info_str, ready_str); + } else { + bool first = true; + uint8_t bit_index; + uint32_t info_len; + /* State is not 0 (Ready), scan each bit to determine what state conditions are */ + for (bit_index = 0; bit_index < 32; bit_index++) { + if ((1 << bit_index) & task_state) { + if (first) { + /* This is the first matched condition, allocate space and copy the condition */ + first = false; + info_len = strlen(task_state_bits[bit_index]) + 1; + new_task->thread_details.extra_info_str = malloc(info_len); + strcpy(new_task->thread_details.extra_info_str, task_state_bits[bit_index]); + } else { + /* This is not the first matched condition, allocate space for a conatenated string, */ + /* concatenate the condition strings, and free the old condition string */ + char *tmp_str; + + info_len = strlen(new_task->thread_details.extra_info_str) + + 2 + strlen(task_state_bits[bit_index]) + 1; + tmp_str = malloc(info_len); + sprintf(tmp_str, "%s, %s", + new_task->thread_details.extra_info_str, task_state_bits[bit_index]); + + free(new_task->thread_details.extra_info_str); + new_task->thread_details.extra_info_str = tmp_str; + } + } + } + } + + /* Add to list */ + list_add_tail(&new_task->lh, &task_list); + tasks_found++; + } + } + } + + /* create space for new thread details */ + rtos->thread_details = malloc(sizeof(struct thread_detail) * tasks_found); + if (!rtos->thread_details) { + LOG_ERROR("Error allocating memory for %d threads", tasks_found); + rtems_task_list_destroy(&task_list); + return ERROR_FAIL; + } + + /* Copy data to array and cleanup task list */ + uint32_t tmp_index = 0; + struct rtems_task_list *tmp_task1; + struct rtems_task_list *tmp_task2; + list_for_each_entry_safe(tmp_task1, tmp_task2, &task_list, lh) { + // Copy data + memcpy(&rtos->thread_details[tmp_index], &tmp_task1->thread_details, sizeof(struct thread_detail)); + // Cleanup item + list_del(&tmp_task1->lh); + free(tmp_task1); + tmp_index++; + } + rtos->thread_count = tasks_found; + + return 0; +} + +static bool rtems_use_fpu_stack(struct rtos *rtos, const struct rtems_params *param, int64_t tcb_ptr) +{ + int retval; + target_addr_t tmp_address; + uint32_t pointer_casts_are_bad; + bool is_fp; + + /* Get the pointer to the API's first object */ + tmp_address = tcb_ptr + param->offsetof_tcb_is_fp; + + retval = target_read_u32(rtos->target, + tmp_address, + &pointer_casts_are_bad); + is_fp = pointer_casts_are_bad; + LOG_DEBUG("RTEMS: Read is_fp at 0x%" PRIx64 ", value 0x%" PRIx32, + tmp_address, + pointer_casts_are_bad); + + if (retval != ERROR_OK) { + LOG_ERROR("Could not read RTEMS is_fp"); + return false; + } + + return is_fp; +} + +static int rtems_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, + struct rtos_reg **reg_list, int *num_regs) +{ + int retval; + const struct rtems_params *param; + + target_addr_t tmp_address; + uint32_t pointer_casts_are_bad; + + uint32_t task_index; + target_addr_t api_info_table_address; + target_addr_t class_info_table_address; + target_addr_t local_table_address; + target_addr_t task_tcb_address; + + if (!rtos->rtos_specific_params) + return -1; + + param = (const struct rtems_params *)rtos->rtos_specific_params; + + /* Use the thread ID to look up the TCB */ + /* Based on + git://git.rtems.org/rtems.git/cpukit/score/src/threadget.c */ + + /*** Do: _Thread_Get_objects_information_by_id() ***/ + uint32_t api_index; + api_index = ((thread_id >> RTEMS_OBJECTS_API_START_BIT) & RTEMS_OBJECTS_API_VALID_BITS); + + /* Get the pointer to the API's object table */ + tmp_address = rtos->symbols[RTEMS_VAL_OBJECTS_INFO].address; + tmp_address += param->pointer_width * api_index; + + retval = target_read_u32(rtos->target, + tmp_address, + &pointer_casts_are_bad); + api_info_table_address = pointer_casts_are_bad; + LOG_DEBUG("RTEMS: Read api_info_table_address at 0x%" PRIx64 ", value 0x%" PRIx32, + tmp_address, + pointer_casts_are_bad); + + if (retval != ERROR_OK) { + LOG_ERROR("Could not read RTEMS api %d object table address", api_index); + return retval; + } + + /* Get the pointer to the API's first object */ + tmp_address = api_info_table_address + param->pointer_width; + + retval = target_read_u32(rtos->target, + tmp_address, + &pointer_casts_are_bad); + class_info_table_address = pointer_casts_are_bad; + LOG_DEBUG("RTEMS: Read class_info_table_address at 0x%" PRIx64 ", value 0x%" PRIx32, + tmp_address, + pointer_casts_are_bad); + + if (retval != ERROR_OK) { + LOG_ERROR("Could not read RTEMS class info table address"); + return retval; + } + + /*** Do: _Objects_Get() ***/ + uint32_t maximim_id; + uint32_t maximim_index; + uint32_t delta; + /* `maximum_id` is the first element in info table */ + tmp_address = class_info_table_address; + retval = target_read_u32(rtos->target, + tmp_address, + &maximim_id); + LOG_DEBUG("RTEMS: Read object maximim index at 0x%" PRIx64 ", value 0x%" PRIx32, + tmp_address, + maximim_id); + + if (retval != ERROR_OK) { + LOG_ERROR("Could not read object maximim index"); + return retval; + } + + delta = maximim_id - thread_id; + /* Get the index bits from `maximim_id` */ + maximim_index = maximim_id & RTEMS_OBJECTS_INDEX_VALID_BITS; + /* Check numbers are in range */ + if (delta >= maximim_index) { + LOG_DEBUG("RTEMS: Invalid tasx ID 0x%" PRIx64 ", maximim 0x%" PRIx32, + thread_id, + maximim_id); + } + task_index = maximim_index - RTEMS_OBJECTS_INDEX_MINIMUM - delta; + + /* Get pointer to `local_table` */ + /* 2nd item in info table */ + tmp_address = class_info_table_address + param->sizeof_objects_id; + retval = target_read_u32(rtos->target, + tmp_address, + &pointer_casts_are_bad); + local_table_address = pointer_casts_are_bad; + LOG_DEBUG("RTEMS: Read local table address at 0x%" PRIx64 ", value 0x%" PRIx32, + tmp_address, + pointer_casts_are_bad); + + if (retval != ERROR_OK) { + LOG_ERROR("Could not read local table address"); + return retval; + } + + tmp_address = local_table_address + param->pointer_width * task_index; + retval = target_read_u32(rtos->target, + tmp_address, + &pointer_casts_are_bad); + task_tcb_address = pointer_casts_are_bad; + LOG_DEBUG("RTEMS: Read task tcb address at 0x%" PRIx64 ", value 0x%" PRIx32, + tmp_address, + pointer_casts_are_bad); + + if (retval != ERROR_OK) { + LOG_ERROR("Could not read task %d tcb address", task_index); + return retval; + } + + /* Get register list offset */ + /* Note: RTEMS stores the registers in the TCB and not on the stack */ + /* Find the registers in the TCB and read like a stack */ + tmp_address = task_tcb_address + param->offsetof_tcb_registers; + if (param->stacking_info->stack_growth_direction == 1) + tmp_address += param->stacking_info->stack_registers_size; + + if (rtems_use_fpu_stack(rtos, param, task_tcb_address)) + return rtos_generic_stack_read(rtos->target, param->stacking_info_fpu, tmp_address, reg_list, num_regs); + else + return rtos_generic_stack_read(rtos->target, param->stacking_info, tmp_address, reg_list, num_regs); +} + +static int rtems_get_symbol_list_to_lookup(struct symbol_table_elem *symbol_list[]) +{ + *symbol_list = malloc(sizeof(rtems_symbol_list)); + + if (!*symbol_list) + return ERROR_FAIL; + + memcpy(*symbol_list, rtems_symbol_list, sizeof(rtems_symbol_list)); + + return 0; +} + +static bool rtems_detect_rtos(struct target *target) +{ + if (target->rtos->symbols && + target->rtos->symbols[RTEMS_VAL_CPU_INFO].address != 0) { + /* looks like RTEMS */ + return true; + } + return false; +} + +static int rtems_create(struct target *target) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(rtems_params_list); i++) + if (strcmp(rtems_params_list[i].target_name, target->type->name) == 0) { + target->rtos->rtos_specific_params = (void *)&rtems_params_list[i]; + return 0; + } + + LOG_ERROR("Could not find target \"%s\" in RTEMS compatibility " + "list", target->type->name); + return -1; +} diff --git a/src/rtos/rtos.c b/src/rtos/rtos.c index f1e8956a38..0f97781955 100644 --- a/src/rtos/rtos.c +++ b/src/rtos/rtos.c @@ -30,6 +30,7 @@ extern const struct rtos_type hwthread_rtos; extern const struct rtos_type riot_rtos; extern const struct rtos_type zephyr_rtos; extern const struct rtos_type rtkernel_rtos; +extern const struct rtos_type rtems_rtos; static const struct rtos_type *rtos_types[] = { &threadx_rtos, @@ -45,6 +46,7 @@ static const struct rtos_type *rtos_types[] = { &riot_rtos, &zephyr_rtos, &rtkernel_rtos, + &rtems_rtos, /* keep this as last, as it always matches with rtos auto */ &hwthread_rtos, NULL diff --git a/src/rtos/rtos_rtems_stackings.c b/src/rtos/rtos_rtems_stackings.c new file mode 100644 index 0000000000..61c219f8cd --- /dev/null +++ b/src/rtos/rtos_rtems_stackings.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/*************************************************************************** + * Copyright (C) 2023 by Reflex Aerospace * + * Philip Kirkpatrick - p.kirkpatr...@reflexaerospace.com * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "rtos.h" +#include "target/armv7m.h" +#include "rtos_rtems_stackings.h" + +static const struct stack_register_offset rtos_rtems_arm_v7r_stack_offsets[] = { + { 0, -1, 32 }, /* r0 (a1) */ + { 1, -1, 32 }, /* r1 (a2) */ + { 2, -1, 32 }, /* r2 (a3) */ + { 3, -1, 32 }, /* r3 (a4) */ + { 4, 0x00, 32 }, /* r4 (v1) */ + { 5, 0x04, 32 }, /* r5 (v2) */ + { 6, 0x08, 32 }, /* r6 (v3) */ + { 7, 0x0C, 32 }, /* r7 (v4) */ + { 8, 0x10, 32 }, /* r8 (a1) */ + { 10, 0x14, 32 }, /* r9 (sb) */ + { 11, 0x18, 32 }, /* r10 (sl) */ + { 12, 0x1C, 32 }, /* r11 (fp) */ + { 13, -1, 32 }, /* r12 (ip) */ + { 14, 0x20, 32 }, /* sp_usr */ + { 15, -1, 32 }, /* lr_usr */ + { 16, 0x24, 32 }, /* pc */ + { 17, -1, 32 }, /* SPSR */ +}; + +const struct rtos_register_stacking rtos_rtems_arm_v7r_stacking = { + .stack_registers_size = 0x28, + .stack_growth_direction = -1, + .num_output_registers = ARRAY_SIZE(rtos_rtems_arm_v7r_stack_offsets), + .register_offsets = rtos_rtems_arm_v7r_stack_offsets +}; + +static const struct stack_register_offset rtos_rtems_arm_v7r_stack_offsets_fpu[] = { + { 0, -1, 32 }, /* r0 (a1) */ + { 1, -1, 32 }, /* r1 (a2) */ + { 2, -1, 32 }, /* r2 (a3) */ + { 3, -1, 32 }, /* r3 (a4) */ + { 4, 0x00, 32 }, /* r4 (v1) */ + { 5, 0x04, 32 }, /* r5 (v2) */ + { 6, 0x08, 32 }, /* r6 (v3) */ + { 7, 0x0C, 32 }, /* r7 (v4) */ + { 8, 0x10, 32 }, /* r8 (a1) */ + { 10, 0x14, 32 }, /* r9 (sb) */ + { 11, 0x18, 32 }, /* r10 (sl) */ + { 12, 0x1C, 32 }, /* r11 (fp) */ + { 13, -1, 32 }, /* r12 (ip) */ + { 14, 0x20, 32 }, /* sp_usr */ + { 15, -1, 32 }, /* lr_usr */ + { 16, 0x24, 32 }, /* pc */ + { 17, -1, 32 }, /* SPSR */ +}; + +const struct rtos_register_stacking rtos_rtems_arm_v7r_stacking_fpu = { + .stack_registers_size = 0x28, + .stack_growth_direction = -1, + .num_output_registers = ARRAY_SIZE(rtos_rtems_arm_v7r_stack_offsets_fpu), + .register_offsets = rtos_rtems_arm_v7r_stack_offsets_fpu +}; diff --git a/src/rtos/rtos_rtems_stackings.h b/src/rtos/rtos_rtems_stackings.h new file mode 100644 index 0000000000..4494a9a7e0 --- /dev/null +++ b/src/rtos/rtos_rtems_stackings.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/*************************************************************************** + * Copyright (C) 2023 by Reflex Aerospace * + * Philip Kirkpatrick - p.kirkpatr...@reflexaerospace.com * + ***************************************************************************/ + +#ifndef OPENOCD_RTOS_RTOS_RTEMS_STACKINGS_H +#define OPENOCD_RTOS_RTOS_RTEMS_STACKINGS_H + +#include "rtos.h" + +extern const struct rtos_register_stacking rtos_rtems_arm_v7r_stacking; +extern const struct rtos_register_stacking rtos_rtems_arm_v7r_stacking_fpu; + +#endif /* OPENOCD_RTOS_RTOS_RTEMS_STACKINGS_H */ --