diff --git a/doc/openocd.texi b/doc/openocd.texi index e6635576e..2cf1cbc27 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -4075,7 +4075,7 @@ The value should normally correspond to a static mapping for the @item @code{-rtos} @var{rtos_type} -- enable rtos support for target, @var{rtos_type} can be one of @option{auto}|@option{eCos}|@option{ThreadX}| -@option{FreeRTOS}|@option{linux}. +@option{FreeRTOS}|@option{linux}|@option{ChibiOS}. @end itemize @end deffn diff --git a/src/rtos/ChibiOS.c b/src/rtos/ChibiOS.c new file mode 100644 index 000000000..2c2b79533 --- /dev/null +++ b/src/rtos/ChibiOS.c @@ -0,0 +1,528 @@ +/*************************************************************************** + * Copyright (C) 2012 by Matthias Blaicher * + * Matthias Blaicher - matthias@blaicher.com * + * * + * Copyright (C) 2011 by Broadcom Corporation * + * Evan Hunter - ehunter@broadcom.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 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_chibios_stackings.h" + + +/** + * @brief ChibiOS/RT memory signature record. + * + * @details Definition copied from os/kernel/include/chregistry.h of ChibiOS/RT. + */ +struct ChibiOS_chdebug { + char ch_identifier[4]; /**< @brief Always set to "main". */ + uint8_t ch_zero; /**< @brief Must be zero. */ + uint8_t ch_size; /**< @brief Size of this structure. */ + uint16_t ch_version; /**< @brief Encoded ChibiOS/RT version. */ + uint8_t ch_ptrsize; /**< @brief Size of a pointer. */ + uint8_t ch_timesize; /**< @brief Size of a @p systime_t. */ + uint8_t ch_threadsize; /**< @brief Size of a @p Thread struct. */ + uint8_t cf_off_prio; /**< @brief Offset of @p p_prio field. */ + uint8_t cf_off_ctx; /**< @brief Offset of @p p_ctx field. */ + uint8_t cf_off_newer; /**< @brief Offset of @p p_newer field. */ + uint8_t cf_off_older; /**< @brief Offset of @p p_older field. */ + uint8_t cf_off_name; /**< @brief Offset of @p p_name field. */ + uint8_t cf_off_stklimit; /**< @brief Offset of @p p_stklimit + field. */ + uint8_t cf_off_state; /**< @brief Offset of @p p_state field. */ + uint8_t cf_off_flags; /**< @brief Offset of @p p_flags field. */ + uint8_t cf_off_refs; /**< @brief Offset of @p p_refs field. */ + uint8_t cf_off_preempt; /**< @brief Offset of @p p_preempt + field. */ + uint8_t cf_off_time; /**< @brief Offset of @p p_time field. */ +}; + +#define GET_CH_KERNEL_MAJOR(codedVersion) ((codedVersion >> 11) & 0x1f) +#define GET_CH_KERNEL_MINOR(codedVersion) ((codedVersion >> 6) & 0x1f) +#define GET_CH_KERNEL_PATCH(codedVersion) ((codedVersion >> 0) & 0x3f) + +/** + * @brief ChibiOS thread states. + */ +const char *ChibiOS_thread_states[] = { + "READY", "CURRENT", "SUSPENDED", "WTSEM", "WTMTX", "WTCOND", "SLEEPING", + "WTEXIT", "WTOREVT", "WTANDEVT", "SNDMSGQ", "SNDMSG", "WTMSG", "WTQUEUE", + "FINAL" +}; + +#define CHIBIOS_NUM_STATES (sizeof(ChibiOS_thread_states)/sizeof(char *)) + +/* Maximum ChibiOS thread name. There is no real limit set by ChibiOS but 64 + * chars ought to be enough. + */ +#define CHIBIOS_THREAD_NAME_STR_SIZE (64) + +struct ChibiOS_params { + const char *target_name; + + struct ChibiOS_chdebug *signature; + const struct rtos_register_stacking *stacking_info; +}; + +struct ChibiOS_params ChibiOS_params_list[] = { + { + "cortex_m3", /* target_name */ + 0, + &rtos_chibios_arm_v7m_stacking, /* stacking_info */ + }, + { + "stm32_stlink", /* target_name */ + 0, + &rtos_chibios_arm_v7m_stacking, /* stacking_info */ + } +}; +#define CHIBIOS_NUM_PARAMS ((int)(sizeof(ChibiOS_params_list)/sizeof(struct ChibiOS_params))) + +static int ChibiOS_detect_rtos(struct target *target); +static int ChibiOS_create(struct target *target); +static int ChibiOS_update_threads(struct rtos *rtos); +static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list); +static int ChibiOS_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]); + +struct rtos_type ChibiOS_rtos = { + .name = "ChibiOS", + + .detect_rtos = ChibiOS_detect_rtos, + .create = ChibiOS_create, + .update_threads = ChibiOS_update_threads, + .get_thread_reg_list = ChibiOS_get_thread_reg_list, + .get_symbol_list_to_lookup = ChibiOS_get_symbol_list_to_lookup, +}; + +enum ChibiOS_symbol_values { + ChibiOS_VAL_rlist = 0, + ChibiOS_VAL_ch_debug = 1, + ChibiOS_VAL_chSysInit = 2 +}; + +static char *ChibiOS_symbol_list[] = { + "rlist", /* Thread ready list*/ + "ch_debug", /* Memory Signatur containing offsets of fields in rlist*/ + "chSysInit", /* Necessary part of API, used for ChibiOS detection*/ + NULL +}; + +#define CHIBIOS_NUM_SYMBOLS (sizeof(ChibiOS_symbol_list)/sizeof(char *)) + +static int ChibiOS_update_memory_signature(struct rtos *rtos) +{ + int retval; + struct ChibiOS_params *param; + struct ChibiOS_chdebug *signature; + + param = (struct ChibiOS_params *) rtos->rtos_specific_params; + + /* Free existing memory description.*/ + if (param->signature) { + free(param->signature); + param->signature = 0; + } + + signature = malloc(sizeof(*signature)); + if (!signature) { + LOG_ERROR("Could not allocate space for ChibiOS/RT memory signature"); + return -1; + } + + retval = target_read_buffer(rtos->target, + rtos->symbols[ChibiOS_VAL_ch_debug].address, + sizeof(*signature), + (uint8_t *) signature); + if (retval != ERROR_OK) { + LOG_ERROR("Could not read ChibiOS/RT memory signature from target"); + goto errfree; + } + + if (strncmp(signature->ch_identifier, "main", 4) != 0) { + LOG_ERROR("Memory signature identifier does not contain magic bytes."); + goto errfree; + } + + if (signature->ch_size < sizeof(*signature)) { + LOG_ERROR("ChibiOS/RT memory signature claims to be smaller " + "than expected"); + goto errfree; + } + + if (signature->ch_size > sizeof(*signature)) { + LOG_WARNING("ChibiOS/RT memory signature claims to be bigger than" + " expected. Assuming compatibility..."); + } + + /* Convert endianness of version field */ + const uint8_t *versionTarget = (const uint8_t *) + &signature->ch_version; + signature->ch_version = rtos->target->endianness == TARGET_LITTLE_ENDIAN ? + le_to_h_u32(versionTarget) : be_to_h_u32(versionTarget); + + const uint16_t ch_version = signature->ch_version; + LOG_INFO("Successfully loaded memory map of ChibiOS/RT target " + "running version %i.%i.%i", GET_CH_KERNEL_MAJOR(ch_version), + GET_CH_KERNEL_MINOR(ch_version), GET_CH_KERNEL_PATCH(ch_version)); + + param->signature = signature; + return 0; + +errfree: + /* Error reading the ChibiOS memory structure */ + free(signature); + param->signature = 0; + return -1; +} + + +static int ChibiOS_update_stacking(struct rtos *rtos) +{ + /* Sometimes the stacking can not be determined only by looking at the + * target name but only a runtime. + * + * For example, this is the case for cortex-m4 targets and ChibiOS which + * only stack the FPU registers if it is enabled during ChibiOS build. + * + * Terminating which stacking is used is target depending. + * + * Assumptions: + * - Once ChibiOS is actually initialized, the stacking is fixed. + * - During startup code, the FPU might not be initialized and the + * detection might fail. + * - Since no threads are running during startup, the problem is solved + * by delaying stacking detection until there are more threads + * available than the current execution. In which case + * ChibiOS_get_thread_reg_list is called. + */ + + /* TODO: Add actual detection, currently it will not work with FPU enabled.*/ + return -1; +} + +static int ChibiOS_update_threads(struct rtos *rtos) +{ + int retval; + const struct ChibiOS_params *param; + int tasks_found = 0; + int rtos_valid = -1; + + if (!rtos->rtos_specific_params) + return -1; + + if (!rtos->symbols) { + LOG_ERROR("No symbols for ChibiOS"); + return -3; + } + + param = (const struct ChibiOS_params *) rtos->rtos_specific_params; + /* Update the memory signature saved in the target memory */ + if (!param->signature) { + retval = ChibiOS_update_memory_signature(rtos); + if (retval != ERROR_OK) { + LOG_ERROR("Reading the memory signature of ChibiOS/RT failed"); + return retval; + } + } + + /* wipe out previous thread details if any */ + int j; + if (rtos->thread_details) { + for (j = 0; j < rtos->thread_count; j++) { + struct thread_detail *current_thread = &rtos->thread_details[j]; + if (current_thread->display_str != NULL) + free(current_thread->display_str); + if (current_thread->thread_name_str != NULL) + free(current_thread->thread_name_str); + if (current_thread->extra_info_str != NULL) + free(current_thread->extra_info_str); + } + free(rtos->thread_details); + rtos->thread_details = NULL; + rtos->thread_count = 0; + } + /* ChibiOS does not save the current thread count. We have to first + * parse the double linked thread list to check for errors and the number of + * threads. */ + uint32_t rlist; + uint32_t current; + uint32_t previous; + uint32_t older; + + retval = target_read_buffer(rtos->target, + rtos->symbols[ChibiOS_VAL_rlist].address, + param->signature->ch_ptrsize, + (uint8_t *)&rlist); + if (retval != ERROR_OK) { + LOG_ERROR("Could not read ChibiOS ReadyList from target"); + return retval; + } + current = rlist; + previous = rlist; + while (1) { + retval = target_read_buffer(rtos->target, + current + param->signature->cf_off_newer, + param->signature->ch_ptrsize, + (uint8_t *)¤t); + if (retval != ERROR_OK) { + LOG_ERROR("Could not read next ChibiOS thread"); + return retval; + } + /* Could be NULL if the kernel is not initialized yet or if the + * registry is corrupted. */ + if (current == 0) { + LOG_ERROR("ChibiOS registry integrity check failed, NULL pointer"); + + rtos_valid = 0; + break; + } + /* Fetch previous thread in the list as a integrity check. */ + retval = target_read_buffer(rtos->target, + current + param->signature->cf_off_older, + param->signature->ch_ptrsize, + (uint8_t *)&older); + if ((retval != ERROR_OK) || (older == 0) || (older != previous)) { + LOG_ERROR("ChibiOS registry integrity check failed, " + "double linked list violation"); + rtos_valid = 0; + break; + } + /* Check for full iteration of the linked list. */ + if (current == rlist) + break; + tasks_found++; + previous = current; + } + if (!rtos_valid) { + /* No RTOS, there is always at least the current execution, though */ + LOG_INFO("Only showing current execution because of a broken " + "ChibiOS thread registry."); + + const char tmp_thread_name[] = "Current Execution"; + const char tmp_thread_extra_info[] = "No RTOS thread"; + + rtos->thread_details = (struct thread_detail *) malloc( + sizeof(struct thread_detail)); + rtos->thread_details->threadid = 1; + rtos->thread_details->exists = true; + rtos->thread_details->display_str = NULL; + + rtos->thread_details->extra_info_str = (char *) malloc( + sizeof(tmp_thread_extra_info)); + strcpy(rtos->thread_details->extra_info_str, tmp_thread_extra_info); + + rtos->thread_details->thread_name_str = (char *) malloc( + sizeof(tmp_thread_name)); + strcpy(rtos->thread_details->thread_name_str, tmp_thread_name); + + rtos->current_thread = 1; + rtos->thread_count = 1; + return ERROR_OK; + } + + /* create space for new thread details */ + rtos->thread_details = (struct thread_detail *) malloc( + sizeof(struct thread_detail) * tasks_found); + if (!rtos->thread_details) { + LOG_ERROR("Could not allocate space for thread details"); + return -1; + } + + rtos->thread_count = tasks_found; + /* Loop through linked list. */ + struct thread_detail *curr_thrd_details = rtos->thread_details; + while (curr_thrd_details < rtos->thread_details + tasks_found) { + uint32_t name_ptr = 0; + char tmp_str[CHIBIOS_THREAD_NAME_STR_SIZE]; + + retval = target_read_buffer(rtos->target, + current + param->signature->cf_off_newer, + param->signature->ch_ptrsize, + (uint8_t *)¤t); + if (retval != ERROR_OK) { + LOG_ERROR("Could not read next ChibiOS thread"); + return -6; + } + + /* Check for full iteration of the linked list. */ + if (current == rlist) + break; + + /* Save the thread pointer */ + curr_thrd_details->threadid = current; + + /* read the name pointer */ + retval = target_read_buffer(rtos->target, + current + param->signature->cf_off_name, + param->signature->ch_ptrsize, + (uint8_t *)&name_ptr); + if (retval != ERROR_OK) { + LOG_ERROR("Could not read ChibiOS thread name pointer from target"); + return retval; + } + + /* Read the thread name */ + retval = target_read_buffer(rtos->target, name_ptr, + CHIBIOS_THREAD_NAME_STR_SIZE, + (uint8_t *)&tmp_str); + if (retval != ERROR_OK) { + LOG_ERROR("Error reading thread name from ChibiOS target"); + return retval; + } + tmp_str[CHIBIOS_THREAD_NAME_STR_SIZE - 1] = '\x00'; + + if (tmp_str[0] == '\x00') + strcpy(tmp_str, "No Name"); + + curr_thrd_details->thread_name_str = (char *)malloc( + strlen(tmp_str) + 1); + strcpy(curr_thrd_details->thread_name_str, tmp_str); + + /* State info */ + uint8_t threadState; + const char *state_desc; + + retval = target_read_buffer(rtos->target, + current + param->signature->cf_off_state, + 1, &threadState); + if (retval != ERROR_OK) { + LOG_ERROR("Error reading thread state from ChibiOS target"); + return retval; + } + + + if (threadState < CHIBIOS_NUM_STATES) + state_desc = ChibiOS_thread_states[threadState]; + else + state_desc = "Unknown state"; + + curr_thrd_details->extra_info_str = (char *)malloc(strlen( + state_desc)+1); + strcpy(curr_thrd_details->extra_info_str, state_desc); + + curr_thrd_details->exists = true; + curr_thrd_details->display_str = NULL; + + curr_thrd_details++; + } + /* NOTE: By design, cf_off_name equals readylist_current_offset */ + retval = target_read_buffer(rtos->target, + rlist + param->signature->cf_off_name, + param->signature->ch_ptrsize, + (uint8_t *)&rtos->current_thread); + if (retval != ERROR_OK) { + LOG_ERROR("Could not read current Thread from ChibiOS target"); + return retval; + } + + return 0; +} + +static int ChibiOS_get_thread_reg_list(struct rtos *rtos, int64_t thread_id, char **hex_reg_list) +{ + int retval; + const struct ChibiOS_params *param; + int64_t stack_ptr = 0; + + *hex_reg_list = NULL; + if ((rtos == NULL) || (thread_id == 0) || + (rtos->rtos_specific_params == NULL)) + return -1; + + param = (const struct ChibiOS_params *) rtos->rtos_specific_params; + + if (!param->signature) + return -1; + + /* Update stacking if it can only be determined from runtime information */ + if ((param->stacking_info == 0) && + (ChibiOS_update_stacking(rtos) != ERROR_OK)) { + LOG_ERROR("Failed to determine exact stacking for the target type %s", rtos->target->type->name); + return -1; + } + + /* Read the stack pointer */ + retval = target_read_buffer(rtos->target, + thread_id + param->signature->cf_off_ctx, + param->signature->ch_ptrsize, + (uint8_t *)&stack_ptr); + if (retval != ERROR_OK) { + LOG_ERROR("Error reading stack frame from ChibiOS thread"); + return retval; + } + + return rtos_generic_stack_read(rtos->target, param->stacking_info, stack_ptr, hex_reg_list); +} + +static int ChibiOS_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]) +{ + unsigned int i; + *symbol_list = (symbol_table_elem_t *) malloc( + sizeof(symbol_table_elem_t) * CHIBIOS_NUM_SYMBOLS); + + for (i = 0; i < CHIBIOS_NUM_SYMBOLS; i++) + (*symbol_list)[i].symbol_name = ChibiOS_symbol_list[i]; + + return 0; +} + +static int ChibiOS_detect_rtos(struct target *target) +{ + if ((target->rtos->symbols != NULL) && + (target->rtos->symbols[ChibiOS_VAL_rlist].address != 0) && + (target->rtos->symbols[ChibiOS_VAL_chSysInit].address != 0)) { + + if (target->rtos->symbols[ChibiOS_VAL_ch_debug].address == 0) { + LOG_INFO("It looks like the target is running ChibiOS without " + "ch_debug."); + return 0; + } + + /* looks like ChibiOS with memory map enabled.*/ + return 1; + } + + return 0; +} + +static int ChibiOS_create(struct target *target) +{ + int i = 0; + while ((i < CHIBIOS_NUM_PARAMS) && + (0 != strcmp(ChibiOS_params_list[i].target_name, target->type->name))) { + i++; + } + if (i >= CHIBIOS_NUM_PARAMS) { + LOG_WARNING("Could not find target \"%s\" in ChibiOS compatibility " + "list", target->type->name); + return -1; + } + + target->rtos->rtos_specific_params = (void *) &ChibiOS_params_list[i]; + return 0; +} diff --git a/src/rtos/Makefile.am b/src/rtos/Makefile.am index 1aa0c5337..77d274ce5 100644 --- a/src/rtos/Makefile.am +++ b/src/rtos/Makefile.am @@ -22,8 +22,8 @@ include $(top_srcdir)/common.mk METASOURCES = AUTO noinst_LTLIBRARIES = librtos.la -noinst_HEADERS = rtos.h rtos_standard_stackings.h rtos_ecos_stackings.h linux_header.h -librtos_la_SOURCES = rtos.c rtos_standard_stackings.c rtos_ecos_stackings.c FreeRTOS.c ThreadX.c eCos.c linux.c +noinst_HEADERS = rtos.h rtos_standard_stackings.h rtos_ecos_stackings.h linux_header.h rtos_chibios_stackings.h +librtos_la_SOURCES = rtos.c rtos_standard_stackings.c rtos_ecos_stackings.c rtos_chibios_stackings.c FreeRTOS.c ThreadX.c eCos.c linux.c ChibiOS.c librtos_la_CFLAGS = if IS_MINGW diff --git a/src/rtos/rtos.c b/src/rtos/rtos.c index ed559c391..8e2b568dc 100644 --- a/src/rtos/rtos.c +++ b/src/rtos/rtos.c @@ -34,12 +34,14 @@ extern struct rtos_type FreeRTOS_rtos; extern struct rtos_type ThreadX_rtos; extern struct rtos_type eCos_rtos; extern struct rtos_type Linux_os; +extern struct rtos_type ChibiOS_rtos; static struct rtos_type *rtos_types[] = { &ThreadX_rtos, &FreeRTOS_rtos, &eCos_rtos, &Linux_os, + &ChibiOS_rtos, NULL }; diff --git a/src/rtos/rtos_chibios_stackings.c b/src/rtos/rtos_chibios_stackings.c new file mode 100644 index 000000000..2163f0609 --- /dev/null +++ b/src/rtos/rtos_chibios_stackings.c @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright (C) 2012 by Matthias Blaicher * + * Matthias Blaicher - matthias@blaicher.com * + * * + * Copyright (C) 2011 by Broadcom Corporation * + * Evan Hunter - ehunter@broadcom.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 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_chibios_arm_v7m_stack_offsets[] = { + { -1, 32 }, /* r0 */ + { -1, 32 }, /* r1 */ + { -1, 32 }, /* r2 */ + { -1, 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 */ + { -1, 32 }, /* r12 */ + { -2, 32 }, /* sp */ + { -1, 32 }, /* lr */ + { 0x20, 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 */ + { -1, 32 }, /* xPSR */ +}; + +const struct rtos_register_stacking rtos_chibios_arm_v7m_stacking = { + 0x24, /* stack_registers_size */ + -1, /* stack_growth_direction */ + 26, /* num_output_registers */ + 0, /* stack_alignment */ + rtos_chibios_arm_v7m_stack_offsets /* register_offsets */ +}; diff --git a/src/rtos/rtos_chibios_stackings.h b/src/rtos/rtos_chibios_stackings.h new file mode 100644 index 000000000..db45060b8 --- /dev/null +++ b/src/rtos/rtos_chibios_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_CHIBIOS_STACKINGS_H_ +#define INCLUDED_RTOS_CHIBIOS_STACKINGS_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "rtos.h" + +extern const struct rtos_register_stacking rtos_chibios_arm_v7m_stacking; + +#endif /* ifndef INCLUDED_RTOS_CHIBIOS_STACKINGS_H_ */