system: stack watchpoint support on C3

Closes IDF-2307
pull/6416/head
Jakob Hasse 2020-12-03 14:28:06 +08:00
rodzic 9827adaa09
commit b51889dccb
6 zmienionych plików z 44 dodań i 14 usunięć

Wyświetl plik

@ -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.

Wyświetl plik

@ -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)

Wyświetl plik

@ -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);

Wyświetl plik

@ -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)

Wyświetl plik

@ -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)

Wyświetl plik

@ -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