expression_with_stack: added a tweak on TCB stackpointers to avoid false trigger of stack overflow

This commit is contained in:
Felipe Neves 2020-04-01 12:10:13 -03:00
parent 124ec431e6
commit 10c498ae7d
8 changed files with 132 additions and 99 deletions

View File

@ -13,8 +13,10 @@
// limitations under the License.
#pragma once
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "esp_debug_helpers.h"
#include "esp_log.h"
@ -22,53 +24,26 @@
extern "C" {
#endif
typedef void (*shared_stack_function)(void);
#define ESP_EXECUTE_EXPRESSION_WITH_STACK(lock, stack, stack_size, expression) \
esp_execute_shared_stack_function(lock, stack, stack_size, expression)
/**
* @brief Executes a 1-line expression with a application alocated stack
* @brief Calls user defined shared stack space function
* @param lock Mutex object to protect in case of shared stack
* @param stack Pointer to user alocated stack
* @param stack_size Size of current stack in bytes
* @param expression Expression or function to be executed using the stack
* @param function pointer to the shared stack function to be executed
* @note if either lock, stack or stack size is invalid, the expression will
* be called using the current stack.
*/
#define ESP_EXECUTE_EXPRESSION_WITH_STACK(lock, stack, stack_size, expression) \
({ \
assert(lock && stack && (stack_size >= CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE)); \
uint32_t backup; \
xSemaphoreTake(lock, portMAX_DELAY); \
StackType_t *top_of_stack = esp_switch_stack_setup(stack, stack_size); \
esp_switch_stack_enter(top_of_stack, &backup); \
{ \
expression; \
} \
esp_switch_stack_exit(&backup); \
xSemaphoreGive(lock); \
})
void esp_execute_shared_stack_function(SemaphoreHandle_t lock,
void *stack,
size_t stack_size,
shared_stack_function function);
/**
* @brief Fill stack frame with CPU-specifics value before use
* @param stack Caller allocated stack pointer
* @param stack_size Size of stack in bytes
* @return New pointer to the top of stack
* @note Application must not call this function directly
*/
StackType_t * esp_switch_stack_setup(StackType_t *stack, size_t stack_size);
/**
* @brief Changes CPU sp-register to use another stack space and save the previous one
* @param stack Caller allocated stack pointer
* @param backup_stack Pointer to a place to save the current stack
* @note Application must not call this function directly
*/
extern void esp_switch_stack_enter(StackType_t *stack, uint32_t *backup_stack);
/**
* @brief Restores the previous CPU sp-register
* @param backup_stack Pointer to the place where stack was saved
* @note Application must not call this function directly
*/
extern void esp_switch_stack_exit(uint32_t *backup_stack);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -21,16 +21,30 @@ void external_stack_function(void)
void another_external_stack_function(void)
{
//We can even use Freertos resources inside of this context.
vTaskDelay(100);
printf("Done!, sp=%p\n", get_sp());
printf("We can even use FreeRTOS resources... yielding, sp=%p\n", get_sp());
taskYIELD();
shared_stack_sp = (StackType_t *)get_sp();
}
TEST_CASE("test printf using shared buffer stack", "[newlib]")
{
portSTACK_TYPE *shared_stack = malloc(SHARED_STACK_SIZE);
TEST_ASSERT_NOT_NULL(shared_stack);
SemaphoreHandle_t printf_lock = xSemaphoreCreateMutex();
TEST_ASSERT_NOT_NULL(printf_lock);
printf("current task sp: %p\n", get_sp());
printf("shared_stack: %p\n", (void *)shared_stack);
printf("shared_stack expected top: %p\n", (void *)(shared_stack + SHARED_STACK_SIZE));
esp_execute_shared_stack_function(printf_lock,
shared_stack,
SHARED_STACK_SIZE,
external_stack_function);
TEST_ASSERT(((shared_stack_sp >= shared_stack_sp) &&
TEST_ASSERT(((shared_stack_sp >= shared_stack) &&
(shared_stack_sp < (shared_stack + SHARED_STACK_SIZE))));
esp_execute_shared_stack_function(printf_lock,
@ -38,7 +52,7 @@ void another_external_stack_function(void)
SHARED_STACK_SIZE,
another_external_stack_function);
TEST_ASSERT(((shared_stack_sp >= shared_stack_sp) &&
TEST_ASSERT(((shared_stack_sp >= shared_stack) &&
(shared_stack_sp < (shared_stack + SHARED_STACK_SIZE))));
vSemaphoreDelete(printf_lock);

View File

@ -15,14 +15,33 @@
#include <esp_expression_with_stack.h>
#include <freertos/xtensa_rtos.h>
#include <freertos/xtensa_context.h>
#include <setjmp.h>
#include <string.h>
StackType_t * esp_switch_stack_setup(StackType_t *stack, size_t stack_size)
StackType_t *xtensa_shared_stack;
shared_stack_function xtensa_shared_stack_callback;
jmp_buf xtensa_shared_stack_env;
bool xtensa_shared_stack_function_done = false;
static portMUX_TYPE xtensa_shared_stack_spinlock = portMUX_INITIALIZER_UNLOCKED;
static void *current_task_stack = NULL;
extern void esp_shared_stack_invoke_function(void);
static void esp_switch_stack_setup(StackType_t *stack, size_t stack_size)
{
#if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK
int watchpoint_place = (((int)stack + 31) & ~31);
#endif
StackType_t *top_of_stack = (StackType_t *)&stack[0] +
((stack_size * sizeof(StackType_t)) / sizeof(StackType_t));
//We need also to tweak current task stackpointer to avoid erroneous
//stack overflow indication, so fills the stack with freertos known pattern:
memset(stack, 0xa5U, stack_size * sizeof(StackType_t));
StaticTask_t *current = (StaticTask_t *)xTaskGetCurrentTaskHandle();
//Then put the fake stack inside of TCB:
current_task_stack = current->pxDummy6;
current->pxDummy6 = (void *)stack;
StackType_t *top_of_stack = stack + stack_size;
//Align stack to a 16byte boundary, as required by CPU specific:
top_of_stack = (StackType_t *)(((UBaseType_t)(top_of_stack - 31) -
@ -38,5 +57,36 @@ StackType_t * esp_switch_stack_setup(StackType_t *stack, size_t stack_size)
esp_set_watchpoint(2, (char*)watchpoint_place, 32, ESP_WATCHPOINT_STORE);
#endif
return top_of_stack;
xtensa_shared_stack = top_of_stack;
}
void esp_execute_shared_stack_function(SemaphoreHandle_t lock, void *stack, size_t stack_size, shared_stack_function function)
{
assert(lock);
assert(stack);
assert(stack_size > 0 && stack_size >= CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE);
assert(function);
xSemaphoreTake(lock, portMAX_DELAY);
portENTER_CRITICAL(&xtensa_shared_stack_spinlock);
xtensa_shared_stack_function_done = false;
esp_switch_stack_setup(stack, stack_size);
xtensa_shared_stack_callback = function;
portEXIT_CRITICAL(&xtensa_shared_stack_spinlock);
setjmp(xtensa_shared_stack_env);
if(!xtensa_shared_stack_function_done) {
esp_shared_stack_invoke_function();
}
portENTER_CRITICAL(&xtensa_shared_stack_spinlock);
StaticTask_t *current = (StaticTask_t *)xTaskGetCurrentTaskHandle();
//Restore current task stack:
current->pxDummy6 = (StackType_t *)current_task_stack;
vPortSetStackWatchpoint(current->pxDummy6);
portEXIT_CRITICAL(&xtensa_shared_stack_spinlock);
xSemaphoreGive(lock);
}

View File

@ -13,52 +13,39 @@
// limitations under the License.
#include <freertos/xtensa_context.h>
.extern esp_clear_watchpoint
.extern xtensa_shared_stack
.extern xtensa_shared_stack_callback
.extern xtensa_shared_stack_function_done
.extern xtensa_shared_stack_env
.extern longjmp
.text
/**
* extern void switch_stack_enter(portSTACK_TYPE *stack, portSTACK_TYPE *backup_stack);
*/
.globl esp_switch_stack_enter
.type esp_switch_stack_enter,@function
/* extern void esp_shared_stack_invoke_function(void) */
.globl esp_shared_stack_invoke_function
.type esp_shared_stack_invoke_function,@function
.align 4
esp_switch_stack_enter:
esp_shared_stack_invoke_function:
#ifndef __XTENSA_CALL0_ABI__
entry sp, 0x10
mov a4, a1
s32i a4, a3, 0 /* on a3 there is a safe place to save the current stack */
l32i a4, a2, 0 /* obtains the user allocated stack buffer */
mov a1, a4 /* sp register now contains caller specified stack */
retw
#else
#error "this code is written for Window ABI"
#endif
/**
* extern void switch_stack_exit(portSTACK_TYPE *backup_stack);
*/
.globl esp_switch_stack_exit
.type esp_switch_stack_exit,@function
.align 4
esp_switch_stack_exit:
#ifndef __XTENSA_CALL0_ABI__
movi a0, 0 /* no need to rotate window, it will be destroyed anyway */
movi a6, shared_stack
movi a0, 0 /* must not rotate the window here, */
/* the state of execution for shared stack */
/* functions will be completely destroyed at end */
movi a6, xtensa_shared_stack
l32i sp, a6, 0 /* load shared stack pointer */
movi a12, shared_stack_callback
movi a12, xtensa_shared_stack_callback
l32i a12, a12, 0
callx4 a12 /* call user function */
movi a6, shared_stack_function_done
movi a7, 1
movi a6, xtensa_shared_stack_function_done
movi a7, 1
s32i a7, a6, 0 /* hint the function was finished */
movi a6, shared_stack_env
movi a6, xtensa_shared_stack_env
movi a7, 0
movi a12, longjmp
movi a12, longjmp
callx4 a12 /* jump to last clean state previously saved */
ret
#else
#error "this code is written for Window ABI"
#else
#error "this code is written for Window ABI"
#endif

View File

@ -8,24 +8,31 @@ A given function can be executed with a user allocated stack space
which is independent of current task stack, this mechanism can be
used to save stack space wasted by tasks which call a common function
with intensive stack usage such as `printf`. The given function can
be called inside the macro :cpp:func:`ESP_EXECUTE_EXPRESSION_WITH_STACK`
it will redirect the target function to be executed using the space
allocated by the user.
be called inside the shared stack space which is a callback function
deferred by calling :cpp:func:`esp_execute_shared_stack_function`,
passing that function as parameter
Usage
-----
:cpp:func:`ESP_EXECUTE_EXPRESSION_WITH_STACK` takes three arguments,
:cpp:func:`esp_execute_shared_stack_function` takes four arguments,
a mutex object allocated by the caller, which is used to protect if
the same function shares its allocated stack, a pointer to the top
of stack used to that fuction, and the function itself, note the
function is passed exactly in the same way as do when you call
it on a regular way.
of stack used to that fuction, the size in bytes of stack and, a pointer
to a user function where the shared stack space will reside, after calling
the function, the user defined function will be deferred as a callback
where functions can be called using the user allocated space without
taking space from current task stack.
The usage may looks like the code below:
.. code-block:: c
void external_stack_function(void)
{
printf("Executing this printf from external stack! \n");
}
//Let's suppose we wanting to call printf using a separated stack space
//allowing app to reduce its stack size.
void app_main()
@ -39,9 +46,11 @@ The usage may looks like the code below:
assert(printf_lock != NULL);
//Call the desired function using the macro helper:
ESP_EXECUTE_EXPRESSION_WITH_STACK(printf_lock,
shared_stack,
printf("Executing this from external stack! \n"));
esp_execute_shared_stack_function(printf_lock,
shared_stack,
8192,
external_stack_function);
vSemaphoreDelete(printf_lock);
free(shared_stack);
}
@ -51,6 +60,4 @@ The usage may looks like the code below:
API Reference
-------------
.. include:: /_build/inc/esp_expression_with_stack.inc
.. include:: /_build/inc/esp_expression_with_stack.inc

View File

@ -16,8 +16,8 @@ System API
Heap Memory Allocation <mem_alloc>
Heap Memory Debugging <heap_debug>
High Resolution Timer <esp_timer>
:esp32: Himem (large external SPI RAM) API <himem>
:esp32: Inter-Processor Call <ipc>
Himem (large external SPI RAM) API <himem>
Inter-Processor Call <ipc>
Call function with external stack <esp_function_with_shared_stack>
Interrupt Allocation <intr_alloc>
Logging <log>

View File

@ -1 +0,0 @@
.. include:: ../../../en/api-reference/system/esp_expression_with_stack.rst

View File

@ -0,0 +1 @@
.. include:: ../../../en/api-reference/system/esp_function_with_shared_stack.rst