RISC-V: Create a wrapper around FreeRTOS Tasks to detect the ones returning

This commit is contained in:
Omar Chebib 2022-10-27 18:41:19 +08:00
parent 6a8f528e08
commit d580f6b076
3 changed files with 80 additions and 73 deletions

View File

@ -257,8 +257,8 @@ void esp_panic_handler(panic_info_t *info)
*
* ----------------------------------------------------------------------------------------
* core - core where exception was triggered
* exception - what kind of exception occured
* description - a short description regarding the exception that occured
* exception - what kind of exception occurred
* description - a short description regarding the exception that occurred
* details - more details about the exception
* state - processor state like register contents, and backtrace
* elf_info - details about the image currently running
@ -323,6 +323,14 @@ void esp_panic_handler(panic_info_t *info)
PANIC_INFO_DUMP(info, state);
panic_print_str("\r\n");
/* No matter if we come here from abort or an exception, this variable must be reset.
* Else, any exception/error occurring during the current panic handler would considered
* an abort. Do this after PANIC_INFO_DUMP(info, state) as it also checks this variable.
* For example, if coredump triggers a stack overflow and this variable is not reset,
* the second panic would be still be marked as the result of an abort, even the previous
* message reason would be kept. */
g_panic_abort = false;
#ifdef WITH_ELF_SHA256
panic_print_str("\r\nELF file SHA256: ");
char sha256_buf[65];
@ -359,10 +367,6 @@ void esp_panic_handler(panic_info_t *info)
} else {
disable_all_wdts();
s_dumping_core = true;
/* No matter if we come here from abort or an exception, this variable must be reset.
* Else, any exception/error occuring during the current panic handler would considered
* an abort. */
g_panic_abort = false;
#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
esp_core_dump_to_flash(info);
#endif

View File

@ -536,30 +536,6 @@ void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
// ------------------------ Stack --------------------------
__attribute__((noreturn)) static void _prvTaskExitError(void)
{
/* A function that implements a task must not exit or attempt to return to
its caller as there is nothing to return to. If a task wants to exit it
should instead call vTaskDelete( NULL ).
Artificially force an assert() to be triggered if configASSERT() is
defined, then stop here so application writers can catch the error. */
portDISABLE_INTERRUPTS();
abort();
}
__attribute__((naked)) static void prvTaskExitError(void)
{
asm volatile(".option push\n" \
".option norvc\n" \
"nop\n" \
".option pop");
/* Task entry's RA will point here. Shifting RA into prvTaskExitError is necessary
to make GDB backtrace ending inside that function.
Otherwise backtrace will end in the function laying just before prvTaskExitError in address space. */
_prvTaskExitError();
}
/**
* @brief Align stack pointer in a downward growing stack
*
@ -641,6 +617,32 @@ FORCE_INLINE_ATTR UBaseType_t uxInitialiseStackTLS(UBaseType_t uxStackPointer, u
return uxStackPointer;
}
#if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
/**
* Wrapper to allow task functions to return. Force the optimization option -O1 on that function to make sure there
* is no tail-call. Indeed, we need the compiler to keep the return address to this function when calling `panic_abort`.
*
* Thanks to the `naked` attribute, GDB stub backtrace doesn't print:
* #2 0x00000000 in ?? ()
*
* However, this also means that when calculating vPortTaskWrapper's call frame in a backtrace, return address
* register `ra`, will NOT contain 0x00000000.
*/
static void __attribute__((optimize("O1"), naked)) vPortTaskWrapper(TaskFunction_t pxCode, void *pvParameters)
{
extern void __attribute__((noreturn)) panic_abort(const char *details);
static char DRAM_ATTR msg[80] = "FreeRTOS: FreeRTOS Task \"\0";
pxCode(pvParameters);
//FreeRTOS tasks should not return. Log the task name and abort.
char *pcTaskName = pcTaskGetName(NULL);
/* We cannot use s(n)printf because it is in flash */
strcat(msg, pcTaskName);
strcat(msg, "\" should not return, Aborting now!");
panic_abort(msg);
}
#endif // CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
/**
* @brief Initialize the task's starting interrupt stack frame
*
@ -669,16 +671,16 @@ FORCE_INLINE_ATTR UBaseType_t uxInitialiseStackFrame(UBaseType_t uxStackPointer,
RvExcFrame *frame = (RvExcFrame *)uxStackPointer;
memset(frame, 0, sizeof(RvExcFrame));
/*
Initialize the stack frame.
Note: Shifting RA into prvTaskExitError is necessary to make the GDB backtrace terminate inside that function.
Otherwise, the backtrace will end in the function located just before prvTaskExitError in the address space.
*/
/* Initialize the stack frame. */
extern uint32_t __global_pointer$;
frame->ra = (UBaseType_t)prvTaskExitError + 4; // size of the nop instruction at the beginning of prvTaskExitError
frame->mepc = (UBaseType_t)pxCode;
frame->a0 = (UBaseType_t)pvParameters;
#if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
frame->mepc = (UBaseType_t)vPortTaskWrapper;
frame->a0 = (UBaseType_t)pxCode;
frame->a1 = (UBaseType_t)pvParameters;
#else
frame->mepc = (UBaseType_t)pxCode;
frame->a0 = (UBaseType_t)pvParameters;
#endif // CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
frame->gp = (UBaseType_t)&__global_pointer$;
frame->tp = (UBaseType_t)threadptr_reg_init;

View File

@ -114,31 +114,6 @@ void vPortEndScheduler(void)
// ------------------------ Stack --------------------------
__attribute__((noreturn)) static void _prvTaskExitError(void)
{
/* A function that implements a task must not exit or attempt to return to
its caller as there is nothing to return to. If a task wants to exit it
should instead call vTaskDelete( NULL ).
Artificially force an assert() to be triggered if configASSERT() is
defined, then stop here so application writers can catch the error. */
configASSERT(uxCriticalNesting == ~0UL);
portDISABLE_INTERRUPTS();
abort();
}
__attribute__((naked)) static void prvTaskExitError(void)
{
asm volatile(".option push\n" \
".option norvc\n" \
"nop\n" \
".option pop");
/* Task entry's RA will point here. Shifting RA into prvTaskExitError is necessary
to make GDB backtrace ending inside that function.
Otherwise backtrace will end in the function laying just before prvTaskExitError in address space. */
_prvTaskExitError();
}
/**
* @brief Align stack pointer in a downward growing stack
*
@ -220,6 +195,32 @@ FORCE_INLINE_ATTR UBaseType_t uxInitialiseStackTLS(UBaseType_t uxStackPointer, u
return uxStackPointer;
}
#if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
/**
* Wrapper to allow task functions to return. Force the optimization option -O1 on that function to make sure there
* is no tail-call. Indeed, we need the compiler to keep the return address to this function when calling `panic_abort`.
*
* Thanks to the `naked` attribute, GDB stub backtrace doesn't print:
* #2 0x00000000 in ?? ()
*
* However, this also means that when calculating vPortTaskWrapper's call frame in a backtrace, return address
* register `ra`, will NOT contain 0x00000000.
*/
static void __attribute__((optimize("O1"), naked)) vPortTaskWrapper(TaskFunction_t pxCode, void *pvParameters)
{
extern void __attribute__((noreturn)) panic_abort(const char *details);
static char DRAM_ATTR msg[80] = "FreeRTOS: FreeRTOS Task \"\0";
pxCode(pvParameters);
//FreeRTOS tasks should not return. Log the task name and abort.
char *pcTaskName = pcTaskGetName(NULL);
/* We cannot use s(n)printf because it is in flash */
strcat(msg, pcTaskName);
strcat(msg, "\" should not return, Aborting now!");
panic_abort(msg);
}
#endif // CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
/**
* @brief Initialize the task's starting interrupt stack frame
*
@ -248,16 +249,16 @@ FORCE_INLINE_ATTR UBaseType_t uxInitialiseStackFrame(UBaseType_t uxStackPointer,
RvExcFrame *frame = (RvExcFrame *)uxStackPointer;
memset(frame, 0, sizeof(RvExcFrame));
/*
Initialize the stack frame.
Note: Shifting RA into prvTaskExitError is necessary to make the GDB backtrace terminate inside that function.
Otherwise, the backtrace will end in the function located just before prvTaskExitError in the address space.
*/
/* Initialize the stack frame. */
extern uint32_t __global_pointer$;
frame->ra = (UBaseType_t)prvTaskExitError + 4; // size of the nop instruction at the beginning of prvTaskExitError
frame->mepc = (UBaseType_t)pxCode;
frame->a0 = (UBaseType_t)pvParameters;
#if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
frame->mepc = (UBaseType_t)vPortTaskWrapper;
frame->a0 = (UBaseType_t)pxCode;
frame->a1 = (UBaseType_t)pvParameters;
#else
frame->mepc = (UBaseType_t)pxCode;
frame->a0 = (UBaseType_t)pvParameters;
#endif // CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
frame->gp = (UBaseType_t)&__global_pointer$;
frame->tp = (UBaseType_t)threadptr_reg_init;