Răsfoiți Sursa

system: stack watchpoint support on C3

Closes IDF-2307
Jakob Hasse 5 ani în urmă
părinte
comite
b51889dccb

+ 11 - 6
components/freertos/Kconfig

@@ -99,14 +99,19 @@ menu "FreeRTOS"
             FreeRTOS can check if a stack has overflown its bounds by checking either the value of
             the stack pointer or by checking the integrity of canary bytes. (See FREERTOS_CHECK_STACKOVERFLOW
             for more information.) These checks only happen on a context switch, and the situation that caused
-            the stack overflow may already be long gone by then. This option will use the debug memory
-            watchpoint 1 (the second one) to allow breaking into the debugger (or panic'ing) as soon as any
+            the stack overflow may already be long gone by then. This option will use the last debug memory
+            watchpoint to allow breaking into the debugger (or panic'ing) as soon as any
             of the last 32 bytes on the stack of a task are overwritten. The side effect is that using gdb, you
-            effectively only have one watchpoint; the 2nd one is overwritten as soon as a task switch happens.
+            effectively have one hardware watchpoint less because the last one is overwritten as soon as a task
+            switch happens.
 
-            This check only triggers if the stack overflow writes within 4 bytes of the end of the stack, rather than
-            overshooting further, so it is worth combining this approach with one of the other stack overflow check
-            methods.
+            Another consequence is that due to alignment requirements of the watchpoint, the usable stack size
+            decreases by up to 60 bytes. This is because the watchpoint region has to be aligned to its size and the
+            size for the stack watchpoint in IDF is 32 bytes.
+
+            This check only triggers if the stack overflow writes within 32 bytes near the end of the stack, rather
+            than overshooting further, so it is worth combining this approach with one of the other stack overflow
+            check methods.
 
             When this watchpoint is hit, gdb will stop with a SIGTRAP message. When no JTAG OCD is attached, esp-idf
             will panic on an unhandled debug exception.

+ 5 - 1
components/freertos/port/riscv/port.c

@@ -94,6 +94,7 @@
 #include "esp_intr_alloc.h"
 #include "esp_private/crosscore_int.h"
 #include "esp_attr.h"
+#include "esp_debug_helpers.h"
 #include "esp_log.h"
 
 /**
@@ -279,9 +280,12 @@ void vPortYield(void)
 
 }
 
+#define STACK_WATCH_AREA_SIZE 32
 void vPortSetStackWatchpoint(void *pxStackStart)
 {
-    (void)pxStackStart; // TODO ESP32-C3 IDF-2207
+    uint32_t addr = (uint32_t)pxStackStart;
+    addr = (addr + (STACK_WATCH_AREA_SIZE - 1)) & (~(STACK_WATCH_AREA_SIZE - 1));
+    esp_set_watchpoint(7, (char *)addr, STACK_WATCH_AREA_SIZE, ESP_WATCHPOINT_STORE);
 }
 
 BaseType_t xPortInIsrContext(void)

+ 5 - 4
components/hal/esp32c3/include/hal/cpu_ll.h

@@ -107,14 +107,15 @@ static inline void cpu_ll_set_watchpoint(int id,
 {
     uint32_t addr_napot;
     RV_WRITE_CSR(tselect,id);
-    RV_SET_CSR(CSR_TCONTROL,TCONTROL_MTE);
+    RV_SET_CSR(CSR_TCONTROL, TCONTROL_MPTE | TCONTROL_MTE);
     RV_SET_CSR(CSR_TDATA1, TDATA1_USER|TDATA1_MACHINE);
     RV_SET_CSR_FIELD(CSR_TDATA1, TDATA1_MATCH, 1);
-    addr_napot = ((uint32_t)addr)|((size>>1)-1);
-    if(on_read) {
+    // add 0 in napot encoding
+    addr_napot = ((uint32_t) addr) | ((size >> 1) - 1);
+    if (on_read) {
         RV_SET_CSR(CSR_TDATA1, TDATA1_LOAD);
     }
-    if(on_write) {
+    if (on_write) {
         RV_SET_CSR(CSR_TDATA1, TDATA1_STORE);
     }
     RV_WRITE_CSR(tdata2,addr_napot);

+ 2 - 1
components/riscv/include/riscv/csr.h

@@ -90,13 +90,14 @@ extern "C" {
 #define CSR_TDATA1          0x7a1
 
 #define TCONTROL_MTE     (1<<3)    /*R/W, Current M mode trigger enable bit*/
+#define TCONTROL_MPTE    (1<<7)    /*R/W, Previous M mode trigger enable bit*/
 
 #define TDATA1_LOAD      (1<<0)  /*R/W,Fire trigger on load address match*/
 #define TDATA1_STORE     (1<<1)  /*R/W,Fire trigger on store address mat*/
 #define TDATA1_EXECUTE   (1<<2)  /*R/W,Fire trigger on instruction fetch address match*/
 #define TDATA1_USER      (1<<3)  /*R/W,allow trigger to be fired in user mode*/
 #define TDATA1_MACHINE   (1<<6)  /*R/W,Allow trigger to be fired while hart is executing in machine mode*/
-#define TDATA1_MATCH
+#define TDATA1_MATCH     (1<<7)
 #define TDATA1_MATCH_V   (0xF)   /*R/W,Address match type :0 : Exact byte match  1 : NAPOT range match */
 #define TDATA1_MATCH_S   (7)
 

+ 18 - 2
tools/test_apps/system/panic/main/test_panic_main.c

@@ -100,14 +100,30 @@ static void IRAM_ATTR test_int_wdt_cache_disabled(void)
     }
 }
 
+/**
+ * This function overwrites the stack beginning from the valid area continuously towards and beyond
+ * the end of the stack (stack base) of the current task.
+ * This is to test stack protection measures like a watchpoint at the end of the stack.
+ *
+ * @note: This test DOES NOT write beyond the stack limit. It only writes up to exactly the limit itself.
+ *        The FreeRTOS stack protection mechanisms all trigger shortly before the end of the stack.
+ */
 static void test_stack_overflow(void)
 {
     register uint32_t* sp asm("sp");
-    uint32_t *end = sp - CONFIG_ESP_MAIN_TASK_STACK_SIZE;
+    TaskStatus_t pxTaskStatus;
+    vTaskGetInfo(NULL, &pxTaskStatus, pdFALSE, pdFALSE);
+    uint32_t *end = (uint32_t*) pxTaskStatus.pxStackBase;
+
     // offset - 20 bytes from SP in order to not corrupt the current frame.
+    // Need to write from higher to lower addresses since the stack grows downwards and the watchpoint/canary is near
+    // the end of the stack (lowest address).
     for (uint32_t* ptr = sp - 5; ptr != end; --ptr) {
-        *ptr = rand();
+        *ptr = 0;
     }
+
+    // trigger a context switch to initiate checking the FreeRTOS stack canary
+    vTaskDelay(pdMS_TO_TICKS(0));
 }
 
 static void test_illegal_instruction(void)

+ 3 - 0
tools/test_apps/system/panic/sdkconfig.defaults

@@ -10,3 +10,6 @@ CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
 
 # To panic on task WDT
 CONFIG_ESP_TASK_WDT_PANIC=y
+
+# For vTaskGetInfo() used in test_stack_overflow()
+CONFIG_FREERTOS_USE_TRACE_FACILITY=y