From: Broadcom Corporation (Evan Hunter) Date: Thu, 14 Apr 2011 08:25:01 +0000 (+0200) Subject: RTOS Thread awareness support wip X-Git-Tag: v0.5.0-rc1~88 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=b69119668ed8d9633280f8b596fe9af60f51644b;p=openocd RTOS Thread awareness support wip - works on Cortex-M3 with ThreadX and FreeRTOS Compared to original patch a few nits were fixed: - remove stricmp usage - unsigned compare fix - printf formatting fixes - fixed a bug with overrunning a memory buffer allocated with malloc. --- diff --git a/configure.in b/configure.in index a73354f9..8315a18d 100644 --- a/configure.in +++ b/configure.in @@ -1170,6 +1170,7 @@ AC_OUTPUT(dnl src/xsvf/Makefile dnl src/svf/Makefile dnl src/target/Makefile dnl + src/rtos/Makefile dnl src/server/Makefile dnl src/flash/Makefile dnl src/flash/nor/Makefile dnl diff --git a/src/Makefile.am b/src/Makefile.am index 77449862..3d7b05f6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,7 +8,8 @@ SUBDIRS = \ svf \ xsvf \ pld \ - server + server \ + rtos lib_LTLIBRARIES = libopenocd.la bin_PROGRAMS = openocd @@ -100,6 +101,7 @@ libopenocd_la_LIBADD = \ $(top_builddir)/src/flash/libflash.la \ $(top_builddir)/src/target/libtarget.la \ $(top_builddir)/src/server/libserver.la \ + $(top_builddir)/src/rtos/librtos.la \ $(top_builddir)/src/helper/libhelper.la \ $(FTDI2232LIB) $(MINGWLDADD) $(LIBUSB) diff --git a/src/rtos/FreeRTOS.c b/src/rtos/FreeRTOS.c new file mode 100644 index 00000000..00074160 --- /dev/null +++ b/src/rtos/FreeRTOS.c @@ -0,0 +1,481 @@ +/*************************************************************************** + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "target/target.h" +#include "target/target_type.h" +#include "rtos.h" +#include "helper/log.h" +#include "rtos_standard_stackings.h" + +#define FreeRTOS_STRUCT( int_type, ptr_type, list_prev_offset ) + + +struct FreeRTOS_params +{ + const char * target_name; + const unsigned char thread_count_width; + const unsigned char pointer_width; + const unsigned char list_next_offset; + const unsigned char list_width; + const unsigned char list_elem_next_offset; + const unsigned char list_elem_content_offset; + const unsigned char thread_stack_offset; + const unsigned char thread_name_offset; + const struct rtos_register_stacking* stacking_info; +}; + + + + +const struct FreeRTOS_params FreeRTOS_params_list[] = +{ + { "cortex_m3", // target_name + 4, // thread_count_width; + 4, // pointer_width; + 16, // list_next_offset; + 20, // list_width; + 8, // list_elem_next_offset; + 12, // list_elem_content_offset + 0, // thread_stack_offset; + 52, // thread_name_offset; + &rtos_standard_Cortex_M3_stacking, // stacking_info + } + +}; + + +#define FREERTOS_NUM_PARAMS ((int)(sizeof(FreeRTOS_params_list)/sizeof(struct FreeRTOS_params))) + +static int FreeRTOS_detect_rtos( struct target* target ); +static int FreeRTOS_create( struct target* target ); +static int FreeRTOS_update_threads( struct rtos *rtos ); +static int FreeRTOS_get_thread_reg_list(struct rtos *rtos, long long thread_id, char ** hex_reg_list ); +static int FreeRTOS_get_symbol_list_to_lookup(symbol_table_elem_t * symbol_list[]); + + + + +struct rtos_type FreeRTOS_rtos = +{ + .name = "FreeRTOS", + + .detect_rtos = FreeRTOS_detect_rtos, + .create = FreeRTOS_create, + .update_threads = FreeRTOS_update_threads, + .get_thread_reg_list = FreeRTOS_get_thread_reg_list, + .get_symbol_list_to_lookup = FreeRTOS_get_symbol_list_to_lookup, +}; + +enum FreeRTOS_symbol_values +{ + FreeRTOS_VAL_pxCurrentTCB = 0, + FreeRTOS_VAL_pxReadyTasksLists = 1, + FreeRTOS_VAL_xDelayedTaskList1 = 2, + FreeRTOS_VAL_xDelayedTaskList2 = 3, + FreeRTOS_VAL_pxDelayedTaskList = 4, + FreeRTOS_VAL_pxOverflowDelayedTaskList = 5, + FreeRTOS_VAL_xPendingReadyList = 6, + FreeRTOS_VAL_xTasksWaitingTermination = 7, + FreeRTOS_VAL_xSuspendedTaskList = 8, + FreeRTOS_VAL_uxCurrentNumberOfTasks = 9, +}; + +static char* FreeRTOS_symbol_list[] = +{ + "pxCurrentTCB", + "pxReadyTasksLists", + "xDelayedTaskList1", + "xDelayedTaskList2", + "pxDelayedTaskList", + "pxOverflowDelayedTaskList", + "xPendingReadyList", + "xTasksWaitingTermination", + "xSuspendedTaskList", + "uxCurrentNumberOfTasks", + NULL +}; + +#define FREERTOS_NUM_SYMBOLS (sizeof(FreeRTOS_symbol_list)/sizeof(char*)) + +// TODO: +// this is not safe for little endian yet +// may be problems reading if sizes are not 32 bit long integers. +// test mallocs for failure + +static int FreeRTOS_update_threads( struct rtos *rtos ) +{ + int i = 0; + int retval; + int tasks_found = 0; + const struct FreeRTOS_params* param; + + if (rtos->rtos_specific_params == NULL ) + { + return -1; + } + + param = (const struct FreeRTOS_params*) rtos->rtos_specific_params; + + if ( rtos->symbols == NULL ) + { + LOG_OUTPUT("No symbols for FreeRTOS\r\n"); + return -3; + } + + if ( rtos->symbols[FreeRTOS_VAL_uxCurrentNumberOfTasks].address == 0 ) + { + LOG_OUTPUT("Don't have the number of threads in FreeRTOS \r\n"); + return -2; + } + + int thread_list_size = 0; + retval = target_read_buffer( rtos->target, rtos->symbols[FreeRTOS_VAL_uxCurrentNumberOfTasks].address, param->thread_count_width, (uint8_t *)&thread_list_size); + + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Could not read FreeRTOS thread count from target\r\n"); + return retval; + } + + + // wipe out previous thread details if any + if ( rtos->thread_details != NULL ) + { + int j; + for( j = 0; j < rtos->thread_count; j++ ) + { + if ( rtos->thread_details[j].display_str != NULL ) + { + free( rtos->thread_details[j].display_str ); + rtos->thread_details[j].display_str = NULL; + } + if ( rtos->thread_details[j].thread_name_str != NULL ) + { + free( rtos->thread_details[j].thread_name_str ); + rtos->thread_details[j].thread_name_str = NULL; + } + if ( rtos->thread_details[j].extra_info_str != NULL ) + { + free( rtos->thread_details[j].extra_info_str ); + rtos->thread_details[j].extra_info_str = NULL; + } + } + free( rtos->thread_details ); + rtos->thread_details = NULL; + } + + + // read the current thread + retval = target_read_buffer( rtos->target, rtos->symbols[FreeRTOS_VAL_pxCurrentTCB].address, param->pointer_width, (uint8_t *)&rtos->current_thread ); + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Error reading current thread in FreeRTOS thread list\r\n"); + return retval; + } + + if ( ( thread_list_size == 0 ) || ( rtos->current_thread == 0 ) ) + { + // Either : No RTOS threads - there is always at least the current execution though + // OR : No current thread - all threads suspended - show the current execution of idling + char tmp_str[] = "Current Execution"; + thread_list_size++; + tasks_found++; + rtos->thread_details = (struct thread_detail*) malloc( sizeof( struct thread_detail ) * thread_list_size ); + rtos->thread_details->threadid = 1; + rtos->thread_details->exists = true; + rtos->thread_details->display_str = NULL; + rtos->thread_details->extra_info_str = NULL; + rtos->thread_details->thread_name_str = (char*) malloc( sizeof(tmp_str) ); + strcpy( rtos->thread_details->thread_name_str, tmp_str ); + + + if ( thread_list_size == 1 ) + { + rtos->thread_count = 1; + return ERROR_OK; + } + } + else + { + // create space for new thread details + rtos->thread_details = (struct thread_detail*) malloc( sizeof( struct thread_detail ) * thread_list_size ); + } + + + // Unfortunately, we can't know how many lists there are for pxReadyTasksLists, + // So figure it out via other variables + int num_ready_task_lists = (rtos->symbols[FreeRTOS_VAL_xDelayedTaskList1].address - rtos->symbols[FreeRTOS_VAL_pxReadyTasksLists].address) / param->list_width; + + + symbol_address_t* list_of_lists = (symbol_address_t *)malloc( sizeof( symbol_address_t ) * ( num_ready_task_lists + 5 ) ); + + int num_lists; + for( num_lists = 0; num_lists < num_ready_task_lists; num_lists++ ) + { + list_of_lists[num_lists] = rtos->symbols[FreeRTOS_VAL_pxReadyTasksLists].address + num_lists * param->list_width; + } + + list_of_lists[num_lists++] = rtos->symbols[FreeRTOS_VAL_xDelayedTaskList1].address; + list_of_lists[num_lists++] = rtos->symbols[FreeRTOS_VAL_xDelayedTaskList2].address; + list_of_lists[num_lists++] = rtos->symbols[FreeRTOS_VAL_xPendingReadyList].address; + list_of_lists[num_lists++] = rtos->symbols[FreeRTOS_VAL_xTasksWaitingTermination].address; + + + for( i = 0; i < num_lists; i++ ) + { + if ( list_of_lists[i] == 0 ) + { + continue; + } + + // Read the number of threads in this list + long long list_thread_count = 0; + retval = target_read_buffer( rtos->target, list_of_lists[i], param->thread_count_width, (uint8_t *)&list_thread_count); + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Error reading number of threads in FreeRTOS thread list\r\n"); + return retval; + } + + if ( list_thread_count == 0 ) + { + continue; + } + + // Read the location of first list item + unsigned long long prev_list_elem_ptr = -1; + unsigned long long list_elem_ptr = 0; + retval = target_read_buffer( rtos->target, list_of_lists[i] + param->list_next_offset, param->pointer_width, (uint8_t *)&list_elem_ptr); + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Error reading first thread item location in FreeRTOS thread list\r\n"); + return retval; + } + + + while ( (list_thread_count > 0) && ( list_elem_ptr != 0) && ( list_elem_ptr != prev_list_elem_ptr ) && ( tasks_found < thread_list_size ) ) + { + // Get the location of the thread structure. + rtos->thread_details[tasks_found].threadid = 0; + retval = target_read_buffer( rtos->target, list_elem_ptr + param->list_elem_content_offset, param->pointer_width, (uint8_t *)&(rtos->thread_details[tasks_found].threadid)); + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Error reading thread list item object in FreeRTOS thread list\r\n"); + return retval; + } + + + // get thread name + + #define FREERTOS_THREAD_NAME_STR_SIZE (200) + char tmp_str[FREERTOS_THREAD_NAME_STR_SIZE]; + + // Read the thread name + retval = target_read_buffer( rtos->target, rtos->thread_details[tasks_found].threadid + param->thread_name_offset, FREERTOS_THREAD_NAME_STR_SIZE, (uint8_t *)&tmp_str); + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Error reading first thread item location in FreeRTOS thread list\r\n"); + return retval; + } + tmp_str[FREERTOS_THREAD_NAME_STR_SIZE-1] = '\x00'; + + if ( tmp_str[0] == '\x00' ) + { + strcpy(tmp_str,"No Name"); + } + + rtos->thread_details[tasks_found].thread_name_str = (char*)malloc( strlen(tmp_str)+1 ); + strcpy( rtos->thread_details[tasks_found].thread_name_str, tmp_str ); + rtos->thread_details[tasks_found].display_str = NULL; + rtos->thread_details[tasks_found].exists = true; + + if ( rtos->thread_details[tasks_found].threadid == rtos->current_thread ) + { + char running_str[] = "Running"; + rtos->thread_details[tasks_found].extra_info_str = (char*) malloc( sizeof(running_str) ); + strcpy( rtos->thread_details[tasks_found].extra_info_str, running_str ); + } + else + { + rtos->thread_details[tasks_found].extra_info_str = NULL; + } + + + tasks_found++; + list_thread_count--; + + prev_list_elem_ptr = list_elem_ptr; + list_elem_ptr = 0; + retval = target_read_buffer( rtos->target, prev_list_elem_ptr + param->list_elem_next_offset, param->pointer_width, (uint8_t *)&list_elem_ptr); + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Error reading next thread item location in FreeRTOS thread list\r\n"); + return retval; + } + } + + + } + free( list_of_lists ); + rtos->thread_count = tasks_found; + return 0; +} + +static int FreeRTOS_get_thread_reg_list(struct rtos *rtos, long long thread_id, char ** hex_reg_list ) +{ + int retval; + const struct FreeRTOS_params* param; + long long stack_ptr = 0; + + + *hex_reg_list = NULL; + if ( rtos == NULL ) + { + return -1; + } + + if ( thread_id == 0 ) + { + return -2; + } + + if (rtos->rtos_specific_params == NULL ) + { + return -1; + } + + param = (const struct FreeRTOS_params*) rtos->rtos_specific_params; + + // Read the stack pointer + retval = target_read_buffer( rtos->target, thread_id + param->thread_stack_offset, param->pointer_width, (uint8_t*)&stack_ptr); + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Error reading stack frame from FreeRTOS thread\r\n"); + return retval; + } + + return rtos_generic_stack_read( rtos->target, param->stacking_info, stack_ptr, hex_reg_list ); + +} + +static int FreeRTOS_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 ) * FREERTOS_NUM_SYMBOLS ); + + for( i = 0; i < FREERTOS_NUM_SYMBOLS; i++ ) + { + (*symbol_list)[i].symbol_name = FreeRTOS_symbol_list[i]; + } + + return 0; +} + +#if 0 + +static int FreeRTOS_set_current_thread(struct rtos *rtos, threadid_t thread_id) +{ + return 0; +} + + + +static int FreeRTOS_get_thread_ascii_info( struct rtos* rtos, threadid_t thread_id, char ** info ) +{ + int retval; + const struct FreeRTOS_params* param; + + if ( rtos == NULL ) + { + return -1; + } + + if ( thread_id == 0 ) + { + return -2; + } + + if (rtos->rtos_specific_params == NULL ) + { + return -3; + } + + param = (const struct FreeRTOS_params*) rtos->rtos_specific_params; + +#define FREERTOS_THREAD_NAME_STR_SIZE (200) + char tmp_str[FREERTOS_THREAD_NAME_STR_SIZE]; + + // Read the thread name + retval = target_read_buffer( rtos->target, thread_id + param->thread_name_offset, FREERTOS_THREAD_NAME_STR_SIZE, (uint8_t *)&tmp_str); + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Error reading first thread item location in FreeRTOS thread list\r\n"); + return retval; + } + tmp_str[FREERTOS_THREAD_NAME_STR_SIZE-1] = '\x00'; + + if ( tmp_str[0] == '\x00' ) + { + strcpy(tmp_str,"No Name"); + } + + *info = (char*)malloc( strlen(tmp_str)+1 ); + strcpy( *info, tmp_str ); + return 0; +} + +#endif + +static int FreeRTOS_detect_rtos( struct target* target ) +{ + if ( ( target->rtos->symbols != NULL ) && + ( target->rtos->symbols[FreeRTOS_VAL_pxReadyTasksLists].address != 0 ) ) + { + // looks like FreeRTOS + return 1; + } + return 0; + return 0; +} + + +static int FreeRTOS_create( struct target* target ) +{ + int i = 0; + while ( ( i < FREERTOS_NUM_PARAMS ) && ( 0 != strcmp( FreeRTOS_params_list[i].target_name, target->type->name ) ) ) + { + i++; + } + if ( i >= FREERTOS_NUM_PARAMS ) + { + LOG_OUTPUT("Could not find target in FreeRTOS compatability list\r\n"); + return -1; + } + + target->rtos->rtos_specific_params = (void*) &FreeRTOS_params_list[i]; + return 0; +} diff --git a/src/rtos/Makefile.am b/src/rtos/Makefile.am new file mode 100644 index 00000000..12368d2e --- /dev/null +++ b/src/rtos/Makefile.am @@ -0,0 +1,35 @@ +# *************************************************************************** +# * 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., * +# * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +# *************************************************************************** + +include $(top_srcdir)/common.mk + +METASOURCES = AUTO +noinst_LTLIBRARIES = librtos.la +noinst_HEADERS = rtos.h +librtos_la_SOURCES = rtos.c rtos_standard_stackings.c FreeRTOS.c ThreadX.c + + +librtos_la_CFLAGS = +if IS_MINGW +# FD_* macros are sloppy with their signs on MinGW32 platform +librtos_la_CFLAGS += -Wno-sign-compare +endif + +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in diff --git a/src/rtos/ThreadX.c b/src/rtos/ThreadX.c new file mode 100644 index 00000000..da841118 --- /dev/null +++ b/src/rtos/ThreadX.c @@ -0,0 +1,536 @@ +/*************************************************************************** + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "target/target.h" +#include "target/target_type.h" +#include "rtos.h" +#include "helper/log.h" +#include "rtos_standard_stackings.h" + + +static int ThreadX_detect_rtos( struct target* target ); +static int ThreadX_create( struct target* target ); +static int ThreadX_update_threads( struct rtos* rtos); +static int ThreadX_get_thread_reg_list(struct rtos *rtos, long long thread_id, char ** hex_reg_list ); +static int ThreadX_get_symbol_list_to_lookup(symbol_table_elem_t * symbol_list[]); + + + +struct ThreadX_thread_state +{ + int value; + char * desc; +}; + + +struct ThreadX_thread_state ThreadX_thread_states[] = +{ + { 0, "Ready" }, + { 1, "Completed" }, + { 2, "Terminated" }, + { 3, "Suspended" }, + { 4, "Sleeping" }, + { 5, "Waiting - Queue" }, + { 6, "Waiting - Semaphore" }, + { 7, "Waiting - Event flag" }, + { 8, "Waiting - Memory" }, + { 9, "Waiting - Memory" }, + { 10, "Waiting - I/O" }, + { 11, "Waiting - Filesystem" }, + { 12, "Waiting - Network" }, + { 13, "Waiting - Mutex" }, +}; + + +#define THREADX_NUM_STATES (sizeof(ThreadX_thread_states)/sizeof(struct ThreadX_thread_state)) + + +struct ThreadX_params +{ + char * target_name; + unsigned char pointer_width; + unsigned char thread_stack_offset; + unsigned char thread_name_offset; + unsigned char thread_state_offset; + unsigned char thread_next_offset; + const struct rtos_register_stacking* stacking_info; +}; + +const struct ThreadX_params ThreadX_params_list[] = +{ + { "cortex_m3", // target_name + 4, // pointer_width; + 8, // thread_stack_offset; + 40, // thread_name_offset; + 48, // thread_state_offset; + 136, // thread_next_offset + &rtos_standard_Cortex_M3_stacking, // stacking_info + } + +}; + +#define THREADX_NUM_PARAMS ((int)(sizeof(ThreadX_params_list)/sizeof(struct ThreadX_params))) + +enum ThreadX_symbol_values +{ + ThreadX_VAL_tx_thread_current_ptr = 0, + ThreadX_VAL_tx_thread_created_ptr = 1, + ThreadX_VAL_tx_thread_created_count = 2, +}; + +static char* ThreadX_symbol_list[] = +{ + "_tx_thread_current_ptr", + "_tx_thread_created_ptr", + "_tx_thread_created_count", + NULL +}; + + + +#define THREADX_NUM_SYMBOLS (sizeof(ThreadX_symbol_list)/sizeof(char*)) + + +const struct rtos_type ThreadX_rtos = +{ + .name = "ThreadX", + + .detect_rtos = ThreadX_detect_rtos, + .create = ThreadX_create, + .update_threads = ThreadX_update_threads, + .get_thread_reg_list = ThreadX_get_thread_reg_list, + .get_symbol_list_to_lookup = ThreadX_get_symbol_list_to_lookup, + +}; + +static int ThreadX_update_threads( struct rtos* rtos) +{ + int retval; + int tasks_found = 0; + int thread_list_size = 0; + const struct ThreadX_params* param; + + if ( rtos == NULL ) + { + return -1; + } + + if (rtos->rtos_specific_params == NULL ) + { + return -3; + } + + param = (const struct ThreadX_params*) rtos->rtos_specific_params; + + if ( rtos->symbols == NULL ) + { + LOG_OUTPUT("No symbols for ThreadX\r\n"); + return -4; + } + + if ( rtos->symbols[ThreadX_VAL_tx_thread_created_count].address == 0 ) + { + LOG_OUTPUT("Don't have the number of threads in ThreadX \r\n"); + return -2; + } + + + + + + // read the number of threads + retval = target_read_buffer( rtos->target, rtos->symbols[ThreadX_VAL_tx_thread_created_count].address, 4, (uint8_t *)&thread_list_size); + + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Could not read ThreadX thread count from target\r\n"); + return retval; + } + + + // wipe out previous thread details if any + if ( rtos->thread_details != NULL ) + { + int j; + for( j = 0; j < rtos->thread_count; j++ ) + { + if ( rtos->thread_details[j].display_str != NULL ) + { + free( rtos->thread_details[j].display_str ); + rtos->thread_details[j].display_str = NULL; + } + if ( rtos->thread_details[j].thread_name_str != NULL ) + { + free( rtos->thread_details[j].thread_name_str ); + rtos->thread_details[j].thread_name_str = NULL; + } + if ( rtos->thread_details[j].extra_info_str != NULL ) + { + free( rtos->thread_details[j].extra_info_str ); + rtos->thread_details[j].extra_info_str = NULL; + } + } + free( rtos->thread_details ); + rtos->thread_details = NULL; + } + + + // read the current thread id + retval = target_read_buffer( rtos->target, rtos->symbols[ThreadX_VAL_tx_thread_current_ptr].address, 4, (uint8_t *)&rtos->current_thread); + + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Could not read ThreadX current thread from target\r\n"); + return retval; + } + + if ( ( thread_list_size == 0 ) || ( rtos->current_thread == 0 ) ) + { + // Either : No RTOS threads - there is always at least the current execution though + // OR : No current thread - all threads suspended - show the current execution of idling + char tmp_str[] = "Current Execution"; + thread_list_size++; + tasks_found++; + rtos->thread_details = (struct thread_detail*) malloc( sizeof( struct thread_detail ) * thread_list_size ); + rtos->thread_details->threadid = 1; + rtos->thread_details->exists = true; + rtos->thread_details->display_str = NULL; + rtos->thread_details->extra_info_str = NULL; + rtos->thread_details->thread_name_str = (char*) malloc( sizeof(tmp_str) ); + strcpy( rtos->thread_details->thread_name_str, tmp_str ); + + + if ( thread_list_size == 0 ) + { + rtos->thread_count = 1; + return ERROR_OK; + } + } + else + { + // create space for new thread details + rtos->thread_details = (struct thread_detail*) malloc( sizeof( struct thread_detail ) * thread_list_size ); + } + + + + // Read the pointer to the first thread + long long thread_ptr = 0; + retval = target_read_buffer( rtos->target, rtos->symbols[ThreadX_VAL_tx_thread_created_ptr].address, param->pointer_width, (uint8_t *)&thread_ptr); + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Could not read ThreadX thread location from target\r\n"); + return retval; + } + + + // loop over all threads + long long prev_thread_ptr = 0; + while ( ( thread_ptr != prev_thread_ptr ) && ( tasks_found < thread_list_size ) ) + { + + #define THREADX_THREAD_NAME_STR_SIZE (200) + char tmp_str[THREADX_THREAD_NAME_STR_SIZE]; + unsigned int i = 0; + long long name_ptr = 0; + + // Save the thread pointer + rtos->thread_details[tasks_found].threadid = thread_ptr; + + + // read the name pointer + retval = target_read_buffer( rtos->target, thread_ptr + param->thread_name_offset, param->pointer_width, (uint8_t *)&name_ptr); + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Could not read ThreadX thread name pointer from target\r\n"); + return retval; + } + + // Read the thread name + retval = target_read_buffer( rtos->target, name_ptr, THREADX_THREAD_NAME_STR_SIZE, (uint8_t *)&tmp_str); + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Error reading thread name from ThreadX target\r\n"); + return retval; + } + tmp_str[THREADX_THREAD_NAME_STR_SIZE-1] = '\x00'; + + if ( tmp_str[0] == '\x00' ) + { + strcpy(tmp_str,"No Name"); + } + + + rtos->thread_details[tasks_found].thread_name_str = (char*)malloc( strlen(tmp_str)+1 ); + strcpy( rtos->thread_details[tasks_found].thread_name_str, tmp_str ); + + + + // Read the thread status + long long thread_status = 0; + retval = target_read_buffer( rtos->target, thread_ptr + param->thread_state_offset, 4, (uint8_t *)&thread_status); + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Error reading thread state from ThreadX target\r\n"); + return retval; + } + + for( i = 0; (i < THREADX_NUM_STATES) && (ThreadX_thread_states[i].value!=thread_status); i++ ) + { + } + + + char * state_desc; + if (i < THREADX_NUM_STATES) + { + state_desc = ThreadX_thread_states[i].desc; + } + else + { + state_desc = "Unknown state"; + } + + rtos->thread_details[tasks_found].extra_info_str = (char*)malloc( strlen(state_desc)+1 ); + strcpy( rtos->thread_details[tasks_found].extra_info_str, state_desc ); + + rtos->thread_details[tasks_found].exists = true; + + rtos->thread_details[tasks_found].display_str = NULL; + + + + + + tasks_found++; + prev_thread_ptr = thread_ptr; + + // Get the location of the next thread structure. + thread_ptr = 0; + retval = target_read_buffer( rtos->target, prev_thread_ptr + param->thread_next_offset, param->pointer_width, (uint8_t *) &thread_ptr ); + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Error reading next thread pointer in ThreadX thread list\r\n"); + return retval; + } + + } + + rtos->thread_count = tasks_found; + + return 0; +} + +static int ThreadX_get_thread_reg_list(struct rtos *rtos, long long thread_id, char ** hex_reg_list ) +{ + + int retval; + const struct ThreadX_params* param; + + *hex_reg_list = NULL; + + if ( rtos == NULL ) + { + return -1; + } + + if ( thread_id == 0 ) + { + return -2; + } + + if (rtos->rtos_specific_params == NULL ) + { + return -3; + } + + param = (const struct ThreadX_params*) rtos->rtos_specific_params; + + // Read the stack pointer + long long stack_ptr = 0; + retval = target_read_buffer( rtos->target, thread_id + param->thread_stack_offset, param->pointer_width, (uint8_t*)&stack_ptr); + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Error reading stack frame from ThreadX thread\r\n"); + return retval; + } + + return rtos_generic_stack_read( rtos->target, param->stacking_info, stack_ptr, hex_reg_list ); +} + + + +static int ThreadX_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 ) * THREADX_NUM_SYMBOLS ); + + for( i = 0; i < THREADX_NUM_SYMBOLS; i++ ) + { + (*symbol_list)[i].symbol_name = ThreadX_symbol_list[i]; + } + + return 0; +} + +static int ThreadX_detect_rtos( struct target* target ) +{ + if ( ( target->rtos->symbols != NULL ) && + ( target->rtos->symbols[ThreadX_VAL_tx_thread_created_ptr].address != 0 ) ) + { + // looks like ThreadX + return 1; + } + return 0; +} + + + +#if 0 + +static int ThreadX_set_current_thread(struct rtos *rtos, threadid_t thread_id) +{ + return 0; +} + + + +static int ThreadX_get_thread_detail( struct rtos* rtos, threadid_t thread_id, struct thread_detail* detail ) +{ + unsigned int i = 0; + int retval; + +#define THREADX_THREAD_NAME_STR_SIZE (200) + char tmp_str[THREADX_THREAD_NAME_STR_SIZE]; + + const struct ThreadX_params* param; + + if ( rtos == NULL ) + { + return -1; + } + + if ( thread_id == 0 ) + { + return -2; + } + + if (rtos->rtos_specific_params == NULL ) + { + return -3; + } + + param = (const struct ThreadX_params*) rtos->rtos_specific_params; + + if ( rtos->symbols == NULL ) + { + LOG_OUTPUT("No symbols for ThreadX\r\n"); + return -3; + } + + detail->threadid = thread_id; + + long long name_ptr = 0; + // read the name pointer + retval = target_read_buffer( rtos->target, thread_id + param->thread_name_offset, param->pointer_width, (uint8_t *)&name_ptr); + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Could not read ThreadX thread name pointer from target\r\n"); + return retval; + } + + // Read the thread name + retval = target_read_buffer( rtos->target, name_ptr, THREADX_THREAD_NAME_STR_SIZE, (uint8_t *)&tmp_str); + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Error reading thread name from ThreadX target\r\n"); + return retval; + } + tmp_str[THREADX_THREAD_NAME_STR_SIZE-1] = '\x00'; + + if ( tmp_str[0] == '\x00' ) + { + strcpy(tmp_str,"No Name"); + } + + detail->thread_name_str = (char*)malloc( strlen(tmp_str)+1 ); + + + // Read the thread status + long long thread_status = 0; + retval = target_read_buffer( rtos->target, thread_id + param->thread_state_offset, 4, (uint8_t *)&thread_status); + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Error reading thread state from ThreadX target\r\n"); + return retval; + } + + for( i = 0; (i < THREADX_NUM_STATES) && (ThreadX_thread_states[i].value!=thread_status); i++ ) + { + } + + + char * state_desc; + if (i < THREADX_NUM_STATES) + { + state_desc = ThreadX_thread_states[i].desc; + } + else + { + state_desc = "Unknown state"; + } + + detail->extra_info_str = (char*)malloc( strlen(state_desc)+1 ); + + detail->exists = true; + + detail->display_str = NULL; + + + + + return 0; +} + +#endif + +static int ThreadX_create( struct target* target ) +{ + int i = 0; + while ( ( i < THREADX_NUM_PARAMS ) && ( 0 != strcmp( ThreadX_params_list[i].target_name, target->type->name ) ) ) + { + i++; + } + if ( i >= THREADX_NUM_PARAMS ) + { + LOG_OUTPUT("Could not find target in ThreadX compatability list\r\n"); + return -1; + } + + target->rtos->rtos_specific_params = (void*) &ThreadX_params_list[i]; + target->rtos->current_thread = 0; + target->rtos->thread_details = NULL; + return 0; +} diff --git a/src/rtos/rtos.c b/src/rtos/rtos.c new file mode 100644 index 00000000..129770e8 --- /dev/null +++ b/src/rtos/rtos.c @@ -0,0 +1,657 @@ +/*************************************************************************** + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#include "rtos.h" +#include "target/target.h" +#include "helper/log.h" +#include "server/gdb_server.h" + + +static long long current_threadid = -1; + +static void hex_to_str( char* dst, char * hex_src ); +static int str_to_hex( char* hex_dst, char* src ); + + +/* RTOSs */ +extern struct rtos_type FreeRTOS_rtos; +extern struct rtos_type ThreadX_rtos; + +static struct rtos_type *rtos_types[] = +{ + &ThreadX_rtos, + &FreeRTOS_rtos, + NULL +}; + + +int rtos_create(Jim_GetOptInfo *goi, struct target * target) +{ + int x; + char *cp; + + if (! goi->isconfigure) { + if (goi->argc != 0) { + if (goi->argc != 0) { + Jim_WrongNumArgs(goi->interp, + goi->argc, goi->argv, + "NO PARAMS"); + return JIM_ERR; + } + + Jim_SetResultString(goi->interp, + target_type_name(target), -1); + } + } + + if (target->rtos) { + free((void *)(target->rtos)); + } +// e = Jim_GetOpt_String(goi, &cp, NULL); +// target->rtos = strdup(cp); + + Jim_GetOpt_String(goi, &cp, NULL); + /* now does target type exist */ + + if ( 0 == strcmp( cp, "auto") ) + { + // auto detection of RTOS + target->rtos_auto_detect = true; + x = 0; + } + else + { + + for (x = 0 ; rtos_types[x] ; x++) { + if (0 == strcmp(cp, rtos_types[x]->name)) { + /* found */ + break; + } + } + if (rtos_types[x] == NULL) { + Jim_SetResultFormatted(goi->interp, "Unknown rtos type %s, try one of ", cp); + for (x = 0 ; rtos_types[x] ; x++) { + if (rtos_types[x + 1]) { + Jim_AppendStrings(goi->interp, + Jim_GetResult(goi->interp), + rtos_types[x]->name, + ", ", NULL); + } else { + Jim_AppendStrings(goi->interp, + Jim_GetResult(goi->interp), + " or ", + rtos_types[x]->name,NULL); + } + } + return JIM_ERR; + } + } + /* Create it */ + target->rtos = calloc(1,sizeof(struct rtos)); + target->rtos->type = rtos_types[x]; + target->rtos->current_thread = 0; + target->rtos->symbols = NULL; + target->rtos->target = target; + + if ( 0 != strcmp( cp, "auto") ) + { + target->rtos->type->create( target ); + } + + return JIM_OK; +} + + + + +int gdb_thread_packet(struct connection *connection, struct target *target, char *packet, int packet_size) +{ + if (strstr(packet, "qP")) + { + #define TAG_THREADID 1 /* Echo the thread identifier */ + #define TAG_EXISTS 2 /* Is this process defined enough to + fetch registers and its stack */ + #define TAG_DISPLAY 4 /* A short thing maybe to put on a window */ + #define TAG_THREADNAME 8 /* string, maps 1-to-1 with a thread is */ + #define TAG_MOREDISPLAY 16 /* Whatever the kernel wants to say about */ + + // TODO: need to scanf the mode variable (or it with the tags), and the threadid + + unsigned long mode; + threadid_t threadid = 0; + struct thread_detail* detail; + sscanf(packet, "qP%8lx%16" SCNx64, &mode, &threadid); + + + int found = -1; + + if ((target->rtos != NULL) && (target->rtos->thread_details + != NULL)) { + int thread_num; + for (thread_num = 0; thread_num + < target->rtos->thread_count; thread_num++) { + if (target->rtos->thread_details[thread_num].threadid + == threadid) { + if (target->rtos->thread_details[thread_num].exists) { + found = thread_num; + } + } + } + } + if (found == -1) { + gdb_put_packet(connection, "E01", 3); // thread not found + return ERROR_OK; + } + + detail = &target->rtos->thread_details[found]; + + if ( detail->display_str != NULL ) + { + mode &= TAG_DISPLAY; + } + if ( detail->thread_name_str != NULL ) + { + mode &= TAG_THREADNAME; + } + if ( detail->extra_info_str != NULL ) + { + mode &= TAG_MOREDISPLAY; + } + + + mode &= TAG_THREADID | TAG_EXISTS; + + char thread_str[1000]; + + sprintf(thread_str, "%08lx", mode); + sprintf(thread_str, "%016" PRIx64, threadid); + + + if (mode & TAG_THREADID) { + sprintf(thread_str, "%08" PRIx32 "10%016" PRIx64, TAG_THREADID, threadid); + } + if (mode & TAG_EXISTS) { + sprintf(thread_str, "%08" PRIx32 "08%08" PRIx32, TAG_EXISTS, (detail->exists==true)?1:0); + } + if (mode & TAG_DISPLAY) { + sprintf(thread_str, "%08" PRIx32 "%02x%s", TAG_DISPLAY, (unsigned char)strlen(detail->display_str), detail->display_str ); + } + if (mode & TAG_MOREDISPLAY) { + sprintf(thread_str, "%08" PRIx32 "%02x%s", TAG_MOREDISPLAY, (unsigned char)strlen(detail->extra_info_str), detail->extra_info_str ); + } + if (mode & TAG_THREADNAME) { + sprintf(thread_str, "%08" PRIx32 "%02x%s", TAG_THREADNAME, (unsigned char)strlen(detail->thread_name_str), detail->thread_name_str ); + } + + //gdb_put_packet(connection, tmpstr, sizeof(tmpstr)-1); + gdb_put_packet(connection, thread_str, strlen(thread_str)); + + // gdb_put_packet(connection, "", 0); + // gdb_put_packet(connection, "OK", 2); // all threads alive + return ERROR_OK; + } + else if (strstr(packet, "qThreadExtraInfo,")) + { + if ((target->rtos != NULL) && (target->rtos->thread_details != NULL) && (target->rtos->thread_count != 0)) + { + threadid_t threadid = 0; + int found = -1; + sscanf(packet, "qThreadExtraInfo,%" SCNx64, &threadid ); + + if ((target->rtos != NULL) && (target->rtos->thread_details + != NULL)) { + int thread_num; + for (thread_num = 0; thread_num + < target->rtos->thread_count; thread_num++) { + if (target->rtos->thread_details[thread_num].threadid + == threadid) { + if (target->rtos->thread_details[thread_num].exists) { + found = thread_num; + } + } + } + } + if (found == -1) { + gdb_put_packet(connection, "E01", 3); // thread not found + return ERROR_OK; + } + + struct thread_detail* detail = &target->rtos->thread_details[found]; + + int str_size = 0; + if ( detail->display_str != NULL ) + { + str_size += strlen(detail->display_str); + } + if ( detail->thread_name_str != NULL ) + { + str_size += strlen(detail->thread_name_str); + } + if ( detail->extra_info_str != NULL ) + { + str_size += strlen(detail->extra_info_str); + } + + char * tmp_str = (char*) malloc( str_size + 7 ); + char* tmp_str_ptr = tmp_str; + + if ( detail->display_str != NULL ) + { + tmp_str_ptr += sprintf( tmp_str_ptr, "%s", detail->display_str ); + } + if ( detail->thread_name_str != NULL ) + { + if ( tmp_str_ptr != tmp_str ) + { + tmp_str_ptr += sprintf( tmp_str_ptr, " : " ); + } + tmp_str_ptr += sprintf( tmp_str_ptr, "%s", detail->thread_name_str ); + } + if ( detail->extra_info_str != NULL ) + { + if ( tmp_str_ptr != tmp_str ) + { + tmp_str_ptr += sprintf( tmp_str_ptr, " : " ); + } + tmp_str_ptr += sprintf( tmp_str_ptr, " : %s", detail->extra_info_str ); + } + + char * hex_str = (char*) malloc( strlen(tmp_str)*2 +1 ); + str_to_hex( hex_str, tmp_str ); + + gdb_put_packet(connection, hex_str, strlen(hex_str)); + free(hex_str); + free(tmp_str); + return ERROR_OK; + + } + gdb_put_packet(connection, "", 0); + return ERROR_OK; + } + else if (strstr(packet, "qSymbol")) + { + if ( target->rtos != NULL ) + { + int next_symbol_num = -1; + if (target->rtos->symbols == NULL) + { + target->rtos->type->get_symbol_list_to_lookup( &target->rtos->symbols ); + } + if (0 == strcmp( "qSymbol::", packet ) ) + { + // first query - + next_symbol_num = 0; + } + else + { + long long value = 0; + char * hex_name_str = malloc( strlen(packet)); + char * name_str; + int symbol_num; + + char* found = strstr( packet, "qSymbol::" ); + int numconv; + if (0 == found ) + { + numconv =sscanf(packet, "qSymbol:%" SCNx64 ":%s", &value, hex_name_str); + } + else + { + // No value returned by GDB - symbol was not found + numconv =sscanf(packet, "qSymbol::%s", hex_name_str); + } + name_str = (char*) malloc( 1+ strlen(hex_name_str) / 2 ); + + hex_to_str( name_str, hex_name_str ); + + + symbol_num = 0; + while ( ( target->rtos->symbols[ symbol_num ].symbol_name != NULL ) && ( 0 != strcmp( target->rtos->symbols[ symbol_num ].symbol_name, name_str ) ) ) + { + symbol_num++; + } + + + if ( target->rtos->symbols[ symbol_num ].symbol_name == NULL ) + { + LOG_OUTPUT("ERROR: unknown symbol\r\n"); + gdb_put_packet(connection, "OK", 2); + return ERROR_OK; + } + + target->rtos->symbols[ symbol_num ].address = value; + + next_symbol_num = symbol_num+1; + free( hex_name_str ); + free( name_str ); + + } + + int symbols_done = 0; + if ( target->rtos->symbols[ next_symbol_num ].symbol_name == NULL ) + { + if ( ( target->rtos_auto_detect == false ) || + ( 1 == target->rtos->type->detect_rtos( target ) ) ) + { + // Found correct RTOS or not autodetecting + if ( target->rtos_auto_detect == true ) + { + LOG_OUTPUT( "Auto-detected RTOS: %s\r\n",target->rtos->type->name ); + } + symbols_done = 1; + } + else + { + // Auto detecting RTOS and currently not found + if( 1 != rtos_try_next( target ) ) + { + // No more RTOS's to try + symbols_done = 1; + } + else + { + next_symbol_num = 0; + target->rtos->type->get_symbol_list_to_lookup( &target->rtos->symbols ); + } + + } + } + + + if ( symbols_done == 1 ) + { + target->rtos_auto_detect = false; + target->rtos->type->create( target ); + target->rtos->type->update_threads(target->rtos); + // No more symbols needed + gdb_put_packet(connection, "OK", 2); + return ERROR_OK; + + } + else + { + char* symname = target->rtos->symbols[ next_symbol_num ].symbol_name; + char qsymstr[] = "qSymbol:"; + char * opstring = (char*)malloc(sizeof(qsymstr)+strlen(symname)*2+1); + char * posptr = opstring; + posptr += sprintf( posptr, "%s", qsymstr ); + str_to_hex( posptr, symname ); + gdb_put_packet(connection, opstring, strlen(opstring)); + free(opstring); + return ERROR_OK; + } + + } + gdb_put_packet(connection, "OK", 2); + return ERROR_OK; + } + else if (strstr(packet, "qfThreadInfo")) + { + int i; + if ( ( target->rtos != NULL ) && ( target->rtos->thread_count != 0 ) ) + { + + char* out_str = (char*) malloc(17 * target->rtos->thread_count + 5); + char* tmp_str = out_str; + tmp_str += sprintf(tmp_str, "m"); + for (i = 0; i < target->rtos->thread_count; i++) { + if (i != 0) { + tmp_str += sprintf(tmp_str, ","); + } + tmp_str += sprintf(tmp_str, "%016" PRIx64, + target->rtos->thread_details[i].threadid); + } + tmp_str[0] = 0; + gdb_put_packet(connection, out_str, strlen(out_str)); + } + else + { + gdb_put_packet(connection, "", 0); + } + + return ERROR_OK; + } + else if (strstr(packet, "qsThreadInfo")) + { + gdb_put_packet(connection, "l", 1); + return ERROR_OK; + } + else if (strstr(packet, "qAttached")) + { + gdb_put_packet(connection, "1", 1); + return ERROR_OK; + } + else if (strstr(packet, "qOffsets")) + { + char offsets[] = "Text=0;Data=0;Bss=0"; + gdb_put_packet(connection, offsets, sizeof(offsets)-1); + return ERROR_OK; + } + else if (strstr(packet, "qC")) + { + gdb_put_packet(connection, "QC0", 3); + return ERROR_OK; + } + else if ( packet[0] == 'T' ) // Is thread alive? + { + threadid_t threadid; + int found = -1; + sscanf(packet, "T%" SCNx64, &threadid); + if ((target->rtos != NULL) && (target->rtos->thread_details + != NULL)) { + int thread_num; + for (thread_num = 0; thread_num + < target->rtos->thread_count; thread_num++) { + if (target->rtos->thread_details[thread_num].threadid + == threadid) { + if (target->rtos->thread_details[thread_num].exists) { + found = thread_num; + } + } + } + } + if (found != -1) { + gdb_put_packet(connection, "OK", 2); // thread alive + } else { + gdb_put_packet(connection, "E01", 3); // thread not found + } + } + else if ( packet[0] == 'H') // Set current thread ( 'c' for step and continue, 'g' for all other operations ) + { + if (packet[1] == 'g') + { + sscanf(packet, "Hg%16" SCNx64, ¤t_threadid); + } + gdb_put_packet(connection, "OK", 2); + } + + return GDB_THREAD_PACKET_NOT_CONSUMED; +} + +int rtos_get_gdb_reg_list(struct connection *connection, struct target *target, struct reg **reg_list[], int *reg_list_size) +{ + if ( ( target->rtos != NULL ) && + ( current_threadid != 1 ) && + ( current_threadid != 0 ) && + ( current_threadid != target->rtos->current_thread ) ) + { + char * hex_reg_list; + target->rtos->type->get_thread_reg_list( target->rtos, current_threadid, &hex_reg_list ); + + if ( hex_reg_list != NULL ) + { + gdb_put_packet(connection, hex_reg_list, strlen(hex_reg_list)); + free(hex_reg_list); + return ERROR_OK; + } + } + return ERROR_FAIL; +} + + + +int rtos_generic_stack_read( struct target * target, const struct rtos_register_stacking* stacking, long long stack_ptr, char ** hex_reg_list ) +{ + int list_size = 0; + char * tmp_str_ptr; + long long new_stack_ptr; + int i; + int retval; + + if ( stack_ptr == 0) + { + LOG_OUTPUT("Error: null stack pointer in thread\r\n"); + return -5; + } + // Read the stack + uint8_t * stack_data = (uint8_t*) malloc( stacking->stack_registers_size ); + uint32_t address = stack_ptr; + + if ( stacking->stack_growth_direction == 1 ) + { + address -= stacking->stack_registers_size; + } + retval = target_read_buffer( target, stack_ptr, stacking->stack_registers_size, stack_data); + if ( retval != ERROR_OK ) + { + LOG_OUTPUT("Error reading stack frame from FreeRTOS thread\r\n"); + return retval; + } +/* + LOG_OUTPUT("Stack Data :"); + for(i = 0; i < stacking->stack_registers_size; i++ ) + { + LOG_OUTPUT("%02X",stack_data[i]); + } + LOG_OUTPUT("\r\n"); +*/ + for( i = 0; i < stacking->num_output_registers; i++ ) + { + list_size += stacking->register_offsets[i].width_bits/8; + } + *hex_reg_list = (char*)malloc( list_size*2 +1 ); + tmp_str_ptr = *hex_reg_list; + new_stack_ptr = stack_ptr + stacking->stack_growth_direction * stacking->stack_registers_size; + for( i = 0; i < stacking->num_output_registers; i++ ) + { + int j; + for ( j = 0; j < stacking->register_offsets[i].width_bits/8; j++ ) + { + if ( stacking->register_offsets[i].offset == -1 ) + { + tmp_str_ptr += sprintf( tmp_str_ptr, "%02x", 0 ); + } + else if ( stacking->register_offsets[i].offset == -2 ) + { + tmp_str_ptr += sprintf( tmp_str_ptr, "%02x", ((uint8_t*)&new_stack_ptr)[j] ); + } + else + { + tmp_str_ptr += sprintf( tmp_str_ptr,"%02x", stack_data[ stacking->register_offsets[i].offset + j ] ); + } + } + } +// LOG_OUTPUT("Output register string: %s\r\n", *hex_reg_list); + return ERROR_OK; +} + +int rtos_try_next( struct target * target ) +{ + int x; + + if ( target->rtos == NULL ) + { + return -1; + } + + for (x = 0 ; rtos_types[x] ; x++) { + if (target->rtos->type == rtos_types[x] ) { + /* found */ + if ( rtos_types[x+1] != NULL ) + { + target->rtos->type = rtos_types[x+1]; + if ( target->rtos->symbols != NULL ) + { + free( target->rtos->symbols ); + } + return 1; + } + else + { + // No more rtos types + return 0; + } + + } + } + return 0; + +} + +static void hex_to_str( char* dst, char * hex_src ) +{ + int src_pos = 0; + int dst_pos = 0; + + while ( hex_src[src_pos] != '\x00' ) + { + char hex_char = hex_src[src_pos]; + char hex_digit_val = (hex_char>='a')?hex_char-'a'+10:(hex_char>='A')?hex_char-'A'+10:hex_char-'0'; + if ( 0 == (src_pos & 0x01) ) + { + dst[dst_pos] = hex_digit_val; + dst[dst_pos+1] = 0; + } + else + { + ((unsigned char*)dst)[dst_pos] <<= 4; + ((unsigned char*)dst)[dst_pos] += hex_digit_val; + dst_pos++; + } + src_pos++; + } + +} + +static int str_to_hex( char* hex_dst, char* src ) +{ + char * posptr = hex_dst; + unsigned i; + for( i = 0; i < strlen(src); i++) + { + posptr += sprintf( posptr, "%02x", (unsigned char)src[i] ); + } + return (posptr-hex_dst); +} + + +int rtos_update_threads( struct target* target ) +{ + if ((target->rtos != NULL) && (target->rtos->type != NULL)) + { + target->rtos->type->update_threads(target->rtos); + } + return ERROR_OK; +} diff --git a/src/rtos/rtos.h b/src/rtos/rtos.h new file mode 100644 index 00000000..cd4bbb27 --- /dev/null +++ b/src/rtos/rtos.h @@ -0,0 +1,106 @@ +/*************************************************************************** + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef RTOS_H +#define RTOS_H + + +#include "server/server.h" +#include +#include + +typedef long long threadid_t; +typedef long long symbol_address_t; + +struct reg; + +/** + * Table should be terminated by an element with NULL in symbol_name + */ +typedef struct symbol_table_elem_struct +{ + char * symbol_name; + symbol_address_t address; + +} symbol_table_elem_t; + +struct thread_detail +{ + threadid_t threadid; + bool exists; + char * display_str; + char * thread_name_str; + char * extra_info_str; +}; + +struct rtos +{ + const struct rtos_type *type; + + + symbol_table_elem_t * symbols; + struct target *target; + + threadid_t current_thread; + struct thread_detail* thread_details; + int thread_count; + + void * rtos_specific_params; + +}; + + + +struct rtos_type +{ + char * name; + int (*detect_rtos) ( struct target* target ); + int (*create) ( struct target* target ); + int (*update_threads) ( struct rtos* rtos ); + int (*get_thread_reg_list) ( struct rtos *rtos, long long thread_id, char ** hex_reg_list ); + int (*get_symbol_list_to_lookup) (symbol_table_elem_t * symbol_list[] ); +}; + + +struct stack_register_offset +{ + signed short offset; // offset in bytes from stack head, or -1 to indicate register is not stacked, or -2 to indicate this is the stack pointer register + unsigned short width_bits; + +}; + +struct rtos_register_stacking +{ + unsigned char stack_registers_size; + signed char stack_growth_direction; + unsigned char num_output_registers; + const struct stack_register_offset* register_offsets; +}; + +#define GDB_THREAD_PACKET_NOT_CONSUMED (-40) + +int rtos_create(Jim_GetOptInfo *goi, struct target * target); +int rtos_generic_stack_read( struct target * target, const struct rtos_register_stacking* stacking, long long stack_ptr, char ** hex_reg_list ); +int rtos_try_next( struct target * target ); +int gdb_thread_packet(struct connection *connection, struct target *target, char *packet, int packet_size); +int rtos_get_gdb_reg_list(struct connection *connection, struct target *target, struct reg **reg_list[], int *reg_list_size); +int rtos_update_threads( struct target *target ); + +#endif // RTOS_H diff --git a/src/rtos/rtos_standard_stackings.c b/src/rtos/rtos_standard_stackings.c new file mode 100644 index 00000000..e0ae0655 --- /dev/null +++ b/src/rtos/rtos_standard_stackings.c @@ -0,0 +1,65 @@ +/*************************************************************************** + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "rtos.h" + +static const struct stack_register_offset rtos_standard_Cortex_M3_stack_offsets [] = +{ { 0x20, 32 }, // r0 + { 0x24, 32 }, // r1 + { 0x28, 32 }, // r2 + { 0x2c, 32 }, // r3 + { 0x00, 32 }, // r4 + { 0x04, 32 }, // r5 + { 0x08, 32 }, // r6 + { 0x0c, 32 }, // r7 + { 0x10, 32 }, // r8 + { 0x14, 32 }, // r9 + { 0x18, 32 }, // r10 + { 0x1c, 32 }, // r11 + { 0x30, 32 }, // r12 + { -2, 32 }, // sp + { 0x34, 32 }, // lr + { 0x38, 32 }, // pc + { -1, 96 }, // FPA1 + { -1, 96 }, // FPA2 + { -1, 96 }, // FPA3 + { -1, 96 }, // FPA4 + { -1, 96 }, // FPA5 + { -1, 96 }, // FPA6 + { -1, 96 }, // FPA7 + { -1, 96 }, // FPA8 + { -1, 32 }, // FPS + { 0x3c, 32 }, // xPSR +}; + + +const struct rtos_register_stacking rtos_standard_Cortex_M3_stacking = +{ + 0x40, // stack_registers_size + 1, // stack_growth_direction + 26, // num_output_registers + rtos_standard_Cortex_M3_stack_offsets // register_offsets +}; + + diff --git a/src/rtos/rtos_standard_stackings.h b/src/rtos/rtos_standard_stackings.h new file mode 100644 index 00000000..1e8568ce --- /dev/null +++ b/src/rtos/rtos_standard_stackings.h @@ -0,0 +1,32 @@ +/*************************************************************************** + * 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., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef INCLUDED_RTOS_STANDARD_STACKINGS_H_ +#define INCLUDED_RTOS_STANDARD_STACKINGS_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "rtos.h" + +extern const struct rtos_register_stacking rtos_standard_Cortex_M3_stacking; + +#endif //ifndef INCLUDED_RTOS_STANDARD_STACKINGS_H_ diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index 77142dfa..0b808589 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -8,6 +8,9 @@ * Copyright (C) 2008 by Spencer Oliver * * spen@spen-soft.co.uk * * * + * 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 * @@ -35,6 +38,7 @@ #include "gdb_server.h" #include #include +#include "rtos/rtos.h" /** @@ -479,7 +483,7 @@ static int gdb_put_packet_inner(struct connection *connection, return ERROR_OK; } -static int gdb_put_packet(struct connection *connection, char *buffer, int len) +int gdb_put_packet(struct connection *connection, char *buffer, int len) { struct gdb_connection *gdb_con = connection->priv; gdb_con->busy = 1; @@ -767,6 +771,7 @@ static void gdb_frontend_halted(struct target *target, struct connection *connec snprintf(sig_reply, 4, "T%2.2x", signal_var); gdb_put_packet(connection, sig_reply, 3); gdb_connection->frontend_state = TARGET_HALTED; + rtos_update_threads( target ); } } @@ -1034,6 +1039,12 @@ static int gdb_get_registers_packet(struct connection *connection, LOG_DEBUG("-"); #endif + if ( ( target->rtos != NULL ) && + ( ERROR_FAIL != rtos_get_gdb_reg_list( connection, target, ®_list, ®_list_size) ) ) + { + return ERROR_OK; + } + if ((retval = target_get_gdb_reg_list(target, ®_list, ®_list_size)) != ERROR_OK) { return gdb_error(connection, retval); @@ -2187,16 +2198,23 @@ static int gdb_input_inner(struct connection *connection) retval = ERROR_OK; switch (packet[0]) { - case 'H': - /* Hct... -- set thread - * we don't have threads, send empty reply */ - gdb_put_packet(connection, NULL, 0); - break; + case 'T': // Is thread alive? + gdb_thread_packet(connection, target, packet, packet_size); + break; + case 'H': // Set current thread ( 'c' for step and continue, 'g' for all other operations ) + gdb_thread_packet(connection, target, packet, packet_size); + break; case 'q': case 'Q': - retval = gdb_query_packet(connection, - target, packet, - packet_size); + retval = gdb_thread_packet(connection, + target, packet, + packet_size); + if ( retval == GDB_THREAD_PACKET_NOT_CONSUMED ) + { + retval = gdb_query_packet(connection, + target, packet, + packet_size); + } break; case 'g': retval = gdb_get_registers_packet( diff --git a/src/server/gdb_server.h b/src/server/gdb_server.h index d7a6ad0a..cb3962ff 100644 --- a/src/server/gdb_server.h +++ b/src/server/gdb_server.h @@ -8,6 +8,9 @@ * Copyright (C) 2008 by Spencer Oliver * * spen@spen-soft.co.uk * * * + * 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 * @@ -39,6 +42,8 @@ struct gdb_service int gdb_target_add_all(struct target *target); int gdb_register_commands(struct command_context *command_context); +int gdb_put_packet(struct connection *connection, char *buffer, int len); + #define ERROR_GDB_BUFFER_TOO_SMALL (-800) #define ERROR_GDB_TIMEOUT (-801) diff --git a/src/target/target.c b/src/target/target.c index 026ca13b..abe1b43a 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -14,6 +14,9 @@ * Copyright (C) 2008 by Rick Altherr * * kc8apf@kc8apf.net> * * * + * 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 * @@ -44,6 +47,7 @@ #include "register.h" #include "trace.h" #include "image.h" +#include "rtos/rtos.h" static int target_read_buffer_default(struct target *target, uint32_t address, @@ -3704,6 +3708,7 @@ enum target_cfg_param { TCFG_COREID, TCFG_CHAIN_POSITION, TCFG_DBGBASE, + TCFG_RTOS, }; static Jim_Nvp nvp_config_opts[] = { @@ -3718,6 +3723,7 @@ static Jim_Nvp nvp_config_opts[] = { { .name = "-coreid", .value = TCFG_COREID }, { .name = "-chain-position", .value = TCFG_CHAIN_POSITION }, { .name = "-dbgbase", .value = TCFG_DBGBASE }, + { .name = "-rtos", .value = TCFG_RTOS }, { .name = NULL, .value = -1 } }; @@ -4024,6 +4030,18 @@ static int target_configure(Jim_GetOptInfo *goi, struct target *target) Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->dbgbase)); /* loop for more */ break; + + case TCFG_RTOS: + /* RTOS */ + { + int result = rtos_create( goi, target ); + if ( result != JIM_OK ) + { + return result; + } + } + /* loop for more */ + break; } } /* while (goi->argc) */ @@ -4746,6 +4764,9 @@ static int target_create(Jim_GetOptInfo *goi) target->endianness = TARGET_ENDIAN_UNKNOWN; + target->rtos = NULL; + target->rtos_auto_detect = false; + /* Do the rest as "configure" options */ goi->isconfigure = 1; e = target_configure(goi, target); diff --git a/src/target/target.h b/src/target/target.h index fd7de560..5b67bf34 100644 --- a/src/target/target.h +++ b/src/target/target.h @@ -8,6 +8,9 @@ * Copyright (C) 2008 by Spencer Oliver * * spen@spen-soft.co.uk * * * + * 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 * @@ -160,6 +163,9 @@ struct target uint32_t dbgbase; /* Really a Cortex-A specific option, but there is no system in place to support target specific options currently. */ + struct rtos *rtos; /* Instance of Real Time Operating System support */ + bool rtos_auto_detect; /* A flag that indicates that the RTOS has been specified as "auto" + * and must be detected when symbols are offered */ }; /** Returns the instance-specific name of the specified target. */