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.
|
// limitations under the License.
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
#include "esp_debug_helpers.h"
|
#include "esp_debug_helpers.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
@ -22,52 +24,25 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#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 lock Mutex object to protect in case of shared stack
|
||||||
* @param stack Pointer to user alocated stack
|
* @param stack Pointer to user alocated stack
|
||||||
* @param stack_size Size of current stack in bytes
|
* @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
|
* @note if either lock, stack or stack size is invalid, the expression will
|
||||||
* be called using the current stack.
|
* be called using the current stack.
|
||||||
*/
|
*/
|
||||||
#define ESP_EXECUTE_EXPRESSION_WITH_STACK(lock, stack, stack_size, expression) \
|
void esp_execute_shared_stack_function(SemaphoreHandle_t lock,
|
||||||
({ \
|
void *stack,
|
||||||
assert(lock && stack && (stack_size >= CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE)); \
|
size_t stack_size,
|
||||||
uint32_t backup; \
|
shared_stack_function function);
|
||||||
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); \
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -21,16 +21,30 @@ void external_stack_function(void)
|
|||||||
void another_external_stack_function(void)
|
void another_external_stack_function(void)
|
||||||
{
|
{
|
||||||
//We can even use Freertos resources inside of this context.
|
//We can even use Freertos resources inside of this context.
|
||||||
vTaskDelay(100);
|
printf("We can even use FreeRTOS resources... yielding, sp=%p\n", get_sp());
|
||||||
printf("Done!, 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);
|
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,
|
esp_execute_shared_stack_function(printf_lock,
|
||||||
shared_stack,
|
shared_stack,
|
||||||
SHARED_STACK_SIZE,
|
SHARED_STACK_SIZE,
|
||||||
external_stack_function);
|
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))));
|
(shared_stack_sp < (shared_stack + SHARED_STACK_SIZE))));
|
||||||
|
|
||||||
esp_execute_shared_stack_function(printf_lock,
|
esp_execute_shared_stack_function(printf_lock,
|
||||||
@ -38,7 +52,7 @@ void another_external_stack_function(void)
|
|||||||
SHARED_STACK_SIZE,
|
SHARED_STACK_SIZE,
|
||||||
another_external_stack_function);
|
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))));
|
(shared_stack_sp < (shared_stack + SHARED_STACK_SIZE))));
|
||||||
|
|
||||||
vSemaphoreDelete(printf_lock);
|
vSemaphoreDelete(printf_lock);
|
||||||
|
@ -15,14 +15,33 @@
|
|||||||
#include <esp_expression_with_stack.h>
|
#include <esp_expression_with_stack.h>
|
||||||
#include <freertos/xtensa_rtos.h>
|
#include <freertos/xtensa_rtos.h>
|
||||||
#include <freertos/xtensa_context.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
|
#if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK
|
||||||
int watchpoint_place = (((int)stack + 31) & ~31);
|
int watchpoint_place = (((int)stack + 31) & ~31);
|
||||||
#endif
|
#endif
|
||||||
StackType_t *top_of_stack = (StackType_t *)&stack[0] +
|
//We need also to tweak current task stackpointer to avoid erroneous
|
||||||
((stack_size * sizeof(StackType_t)) / sizeof(StackType_t));
|
//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:
|
//Align stack to a 16byte boundary, as required by CPU specific:
|
||||||
top_of_stack = (StackType_t *)(((UBaseType_t)(top_of_stack - 31) -
|
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);
|
esp_set_watchpoint(2, (char*)watchpoint_place, 32, ESP_WATCHPOINT_STORE);
|
||||||
#endif
|
#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);
|
||||||
}
|
}
|
@ -14,47 +14,34 @@
|
|||||||
|
|
||||||
#include <freertos/xtensa_context.h>
|
#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
|
.text
|
||||||
|
|
||||||
/**
|
|
||||||
* extern void switch_stack_enter(portSTACK_TYPE *stack, portSTACK_TYPE *backup_stack);
|
/* extern void esp_shared_stack_invoke_function(void) */
|
||||||
*/
|
|
||||||
.globl esp_switch_stack_enter
|
.globl esp_shared_stack_invoke_function
|
||||||
.type esp_switch_stack_enter,@function
|
.type esp_shared_stack_invoke_function,@function
|
||||||
.align 4
|
.align 4
|
||||||
esp_switch_stack_enter:
|
esp_shared_stack_invoke_function:
|
||||||
|
|
||||||
#ifndef __XTENSA_CALL0_ABI__
|
#ifndef __XTENSA_CALL0_ABI__
|
||||||
entry sp, 0x10
|
movi a0, 0 /* must not rotate the window here, */
|
||||||
mov a4, a1
|
/* the state of execution for shared stack */
|
||||||
s32i a4, a3, 0 /* on a3 there is a safe place to save the current stack */
|
/* functions will be completely destroyed at end */
|
||||||
l32i a4, a2, 0 /* obtains the user allocated stack buffer */
|
movi a6, xtensa_shared_stack
|
||||||
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
|
|
||||||
l32i sp, a6, 0 /* load shared stack pointer */
|
l32i sp, a6, 0 /* load shared stack pointer */
|
||||||
movi a12, shared_stack_callback
|
movi a12, xtensa_shared_stack_callback
|
||||||
l32i a12, a12, 0
|
l32i a12, a12, 0
|
||||||
callx4 a12 /* call user function */
|
callx4 a12 /* call user function */
|
||||||
movi a6, shared_stack_function_done
|
movi a6, xtensa_shared_stack_function_done
|
||||||
movi a7, 1
|
movi a7, 1
|
||||||
s32i a7, a6, 0 /* hint the function was finished */
|
s32i a7, a6, 0 /* hint the function was finished */
|
||||||
movi a6, shared_stack_env
|
movi a6, xtensa_shared_stack_env
|
||||||
movi a7, 0
|
movi a7, 0
|
||||||
movi a12, longjmp
|
movi a12, longjmp
|
||||||
callx4 a12 /* jump to last clean state previously saved */
|
callx4 a12 /* jump to last clean state previously saved */
|
||||||
|
@ -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
|
which is independent of current task stack, this mechanism can be
|
||||||
used to save stack space wasted by tasks which call a common function
|
used to save stack space wasted by tasks which call a common function
|
||||||
with intensive stack usage such as `printf`. The given function can
|
with intensive stack usage such as `printf`. The given function can
|
||||||
be called inside the macro :cpp:func:`ESP_EXECUTE_EXPRESSION_WITH_STACK`
|
be called inside the shared stack space which is a callback function
|
||||||
it will redirect the target function to be executed using the space
|
deferred by calling :cpp:func:`esp_execute_shared_stack_function`,
|
||||||
allocated by the user.
|
passing that function as parameter
|
||||||
|
|
||||||
Usage
|
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
|
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
|
the same function shares its allocated stack, a pointer to the top
|
||||||
of stack used to that fuction, and the function itself, note the
|
of stack used to that fuction, the size in bytes of stack and, a pointer
|
||||||
function is passed exactly in the same way as do when you call
|
to a user function where the shared stack space will reside, after calling
|
||||||
it on a regular way.
|
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:
|
The usage may looks like the code below:
|
||||||
|
|
||||||
.. code-block:: c
|
.. 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
|
//Let's suppose we wanting to call printf using a separated stack space
|
||||||
//allowing app to reduce its stack size.
|
//allowing app to reduce its stack size.
|
||||||
void app_main()
|
void app_main()
|
||||||
@ -39,9 +46,11 @@ The usage may looks like the code below:
|
|||||||
assert(printf_lock != NULL);
|
assert(printf_lock != NULL);
|
||||||
|
|
||||||
//Call the desired function using the macro helper:
|
//Call the desired function using the macro helper:
|
||||||
ESP_EXECUTE_EXPRESSION_WITH_STACK(printf_lock,
|
esp_execute_shared_stack_function(printf_lock,
|
||||||
shared_stack,
|
shared_stack,
|
||||||
printf("Executing this from external stack! \n"));
|
8192,
|
||||||
|
external_stack_function);
|
||||||
|
|
||||||
vSemaphoreDelete(printf_lock);
|
vSemaphoreDelete(printf_lock);
|
||||||
free(shared_stack);
|
free(shared_stack);
|
||||||
}
|
}
|
||||||
@ -52,5 +61,3 @@ 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 Allocation <mem_alloc>
|
||||||
Heap Memory Debugging <heap_debug>
|
Heap Memory Debugging <heap_debug>
|
||||||
High Resolution Timer <esp_timer>
|
High Resolution Timer <esp_timer>
|
||||||
:esp32: Himem (large external SPI RAM) API <himem>
|
Himem (large external SPI RAM) API <himem>
|
||||||
:esp32: Inter-Processor Call <ipc>
|
Inter-Processor Call <ipc>
|
||||||
Call function with external stack <esp_function_with_shared_stack>
|
Call function with external stack <esp_function_with_shared_stack>
|
||||||
Interrupt Allocation <intr_alloc>
|
Interrupt Allocation <intr_alloc>
|
||||||
Logging <log>
|
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