mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-01 19:54:32 +02:00
Merge branch 'feature/c3_stack_watchpoint' into 'master'
c3 stack watchpoint Closes IDF-2307 See merge request espressif/esp-idf!11576
This commit is contained in:
@@ -99,14 +99,19 @@ menu "FreeRTOS"
|
|||||||
FreeRTOS can check if a stack has overflown its bounds by checking either the value of
|
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
|
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
|
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
|
the stack overflow may already be long gone by then. This option will use the last debug memory
|
||||||
watchpoint 1 (the second one) to allow breaking into the debugger (or panic'ing) as soon as any
|
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
|
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
|
Another consequence is that due to alignment requirements of the watchpoint, the usable stack size
|
||||||
overshooting further, so it is worth combining this approach with one of the other stack overflow check
|
decreases by up to 60 bytes. This is because the watchpoint region has to be aligned to its size and the
|
||||||
methods.
|
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
|
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.
|
will panic on an unhandled debug exception.
|
||||||
|
@@ -94,6 +94,7 @@
|
|||||||
#include "esp_intr_alloc.h"
|
#include "esp_intr_alloc.h"
|
||||||
#include "esp_private/crosscore_int.h"
|
#include "esp_private/crosscore_int.h"
|
||||||
#include "esp_attr.h"
|
#include "esp_attr.h"
|
||||||
|
#include "esp_debug_helpers.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -279,9 +280,12 @@ void vPortYield(void)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define STACK_WATCH_AREA_SIZE 32
|
||||||
void vPortSetStackWatchpoint(void *pxStackStart)
|
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)
|
BaseType_t xPortInIsrContext(void)
|
||||||
|
@@ -107,14 +107,15 @@ static inline void cpu_ll_set_watchpoint(int id,
|
|||||||
{
|
{
|
||||||
uint32_t addr_napot;
|
uint32_t addr_napot;
|
||||||
RV_WRITE_CSR(tselect,id);
|
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(CSR_TDATA1, TDATA1_USER|TDATA1_MACHINE);
|
||||||
RV_SET_CSR_FIELD(CSR_TDATA1, TDATA1_MATCH, 1);
|
RV_SET_CSR_FIELD(CSR_TDATA1, TDATA1_MATCH, 1);
|
||||||
addr_napot = ((uint32_t)addr)|((size>>1)-1);
|
// add 0 in napot encoding
|
||||||
if(on_read) {
|
addr_napot = ((uint32_t) addr) | ((size >> 1) - 1);
|
||||||
|
if (on_read) {
|
||||||
RV_SET_CSR(CSR_TDATA1, TDATA1_LOAD);
|
RV_SET_CSR(CSR_TDATA1, TDATA1_LOAD);
|
||||||
}
|
}
|
||||||
if(on_write) {
|
if (on_write) {
|
||||||
RV_SET_CSR(CSR_TDATA1, TDATA1_STORE);
|
RV_SET_CSR(CSR_TDATA1, TDATA1_STORE);
|
||||||
}
|
}
|
||||||
RV_WRITE_CSR(tdata2,addr_napot);
|
RV_WRITE_CSR(tdata2,addr_napot);
|
||||||
|
@@ -90,13 +90,14 @@ extern "C" {
|
|||||||
#define CSR_TDATA1 0x7a1
|
#define CSR_TDATA1 0x7a1
|
||||||
|
|
||||||
#define TCONTROL_MTE (1<<3) /*R/W, Current M mode trigger enable bit*/
|
#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_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_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_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_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_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_V (0xF) /*R/W,Address match type :0 : Exact byte match 1 : NAPOT range match */
|
||||||
#define TDATA1_MATCH_S (7)
|
#define TDATA1_MATCH_S (7)
|
||||||
|
|
||||||
|
@@ -100,19 +100,39 @@ 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)
|
static void test_stack_overflow(void)
|
||||||
{
|
{
|
||||||
register uint32_t* sp asm("sp");
|
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.
|
// 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) {
|
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)
|
static void test_illegal_instruction(void)
|
||||||
{
|
{
|
||||||
|
#if __XTENSA__
|
||||||
__asm__ __volatile__("ill");
|
__asm__ __volatile__("ill");
|
||||||
|
#elif __riscv
|
||||||
|
__asm__ __volatile__("unimp");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_instr_fetch_prohibited(void)
|
static void test_instr_fetch_prohibited(void)
|
||||||
|
@@ -10,3 +10,6 @@ CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
|
|||||||
|
|
||||||
# To panic on task WDT
|
# To panic on task WDT
|
||||||
CONFIG_ESP_TASK_WDT_PANIC=y
|
CONFIG_ESP_TASK_WDT_PANIC=y
|
||||||
|
|
||||||
|
# For vTaskGetInfo() used in test_stack_overflow()
|
||||||
|
CONFIG_FREERTOS_USE_TRACE_FACILITY=y
|
||||||
|
Reference in New Issue
Block a user