فهرست منبع

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

Felipe Neves 5 سال پیش
والد
کامیت
223f800dd7

+ 13 - 43
components/esp_common/include/esp_expression_with_stack.h

@@ -13,6 +13,7 @@
 // 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 "freertos/task.h"
@@ -23,57 +24,26 @@
 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)      \
-({                                                                                  \
-    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);                                                 \
-    StaticTask_t *current = (StaticTask_t *)xTaskGetCurrentTaskHandle();            \
-    /* pxDummy6 is the stack base of current thread defined in TCB_t */             \
-    /* place the watchpoint on current task stack after function execution*/        \
-    vPortSetStackWatchpoint(current->pxDummy6);                                     \
-    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
 #ifdef __cplusplus
 }
 }
-#endif
+#endif

+ 9 - 5
components/newlib/test/test_shared_stack_printf.c

@@ -21,8 +21,8 @@ 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("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();
     shared_stack_sp = (StackType_t *)get_sp();
 }
 }
 
 
@@ -30,17 +30,21 @@ TEST_CASE("test printf using shared buffer stack", "[newlib]")
 {
 {
     portSTACK_TYPE *shared_stack = malloc(SHARED_STACK_SIZE);
     portSTACK_TYPE *shared_stack = malloc(SHARED_STACK_SIZE);
 
 
-    TEST_ASSERT(shared_stack != NULL);
+    TEST_ASSERT_NOT_NULL(shared_stack);
 
 
     SemaphoreHandle_t printf_lock = xSemaphoreCreateMutex();
     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, 
@@ -48,7 +52,7 @@ TEST_CASE("test printf using shared buffer stack", "[newlib]")
                                     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);   

+ 59 - 16
components/xtensa/expression_with_stack_xtensa.c

@@ -15,29 +15,72 @@
 #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
     esp_clear_watchpoint(1);
     esp_clear_watchpoint(1);
-    uint32_t watchpoint_place = ((uint32_t)stack + 32) & 0x1f ;
-#endif    
-    StackType_t *top_of_stack =  (StackType_t *)&stack[0] +                  
-            ((stack_size * sizeof(StackType_t)) / sizeof(StackType_t));
+    uint32_t watchpoint_place = ((uint32_t)stack + 32) & ~0x1f ;
+#endif
+    //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));
 
 
-    //Align stack to a 16byte boundary, as required by CPU specific:
-    top_of_stack =  (StackType_t *)(((UBaseType_t)(top_of_stack - 31) -
-                                    ALIGNUP(0x10, sizeof(XtSolFrame) )) & 
-                                    ~0xf);
+    StaticTask_t *current = (StaticTask_t *)xTaskGetCurrentTaskHandle();
+    //Then put the fake stack inside of TCB:
+    current_task_stack = current->pxDummy6;
+    current->pxDummy6 = (void *)stack;
 
 
-    //Fake stack frame to do not break the backtrace
-    XtSolFrame *frame = (XtSolFrame *)top_of_stack;
-    frame->a0 = 0;
-    frame->a1 = (UBaseType_t)top_of_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 - 16) & ~0xf));
 
 
 #if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK
 #if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK
-    esp_set_watchpoint(1, (uint8_t *)watchpoint_place, 32, ESP_WATCHPOINT_STORE);    
+    esp_set_watchpoint(1, (uint8_t *)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);
+}

+ 21 - 34
components/xtensa/expression_with_stack_xtensa_asm.S

@@ -13,52 +13,39 @@
 // limitations under the License.
 // limitations under the License.
 
 
 #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);
- */
-    .globl     esp_switch_stack_enter
-    .type       esp_switch_stack_enter,@function
-    .align      4
-esp_switch_stack_enter:
 
 
-    #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 esp_shared_stack_invoke_function(void) */
 
 
-/**
- * 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:
+    .globl     esp_shared_stack_invoke_function
+    .type       esp_shared_stack_invoke_function,@function
+    .align      4
+esp_shared_stack_invoke_function:
 
 
     #ifndef __XTENSA_CALL0_ABI__
     #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 */
     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    a7, 1       
+    movi    a6, xtensa_shared_stack_function_done
+    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 */
     ret
     ret
-    #else 
+    #else
     #error "this code is written for Window ABI"
     #error "this code is written for Window ABI"
     #endif
     #endif

+ 20 - 13
docs/en/api-reference/system/esp_expression_with_stack.rst → docs/en/api-reference/system/esp_function_with_shared_stack.rst

@@ -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` 
-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
 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
-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:
 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, 
-                                        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);    
         vSemaphoreDelete(printf_lock);    
         free(shared_stack); 
         free(shared_stack); 
     }
     }
@@ -51,6 +60,4 @@ The usage may looks like the code below:
 API Reference
 API Reference
 -------------
 -------------
 
 
-.. include-build-file:: inc/esp_expression_with_stack.inc
-
-
+.. include-build-file:: inc/esp_expression_with_stack.inc

+ 0 - 1
docs/zh_CN/api-reference/system/esp_expression_with_stack.rst

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

+ 1 - 0
docs/zh_CN/api-reference/system/esp_function_with_shared_stack.rst

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