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

@@ -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;