mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
expression_with_stack: added a tweak on TCB stackpointers to avoid false trigger of stack overflow
This commit is contained in:
parent
124ec431e6
commit
10c498ae7d
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
||||
|
@ -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
|
@ -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>
|
||||
|
@ -1 +0,0 @@
|
||||
.. include:: ../../../en/api-reference/system/esp_expression_with_stack.rst
|
@ -0,0 +1 @@
|
||||
.. include:: ../../../en/api-reference/system/esp_function_with_shared_stack.rst
|
Loading…
Reference in New Issue
Block a user