Merge branch 'staging/riscv_wrapper_freertos_tasks_v5.0' into 'release/v5.0'

RISC-V: Create a wrapper around FreeRTOS Tasks to detect the ones returning (backport v5.0)

See merge request espressif/esp-idf!21202
This commit is contained in:
Zim Kalinowski
2022-12-20 16:34:13 +08:00
3 changed files with 72 additions and 60 deletions

View File

@ -247,8 +247,8 @@ void esp_panic_handler(panic_info_t *info)
* *
* ---------------------------------------------------------------------------------------- * ----------------------------------------------------------------------------------------
* core - core where exception was triggered * core - core where exception was triggered
* exception - what kind of exception occured * exception - what kind of exception occurred
* description - a short description regarding the exception that occured * description - a short description regarding the exception that occurred
* details - more details about the exception * details - more details about the exception
* state - processor state like register contents, and backtrace * state - processor state like register contents, and backtrace
* elf_info - details about the image currently running * elf_info - details about the image currently running
@ -313,6 +313,14 @@ void esp_panic_handler(panic_info_t *info)
PANIC_INFO_DUMP(info, state); PANIC_INFO_DUMP(info, state);
panic_print_str("\r\n"); 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 #ifdef WITH_ELF_SHA256
panic_print_str("\r\nELF file SHA256: "); panic_print_str("\r\nELF file SHA256: ");
char sha256_buf[65]; char sha256_buf[65];
@ -349,10 +357,6 @@ void esp_panic_handler(panic_info_t *info)
} else { } else {
disable_all_wdts(); disable_all_wdts();
s_dumping_core = true; 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 #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
esp_core_dump_to_flash(info); esp_core_dump_to_flash(info);
#endif #endif

View File

@ -515,30 +515,29 @@ void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
#endif //( configSUPPORT_STATIC_ALLOCATION == 1 ) #endif //( configSUPPORT_STATIC_ALLOCATION == 1 )
// ------------------------ Stack -------------------------- // ------------------------ Stack --------------------------
#if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
__attribute__((noreturn)) static void _prvTaskExitError(void) /**
* 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 `naked` attribute, the compiler won't generate a prologue and epilogue for the function, which saves time
* and stack space.
*/
static void __attribute__((optimize("O1"), naked)) vPortTaskWrapper(TaskFunction_t pxCode, void *pvParameters)
{ {
/* A function that implements a task must not exit or attempt to return to asm volatile(".cfi_undefined ra\n");
its caller as there is nothing to return to. If a task wants to exit it extern void __attribute__((noreturn)) panic_abort(const char *details);
should instead call vTaskDelete( NULL ). static char DRAM_ATTR msg[80] = "FreeRTOS: FreeRTOS Task \"\0";
pxCode(pvParameters);
Artificially force an assert() to be triggered if configASSERT() is //FreeRTOS tasks should not return. Log the task name and abort.
defined, then stop here so application writers can catch the error. */ char *pcTaskName = pcTaskGetName(NULL);
portDISABLE_INTERRUPTS(); /* We cannot use s(n)printf because it is in flash */
abort(); strcat(msg, pcTaskName);
strcat(msg, "\" should not return, Aborting now!");
panic_abort(msg);
} }
#endif // CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
__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();
}
StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters) StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters)
{ {
@ -602,11 +601,16 @@ StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxC
sp -= RV_STK_FRMSZ; sp -= RV_STK_FRMSZ;
RvExcFrame *frame = (RvExcFrame *)sp; RvExcFrame *frame = (RvExcFrame *)sp;
memset(frame, 0, sizeof(*frame)); memset(frame, 0, sizeof(*frame));
/* 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. */ /* Initialize the stack frame. */
frame->ra = (UBaseType_t)prvTaskExitError + 4/*size of the nop insruction at the beginning of prvTaskExitError*/; #if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
frame->mepc = (UBaseType_t)pxCode; frame->mepc = (UBaseType_t)vPortTaskWrapper;
frame->a0 = (UBaseType_t)pvParameters; 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->gp = (UBaseType_t)&__global_pointer$;
frame->tp = (UBaseType_t)threadptr; frame->tp = (UBaseType_t)threadptr;

View File

@ -113,31 +113,29 @@ void vPortEndScheduler(void)
} }
// ------------------------ Stack -------------------------- // ------------------------ Stack --------------------------
#if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
__attribute__((noreturn)) static void _prvTaskExitError(void) /**
* 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 `naked` attribute, the compiler won't generate a prologue and epilogue for the function, which saves time
* and stack space.
*/
static void __attribute__((optimize("O1"), naked)) vPortTaskWrapper(TaskFunction_t pxCode, void *pvParameters)
{ {
/* A function that implements a task must not exit or attempt to return to asm volatile(".cfi_undefined ra\n");
its caller as there is nothing to return to. If a task wants to exit it extern void __attribute__((noreturn)) panic_abort(const char *details);
should instead call vTaskDelete( NULL ). static char DRAM_ATTR msg[80] = "FreeRTOS: FreeRTOS Task \"\0";
pxCode(pvParameters);
Artificially force an assert() to be triggered if configASSERT() is //FreeRTOS tasks should not return. Log the task name and abort.
defined, then stop here so application writers can catch the error. */ char *pcTaskName = pcTaskGetName(NULL);
configASSERT(uxCriticalNesting == ~0UL); /* We cannot use s(n)printf because it is in flash */
portDISABLE_INTERRUPTS(); strcat(msg, pcTaskName);
abort(); strcat(msg, "\" should not return, Aborting now!");
panic_abort(msg);
} }
#endif // CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
__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();
}
StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters) StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters)
{ {
@ -201,12 +199,18 @@ StackType_t *pxPortInitialiseStack(StackType_t *pxTopOfStack, TaskFunction_t pxC
sp -= RV_STK_FRMSZ; sp -= RV_STK_FRMSZ;
RvExcFrame *frame = (RvExcFrame *)sp; RvExcFrame *frame = (RvExcFrame *)sp;
memset(frame, 0, sizeof(*frame)); memset(frame, 0, sizeof(*frame));
/* 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. */ /* Initialize the stack frame. */
frame->ra = (UBaseType_t)prvTaskExitError + 4/*size of the nop insruction at the beginning of prvTaskExitError*/; extern uint32_t __global_pointer$;
frame->mepc = (UBaseType_t)pxCode; #if CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER
frame->a0 = (UBaseType_t)pvParameters; frame->mepc = (UBaseType_t)vPortTaskWrapper;
frame->gp = (UBaseType_t)&__global_pointer$; 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; frame->tp = (UBaseType_t)threadptr;
//TODO: IDF-2393 //TODO: IDF-2393