esp32: Add core dump saving to flash feature

Complimentary changes:
1) Partition table definitions files with core dump partition
2) Special sub-type for core dump partition
3) Special version of spi_flash_xxx
4) espcoredump.py is script to get core dump from flash and print useful info
5) FreeRTOS API was extended to get tasks snapshots
This commit is contained in:
Alexey Gerenkov 2016-12-22 02:56:23 +03:00
parent 5fbea86a9e
commit 4a3e160888
19 changed files with 1715 additions and 76 deletions

View file

@ -54,6 +54,24 @@ config TRACEMEM_RESERVE_DRAM
default 0x4000 if MEMMAP_TRACEMEM && !MEMMAP_TRACEMEM_TWOBANKS
default 0x0
choice ESP32_COREDUMP_TO_FLASH_OR_UART
prompt "Core dump destination"
default ESP32_ENABLE_COREDUMP_TO_NONE
help
Select place to store core dump: flash, uart or none (to disable core dumps generation).
If core dump is configured to be stored in flash and custom partition table is used add
corresponding entry to your CSV. For examples, please see predefined partition table CSV descriptions
in the components/partition_table directory.
config ESP32_ENABLE_COREDUMP_TO_FLASH
bool "Flash"
config ESP32_ENABLE_COREDUMP_TO_UART
bool "UART"
config ESP32_ENABLE_COREDUMP_TO_NONE
bool "None"
endchoice
# Not implemented and/or needs new silicon rev to work
config MEMMAP_SPISRAM
bool "Use external SPI SRAM chip as main memory"

View file

@ -0,0 +1,276 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
//#include "esp_attr.h"
#include "esp_panic.h"
#include "esp_partition.h"
#ifdef ESP_PLATFORM
// Uncomment this line to force output from this module
#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#include "esp_log.h"
static const char* TAG = "esp_core_dump_init";
#else
#define ESP_LOGD(...)
#endif
// TODO: allow user to set this in menuconfig or get tasks iteratively
#define COREDUMP_MAX_TASKS_NUM 32
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
// magic numbers to control core dump data consistency
#define COREDUMP_FLASH_MAGIC_START 0xDEADBEEFUL
#define COREDUMP_FLASH_MAGIC_END 0xACDCFEEDUL
// core dump partition start
static uint32_t s_core_part_start;
// core dump partition size
static uint32_t s_core_part_size;
static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint32_t data_size)
{
esp_err_t err;
uint32_t data_len = 0, k, len;
union
{
uint8_t data8[4];
uint32_t data32;
} rom_data;
data_len = (data_size / sizeof(uint32_t)) * sizeof(uint32_t);
err = spi_flash_write_panic(off, data, data_len);
if (err != ESP_OK) {
esp_panicPutStr("ERROR: Failed to write data");
esp_panicPutHex(err);
esp_panicPutStr("!\r\n");
return 0;
}
len = data_size % sizeof(uint32_t);
if (len) {
// write last bytes with padding, actual TCB len can be retrieved by esptool from core dump header
rom_data.data32 = 0;
for (k = 0; k < len; k++)
rom_data.data8[k] = *(data + data_len + k);
err = spi_flash_write_panic(off + data_len, &rom_data, sizeof(uint32_t));
if (err != ESP_OK) {
esp_panicPutStr("ERROR: Failed to write data end");
esp_panicPutHex(err);
esp_panicPutStr("!\r\n");
return 0;
}
data_len += sizeof(uint32_t);
}
return data_len;
}
/*
* | MAGIC1 |
* | TOTAL_LEN | TASKS_NUM | TCB_SIZE |
* | TCB_ADDR_1 | STACK_TOP_1 | STACK_END_1 | TCB_1 | STACK_1 |
* . . . .
* . . . .
* | TCB_ADDR_N | STACK_TOP_N | STACK_END_N | TCB_N | STACK_N |
* | MAGIC2 |
*/
void esp_core_dump_to_flash(XtExcFrame *frame)
{
union
{
uint8_t data8[16];
uint32_t data32[4];
} rom_data;
//const esp_partition_t *core_part;
esp_err_t err;
TaskSnapshot_t tasks[COREDUMP_MAX_TASKS_NUM];
UBaseType_t tcb_sz, task_num;
uint32_t data_len = 0, i, len, sec_num;
size_t off;
esp_panicPutStr("Save core dump to flash...\r\n");
task_num = uxTaskGetSnapshotAll(tasks, COREDUMP_MAX_TASKS_NUM, &tcb_sz);
// take TCB padding into account, actual TCB size will be stored in header
if (tcb_sz % sizeof(uint32_t))
len = (tcb_sz / sizeof(uint32_t) + 1) * sizeof(uint32_t);
else
len = tcb_sz;
// header + magic2 + tasknum*(tcb + stack start/end + tcb addr)
data_len = 5*sizeof(uint32_t) + task_num*(len + 2*sizeof(uint32_t) + sizeof(uint32_t *));
for (i = 0; i < task_num; i++) {
if (tasks[i].pxTCB == xTaskGetCurrentTaskHandle()) {
// set correct stack top for current task
tasks[i].pxTopOfStack = (StackType_t *)frame;
esp_panicPutStr("Current task PC/A0/SP ");
esp_panicPutHex(frame->pc);
esp_panicPutStr(" ");
esp_panicPutHex(frame->a0);
esp_panicPutStr(" ");
esp_panicPutHex(frame->a1);
esp_panicPutStr("\r\n");
}
#if( portSTACK_GROWTH < 0 )
len = (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack;
#else
len = (uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack;
#endif
esp_panicPutStr("stack len = ");
esp_panicPutHex(len);
esp_panicPutStr(" ");
esp_panicPutHex((int)tasks[i].pxTopOfStack);
esp_panicPutStr(" ");
esp_panicPutHex((int)tasks[i].pxEndOfStack);
esp_panicPutStr("\r\n");
// take stack padding into account
if (len % sizeof(uint32_t))
len = (len / sizeof(uint32_t) + 1) * sizeof(uint32_t);
data_len += len;
}
esp_panicPutStr("Core dump len =");
esp_panicPutHex(data_len);
esp_panicPutStr("\r\n");
if (data_len > s_core_part_size) {
esp_panicPutStr("ERROR: Not enough space to save core dump!");
return;
}
// TEST READ START
err = spi_flash_read_panic(s_core_part_start + 0, &rom_data, sizeof(rom_data));
if (err != ESP_OK) {
esp_panicPutStr("ERROR: Failed to read flash ");
esp_panicPutHex(err);
esp_panicPutStr("!\r\n");
return;
}
else {
esp_panicPutStr("Data from flash:\r\n");
for (i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) {
esp_panicPutHex(rom_data.data32[i]);
esp_panicPutStr("\r\n");
}
// rom_data[4] = 0;
// esp_panicPutStr(rom_data);
// esp_panicPutStr("\r\n");
}
// TEST READ END
sec_num = data_len / SPI_FLASH_SEC_SIZE;
if (data_len % SPI_FLASH_SEC_SIZE)
sec_num++;
err = spi_flash_erase_range_panic(s_core_part_start + 0, sec_num * SPI_FLASH_SEC_SIZE);
if (err != ESP_OK) {
esp_panicPutStr("ERROR: Failed to erase flash ");
esp_panicPutHex(err);
esp_panicPutStr("!\r\n");
return;
}
rom_data.data32[0] = COREDUMP_FLASH_MAGIC_START;
rom_data.data32[1] = data_len;
rom_data.data32[2] = task_num;
rom_data.data32[3] = tcb_sz;
err = spi_flash_write_panic(s_core_part_start + 0, &rom_data, sizeof(rom_data));
if (err != ESP_OK) {
esp_panicPutStr("ERROR: Failed to write core dump header ");
esp_panicPutHex(err);
esp_panicPutStr("!\r\n");
return;
}
off = sizeof(rom_data);
for (i = 0; i < task_num; i++) {
esp_panicPutStr("Dump task ");
esp_panicPutHex((int)tasks[i].pxTCB);
esp_panicPutStr("\r\n");
// save TCB address, stack base and stack top addr
rom_data.data32[0] = (uint32_t)tasks[i].pxTCB;
rom_data.data32[1] = (uint32_t)tasks[i].pxTopOfStack;
rom_data.data32[2] = (uint32_t)tasks[i].pxEndOfStack;
err = spi_flash_write_panic(s_core_part_start + off, &rom_data, 3*sizeof(uint32_t));
if (err != ESP_OK) {
esp_panicPutStr("ERROR: Failed to write task header ");
esp_panicPutHex(err);
esp_panicPutStr("!\r\n");
return;
}
off += 3*sizeof(uint32_t);
// save TCB
len = esp_core_dump_write_flash_padded(s_core_part_start + off, tasks[i].pxTCB, tcb_sz);
if (len == 0)
return;
off += len;
// save task stack
/*int k;
for (k = 0; k < 8*4; k++) {
esp_panicPutStr("stack[");
esp_panicPutDec(k);
esp_panicPutStr("] = ");
esp_panicPutHex(((uint8_t *)tasks[i].pxTopOfStack)[k]);
esp_panicPutStr("\r\n");
}*/
len = esp_core_dump_write_flash_padded(s_core_part_start + off,
#if( portSTACK_GROWTH < 0 )
tasks[i].pxTopOfStack,
(uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack
#else
tasks[i].pxEndOfStack,
(uint32_t)tasks[i].pxTopOfStack - (uint32_t)tasks[i].pxEndOfStack
#endif
);
if (len == 0)
return;
off += len;
}
rom_data.data32[0] = COREDUMP_FLASH_MAGIC_END;
err = spi_flash_write_panic(s_core_part_start + off, &rom_data, sizeof(uint32_t));
if (err != ESP_OK) {
esp_panicPutStr("Failed to write to flash ");
esp_panicPutHex(err);
esp_panicPutStr("!\r\n");
return;
}
esp_panicPutStr("Core dump has been saved to flash partition.\r\n");
}
#endif
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART
void esp_core_dump_to_uart(XtExcFrame *frame)
{
}
#endif
void esp_core_dump_init()
{
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
const esp_partition_t *core_part;
core_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, NULL);
if (!core_part) {
ESP_LOGE(TAG, "No core dump partition found!");
return;
}
ESP_LOGI(TAG, "Found partition '%s' @ %x %d bytes", core_part->label, core_part->address, core_part->size);
s_core_part_start = core_part->address;
s_core_part_size = core_part->size;
#endif
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART
#endif
}

View file

@ -55,6 +55,7 @@
#include "esp_task_wdt.h"
#include "esp_phy_init.h"
#include "esp_coexist.h"
#include "esp_core_dump.h"
#include "trax.h"
#define STRINGIFY(s) STRINGIFY2(s)
@ -214,6 +215,10 @@ void start_cpu0_default(void)
}
#endif
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
esp_core_dump_init();
#endif
xTaskCreatePinnedToCore(&main_task, "main",
ESP_TASK_MAIN_STACK, NULL,
ESP_TASK_MAIN_PRIO, NULL, 0);

View file

@ -0,0 +1,21 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ESP_CORE_DUMP_H_
#define ESP_CORE_DUMP_H_
void esp_core_dump_init();
void esp_core_dump_to_flash();
void esp_core_dump_to_uart();
#endif

View file

@ -24,6 +24,10 @@
*/
void esp_set_breakpoint_if_jtag(void *fn);
void esp_panicPutchar(char c);
void esp_panicPutStr(const char *c);
void esp_panicPutHex(int a);
void esp_panicPutDec(int a);
#define ESP_WATCHPOINT_LOAD 0x40000000
#define ESP_WATCHPOINT_STORE 0x80000000

View file

@ -33,6 +33,7 @@
#include "esp_panic.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_core_dump.h"
/*
Panic handlers; these get called when an unhandled exception occurs or the assembly-level
@ -46,61 +47,61 @@
#if !CONFIG_ESP32_PANIC_SILENT_REBOOT
//printf may be broken, so we fix our own printing fns...
inline static void panicPutChar(char c)
void esp_panicPutChar(char c)
{
while (((READ_PERI_REG(UART_STATUS_REG(0)) >> UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT) >= 126) ;
WRITE_PERI_REG(UART_FIFO_REG(0), c);
}
inline static void panicPutStr(const char *c)
void esp_panicPutStr(const char *c)
{
int x = 0;
while (c[x] != 0) {
panicPutChar(c[x]);
esp_panicPutChar(c[x]);
x++;
}
}
inline static void panicPutHex(int a)
void esp_panicPutHex(int a)
{
int x;
int c;
for (x = 0; x < 8; x++) {
c = (a >> 28) & 0xf;
if (c < 10) {
panicPutChar('0' + c);
esp_panicPutChar('0' + c);
} else {
panicPutChar('a' + c - 10);
esp_panicPutChar('a' + c - 10);
}
a <<= 4;
}
}
inline static void panicPutDec(int a)
void esp_panicPutDec(int a)
{
int n1, n2;
n1 = a % 10;
n2 = a / 10;
if (n2 == 0) {
panicPutChar(' ');
esp_panicPutChar(' ');
} else {
panicPutChar(n2 + '0');
esp_panicPutChar(n2 + '0');
}
panicPutChar(n1 + '0');
esp_panicPutChar(n1 + '0');
}
#else
//No printing wanted. Stub out these functions.
inline static void panicPutChar(char c) { }
inline static void panicPutStr(const char *c) { }
inline static void panicPutHex(int a) { }
inline static void panicPutDec(int a) { }
void esp_panicPutChar(char c) { }
void esp_panicPutStr(const char *c) { }
void esp_panicPutHex(int a) { }
void esp_panicPutDec(int a) { }
#endif
void __attribute__((weak)) vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName )
{
panicPutStr("***ERROR*** A stack overflow in task ");
panicPutStr((char *)pcTaskName);
panicPutStr(" has been detected.\r\n");
esp_panicPutStr("***ERROR*** A stack overflow in task ");
esp_panicPutStr((char *)pcTaskName);
esp_panicPutStr(" has been detected.\r\n");
abort();
}
@ -161,39 +162,39 @@ void panicHandler(XtExcFrame *frame)
reason = reasons[regs[20]];
}
haltOtherCore();
panicPutStr("Guru Meditation Error: Core ");
panicPutDec(xPortGetCoreID());
panicPutStr(" panic'ed (");
esp_panicPutStr("Guru Meditation Error: Core ");
esp_panicPutDec(xPortGetCoreID());
esp_panicPutStr(" panic'ed (");
if (!abort_called) {
panicPutStr(reason);
panicPutStr(")\r\n");
esp_panicPutStr(reason);
esp_panicPutStr(")\r\n");
if (regs[20]==PANIC_RSN_DEBUGEXCEPTION) {
int debugRsn;
asm("rsr.debugcause %0":"=r"(debugRsn));
panicPutStr("Debug exception reason: ");
if (debugRsn&XCHAL_DEBUGCAUSE_ICOUNT_MASK) panicPutStr("SingleStep ");
if (debugRsn&XCHAL_DEBUGCAUSE_IBREAK_MASK) panicPutStr("HwBreakpoint ");
esp_panicPutStr("Debug exception reason: ");
if (debugRsn&XCHAL_DEBUGCAUSE_ICOUNT_MASK) esp_panicPutStr("SingleStep ");
if (debugRsn&XCHAL_DEBUGCAUSE_IBREAK_MASK) esp_panicPutStr("HwBreakpoint ");
if (debugRsn&XCHAL_DEBUGCAUSE_DBREAK_MASK) {
//Unlike what the ISA manual says, this core seemingly distinguishes from a DBREAK
//reason caused by watchdog 0 and one caused by watchdog 1 by setting bit 8 of the
//debugcause if the cause is watchdog 1 and clearing it if it's watchdog 0.
if (debugRsn&(1<<8)) {
#if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK
panicPutStr("Stack canary watchpoint triggered ");
esp_panicPutStr("Stack canary watchpoint triggered ");
#else
panicPutStr("Watchpoint 1 triggered ");
esp_panicPutStr("Watchpoint 1 triggered ");
#endif
} else {
panicPutStr("Watchpoint 0 triggered ");
esp_panicPutStr("Watchpoint 0 triggered ");
}
}
if (debugRsn&XCHAL_DEBUGCAUSE_BREAK_MASK) panicPutStr("BREAK instr ");
if (debugRsn&XCHAL_DEBUGCAUSE_BREAKN_MASK) panicPutStr("BREAKN instr ");
if (debugRsn&XCHAL_DEBUGCAUSE_DEBUGINT_MASK) panicPutStr("DebugIntr ");
panicPutStr("\r\n");
if (debugRsn&XCHAL_DEBUGCAUSE_BREAK_MASK) esp_panicPutStr("BREAK instr ");
if (debugRsn&XCHAL_DEBUGCAUSE_BREAKN_MASK) esp_panicPutStr("BREAKN instr ");
if (debugRsn&XCHAL_DEBUGCAUSE_DEBUGINT_MASK) esp_panicPutStr("DebugIntr ");
esp_panicPutStr("\r\n");
}
} else {
panicPutStr("abort)\r\n");
esp_panicPutStr("abort)\r\n");
}
if (esp_cpu_in_ocd_debug_mode()) {
@ -219,25 +220,25 @@ void xt_unhandled_exception(XtExcFrame *frame)
int x;
haltOtherCore();
panicPutStr("Guru Meditation Error of type ");
esp_panicPutStr("Guru Meditation Error of type ");
x = regs[20];
if (x < 40) {
panicPutStr(edesc[x]);
esp_panicPutStr(edesc[x]);
} else {
panicPutStr("Unknown");
esp_panicPutStr("Unknown");
}
panicPutStr(" occurred on core ");
panicPutDec(xPortGetCoreID());
esp_panicPutStr(" occurred on core ");
esp_panicPutDec(xPortGetCoreID());
if (esp_cpu_in_ocd_debug_mode()) {
panicPutStr(" at pc=");
panicPutHex(regs[1]);
panicPutStr(". Setting bp and returning..\r\n");
esp_panicPutStr(" at pc=");
esp_panicPutHex(regs[1]);
esp_panicPutStr(". Setting bp and returning..\r\n");
//Stick a hardware breakpoint on the address the handler returns to. This way, the OCD debugger
//will kick in exactly at the context the error happened.
setFirstBreakpoint(regs[1]);
return;
}
panicPutStr(". Exception was unhandled.\r\n");
esp_panicPutStr(". Exception was unhandled.\r\n");
commonErrorHandler(frame);
}
@ -292,16 +293,16 @@ static void putEntry(uint32_t pc, uint32_t sp)
if (pc & 0x80000000) {
pc = (pc & 0x3fffffff) | 0x40000000;
}
panicPutStr(" 0x");
panicPutHex(pc);
panicPutStr(":0x");
panicPutHex(sp);
esp_panicPutStr(" 0x");
esp_panicPutHex(pc);
esp_panicPutStr(":0x");
esp_panicPutHex(sp);
}
static void doBacktrace(XtExcFrame *frame)
{
uint32_t i = 0, pc = frame->pc, sp = frame->a1;
panicPutStr("\nBacktrace:");
esp_panicPutStr("\r\nBacktrace:");
/* Do not check sanity on first entry, PC could be smashed. */
putEntry(pc, sp);
pc = frame->a0;
@ -317,7 +318,7 @@ static void doBacktrace(XtExcFrame *frame)
break;
}
}
panicPutStr("\n\n");
esp_panicPutStr("\r\n\r\n");
}
/*
@ -341,18 +342,18 @@ static void commonErrorHandler(XtExcFrame *frame)
the register window is no longer useful.
*/
if (!abort_called) {
panicPutStr("Register dump:\r\n");
esp_panicPutStr("Register dump:\r\n");
for (x = 0; x < 24; x += 4) {
for (y = 0; y < 4; y++) {
if (sdesc[x + y][0] != 0) {
panicPutStr(sdesc[x + y]);
panicPutStr(": 0x");
panicPutHex(regs[x + y + 1]);
panicPutStr(" ");
esp_panicPutStr(sdesc[x + y]);
esp_panicPutStr(": 0x");
esp_panicPutHex(regs[x + y + 1]);
esp_panicPutStr(" ");
}
esp_panicPutStr("\r\n");
}
panicPutStr("\r\n");
}
}
@ -361,19 +362,27 @@ static void commonErrorHandler(XtExcFrame *frame)
#if CONFIG_ESP32_PANIC_GDBSTUB
disableAllWdts();
panicPutStr("Entering gdb stub now.\r\n");
esp_panicPutStr("Entering gdb stub now.\r\n");
esp_gdbstub_panic_handler(frame);
#elif CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT
panicPutStr("Rebooting...\r\n");
#else
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
esp_core_dump_to_flash(frame);
#endif
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART && !CONFIG_ESP32_PANIC_SILENT_REBOOT
esp_core_dump_to_uart(frame);
#endif
#if CONFIG_ESP32_PANIC_PRINT_REBOOT || CONFIG_ESP32_PANIC_SILENT_REBOOT
esp_panicPutStr("Rebooting...\r\n");
for (x = 0; x < 100; x++) {
ets_delay_us(1000);
}
software_reset();
#else
disableAllWdts();
panicPutStr("CPU halted.\r\n");
esp_panicPutStr("CPU halted.\r\n");
while (1);
#endif
#endif
}

File diff suppressed because it is too large Load diff

View file

@ -863,8 +863,8 @@ typedef struct xSTATIC_TCB
void *pxDummy6;
uint8_t ucDummy7[ configMAX_TASK_NAME_LEN ];
UBaseType_t uxDummyCoreId;
#if ( portSTACK_GROWTH > 0 )
void *pxDummy8;
#if ( portSTACK_GROWTH > 0 || configENABLE_TASK_SNAPSHOT == 1 )
void *pxDummy8;
#endif
#if ( portCRITICAL_NESTING_IN_TCB == 1 )
UBaseType_t uxDummy9;

View file

@ -268,7 +268,9 @@
#define configXT_BOARD 1 /* Board mode */
#define configXT_SIMULATOR 0
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH | CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
#define configENABLE_TASK_SNAPSHOT 1
#endif
#endif /* FREERTOS_CONFIG_H */

View file

@ -181,6 +181,18 @@ typedef struct xTASK_STATUS
uint16_t usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */
} TaskStatus_t;
/*
* Used with the uxTaskGetSnapshotAll() function to save memory snapshot of each task in the system.
* We need this struct because TCB_t is defined (hidden) in tasks.c.
*/
typedef struct xTASK_SNAPSHOT
{
void *pxTCB; /* Address of task control block. */
StackType_t *pxTopOfStack; /* Points to the location of the last item placed on the tasks stack. */
StackType_t *pxEndOfStack; /* Points to the end of the stack. pxTopOfStack < pxEndOfStack, stack grows hi2lo
pxTopOfStack > pxEndOfStack, stack grows lo2hi*/
} TaskSnapshot_t;
/* Possible return values for eTaskConfirmSleepModeStatus(). */
typedef enum
{
@ -2173,6 +2185,9 @@ eSleepModeStatus eTaskConfirmSleepModeStatus( void ) PRIVILEGED_FUNCTION;
*/
void *pvTaskIncrementMutexHeldCount( void );
/* Used by core dump facility to get list of task handles. */
UBaseType_t uxTaskGetSnapshotAll( TaskSnapshot_t * const pxTaskSnapshotArray, const UBaseType_t uxArraySize, UBaseType_t * const pxTcbSz );
#ifdef __cplusplus
}
#endif

View file

@ -181,7 +181,7 @@ typedef struct tskTaskControlBlock
char pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created. Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
BaseType_t xCoreID; /*< Core this task is pinned to */
/* If this moves around (other than pcTaskName size changes), please change the define in xtensa_vectors.S as well. */
#if ( portSTACK_GROWTH > 0 )
#if ( portSTACK_GROWTH > 0 || configENABLE_TASK_SNAPSHOT == 1 )
StackType_t *pxEndOfStack; /*< Points to the end of the stack on architectures where the stack grows up from low memory. */
#endif
@ -885,6 +885,12 @@ UBaseType_t x;
/* Check the alignment of the calculated top of stack is correct. */
configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
#if ( configENABLE_TASK_SNAPSHOT == 1 )
{
/* need stack end for core dumps */
pxNewTCB->pxEndOfStack = pxTopOfStack;
}
#endif
}
#else /* portSTACK_GROWTH */
{
@ -4912,6 +4918,102 @@ TickType_t uxReturn;
#endif /* configUSE_TASK_NOTIFICATIONS */
#if ( configENABLE_TASK_SNAPSHOT == 1 )
static void prvTaskGetSnapshotsFromList( TaskSnapshot_t *pxTaskSnapshotArray, UBaseType_t *uxTask, const UBaseType_t uxArraySize, List_t *pxList )
{
TCB_t *pxNextTCB, *pxFirstTCB;
if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 )
{
listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList );
do
{
listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList );
if( *uxTask >= uxArraySize )
break;
pxTaskSnapshotArray[ *uxTask ].pxTCB = pxNextTCB;
pxTaskSnapshotArray[ *uxTask ].pxTopOfStack = (StackType_t *)pxNextTCB->pxTopOfStack;
#if( portSTACK_GROWTH < 0 )
{
pxTaskSnapshotArray[ *uxTask ].pxEndOfStack = pxNextTCB->pxEndOfStack;
}
#else
{
pxTaskSnapshotArray[ *uxTask ].pxEndOfStack = pxNextTCB->pxStack;
}
#endif
(*uxTask)++;
} while( pxNextTCB != pxFirstTCB );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
UBaseType_t uxTaskGetSnapshotAll( TaskSnapshot_t * const pxTaskSnapshotArray, const UBaseType_t uxArraySize, UBaseType_t * const pxTcbSz )
{
UBaseType_t uxTask = 0, i = 0;
*pxTcbSz = sizeof(TCB_t);
//vTaskSuspendAll(); //WARNING: This only suspends one CPU. ToDo: suspend others as well. Mux using taskQueueMutex maybe?
{
/* Fill in an TaskStatus_t structure with information on each
task in the Ready state. */
i = configMAX_PRIORITIES;
do
{
i--;
prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, &( pxReadyTasksLists[ i ] ) );
} while( i > ( UBaseType_t ) tskIDLE_PRIORITY ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
/* Fill in an TaskStatus_t structure with information on each
task in the Blocked state. */
prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, ( List_t * ) pxDelayedTaskList );
prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, ( List_t * ) pxOverflowDelayedTaskList );
#if( INCLUDE_vTaskDelete == 1 )
{
prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, &xTasksWaitingTermination );
}
#endif
#if ( INCLUDE_vTaskSuspend == 1 )
{
prvTaskGetSnapshotsFromList( pxTaskSnapshotArray, &uxTask, uxArraySize, &xSuspendedTaskList );
}
#endif
}
//( void ) xTaskResumeAll();
#if 0
/* Convention: First num_cpus slots will have current task for that cpu. */
for (i = 0; i < portNUM_PROCESSORS; i++) {
if (pxCurrentTCB[i] == NULL || pxCurrentTCB == pxTaskSnapshotArray[i]) {
continue;
} else {
UBaseType_t j;
for (j = i; j < uxTask; j++) {
if (pxTaskSnapshotArray[j] == pxCurrentTCB[i]) {
TaskHandle_t tmp = pxTaskSnapshotArray[i];
pxTaskSnapshotArray[i] = pxTaskSnapshotArray[j];
pxTaskSnapshotArray[j] = tmp;
break;
}
}
}
}
#endif
return uxTask;
}
#endif
#ifdef FREERTOS_MODULE_TEST
#include "tasks_test_access_functions.h"
#endif

View file

@ -45,8 +45,10 @@ config PARTITION_TABLE_CUSTOM_PHY_DATA_OFFSET
config PARTITION_TABLE_FILENAME
string
default partitions_singleapp.csv if PARTITION_TABLE_SINGLE_APP
default partitions_two_ota.csv if PARTITION_TABLE_TWO_OTA
default partitions_singleapp.csv if PARTITION_TABLE_SINGLE_APP && !ESP32_ENABLE_COREDUMP_TO_FLASH
default partitions_singleapp_coredump.csv if PARTITION_TABLE_SINGLE_APP && ESP32_ENABLE_COREDUMP_TO_FLASH
default partitions_two_ota.csv if PARTITION_TABLE_TWO_OTA && !ESP32_ENABLE_COREDUMP_TO_FLASH
default partitions_two_ota_coredump.csv if PARTITION_TABLE_TWO_OTA && ESP32_ENABLE_COREDUMP_TO_FLASH
default PARTITION_TABLE_CUSTOM_FILENAME if PARTITION_TABLE_CUSTOM
config APP_OFFSET

View file

@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
nvs, data, nvs, 0x9000, 0x6000
phy_init, data, phy, 0xf000, 0x1000
factory, app, factory, 0x10000, 1M
coredump, data, 3, , 64K
1 # Name, Type, SubType, Offset, Size
2 # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
3 nvs, data, nvs, 0x9000, 0x6000
4 phy_init, data, phy, 0xf000, 0x1000
5 factory, app, factory, 0x10000, 1M
6 coredump, data, 3, , 64K

View file

@ -0,0 +1,9 @@
# Name, Type, SubType, Offset, Size
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
nvs, data, nvs, 0x9000, 0x4000
otadata, data, ota, 0xd000, 0x2000
phy_init, data, phy, 0xf000, 0x1000
factory, 0, 0, 0x10000, 1M
coredump, data, 3, , 64K
ota_0, 0, ota_0, , 1M
ota_1, 0, ota_1, , 1M
1 # Name, Type, SubType, Offset, Size
2 # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
3 nvs, data, nvs, 0x9000, 0x4000
4 otadata, data, ota, 0xd000, 0x2000
5 phy_init, data, phy, 0xf000, 0x1000
6 factory, 0, 0, 0x10000, 1M
7 coredump, data, 3, , 64K
8 ota_0, 0, ota_0, , 1M
9 ota_1, 0, ota_1, , 1M

View file

@ -141,6 +141,29 @@ void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu()
esp_intr_noniram_enable();
}
void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu_panic()
{
const uint32_t cpuid = xPortGetCoreID();
const uint32_t other_cpuid = (cpuid == 0) ? 1 : 0;
// do not care about other CPU, it was halted upon entering panic handler
spi_flash_disable_cache(other_cpuid, &s_flash_op_cache_state[other_cpuid]);
// Kill interrupts that aren't located in IRAM
esp_intr_noniram_disable();
// Disable cache on this CPU as well
spi_flash_disable_cache(cpuid, &s_flash_op_cache_state[cpuid]);
}
void IRAM_ATTR spi_flash_enable_interrupts_caches_panic()
{
const uint32_t cpuid = xPortGetCoreID();
// Re-enable cache on this CPU
spi_flash_restore_cache(cpuid, s_flash_op_cache_state[cpuid]);
// Re-enable non-iram interrupts
esp_intr_noniram_enable();
}
#else // CONFIG_FREERTOS_UNICORE
void spi_flash_init_lock()
@ -172,6 +195,22 @@ void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu()
esp_intr_noniram_enable();
}
void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu_panic()
{
// Kill interrupts that aren't located in IRAM
esp_intr_noniram_disable();
// Disable cache on this CPU as well
spi_flash_disable_cache(0, &s_flash_op_cache_state[0]);
}
void IRAM_ATTR spi_flash_enable_interrupts_caches_panic()
{
// Re-enable cache on this CPU
spi_flash_restore_cache(0, s_flash_op_cache_state[0]);
// Re-enable non-iram interrupts
esp_intr_noniram_enable();
}
#endif // CONFIG_FREERTOS_UNICORE
/**

View file

@ -40,5 +40,14 @@ void spi_flash_disable_interrupts_caches_and_other_cpu();
// Enable cache, enable interrupts (to be added in future), resume scheduler
void spi_flash_enable_interrupts_caches_and_other_cpu();
// Disables non-IRAM interrupt handlers on current CPU and caches on both CPUs.
// This function is implied to be called from panic handler
// when non-current CPU is halted and can not execute code from flash.
void spi_flash_disable_interrupts_caches_and_other_cpu_panic();
// Enable cache, enable interrupts (to be added in future) on current CPU.
// This function is implied to be called from panic handler
// when non-current CPU is halted and can not execute code from flash.
void spi_flash_enable_interrupts_caches_panic();
#endif //ESP_SPI_FLASH_CACHE_UTILS_H

View file

@ -58,7 +58,35 @@ static spi_flash_counters_t s_flash_stats;
#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS
/* SPI flash access critical section management functions */
typedef void (*spi_flash_guard_start_func_t)(void);
typedef void (*spi_flash_guard_end_func_t)(void);
/**
* Structure holding SPI flash access critical section management functions
*/
typedef struct {
spi_flash_guard_start_func_t start; // critical section start func
spi_flash_guard_end_func_t end; // critical section end func
} spi_flash_guard_funcs_t;
#define FLASH_GUARD_START(_gp_) do{if((_gp_)) (_gp_)->start();}while(0)
#define FLASH_GUARD_END(_gp_) do{if((_gp_)) (_gp_)->end();}while(0)
static esp_err_t spi_flash_translate_rc(SpiFlashOpResult rc);
static esp_err_t spi_flash_erase_range_internal(uint32_t start_addr, uint32_t size, const spi_flash_guard_funcs_t *flash_guard);
static esp_err_t spi_flash_write_internal(size_t dst, const void *srcv, size_t size, const spi_flash_guard_funcs_t *flash_guard);
static esp_err_t spi_flash_read_internal(size_t src, void *dstv, size_t size, const spi_flash_guard_funcs_t *flash_guard);
const DRAM_ATTR spi_flash_guard_funcs_t s_flash_guard_ops = {
.start = spi_flash_disable_interrupts_caches_and_other_cpu,
.end = spi_flash_enable_interrupts_caches_and_other_cpu
};
const DRAM_ATTR spi_flash_guard_funcs_t s_flash_guard_panic_ops = {
.start = spi_flash_disable_interrupts_caches_and_other_cpu_panic,
.end = spi_flash_enable_interrupts_caches_panic
};
void spi_flash_init()
{
@ -92,6 +120,16 @@ esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sec)
}
esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)
{
return spi_flash_erase_range_internal(start_addr, size, &s_flash_guard_ops);
}
esp_err_t IRAM_ATTR spi_flash_erase_range_panic(uint32_t start_addr, uint32_t size)
{
return spi_flash_erase_range_internal(start_addr, size, &s_flash_guard_panic_ops);
}
static esp_err_t IRAM_ATTR spi_flash_erase_range_internal(uint32_t start_addr, uint32_t size, const spi_flash_guard_funcs_t *flash_guard)
{
if (start_addr % SPI_FLASH_SEC_SIZE != 0) {
return ESP_ERR_INVALID_ARG;
@ -106,7 +144,7 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)
size_t end = start + size / SPI_FLASH_SEC_SIZE;
const size_t sectors_per_block = BLOCK_ERASE_SIZE / SPI_FLASH_SEC_SIZE;
COUNTER_START();
spi_flash_disable_interrupts_caches_and_other_cpu();
FLASH_GUARD_START(flash_guard);
SpiFlashOpResult rc;
rc = spi_flash_unlock();
if (rc == SPI_FLASH_RESULT_OK) {
@ -122,12 +160,22 @@ esp_err_t IRAM_ATTR spi_flash_erase_range(uint32_t start_addr, uint32_t size)
}
}
}
spi_flash_enable_interrupts_caches_and_other_cpu();
FLASH_GUARD_END(flash_guard);
COUNTER_STOP(erase);
return spi_flash_translate_rc(rc);
}
esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
{
return spi_flash_write_internal(dst, srcv, size, &s_flash_guard_ops);
}
esp_err_t IRAM_ATTR spi_flash_write_panic(size_t dst, const void *srcv, size_t size)
{
return spi_flash_write_internal(dst, srcv, size, &s_flash_guard_panic_ops);
}
static esp_err_t IRAM_ATTR spi_flash_write_internal(size_t dst, const void *srcv, size_t size, const spi_flash_guard_funcs_t *flash_guard)
{
// Out of bound writes are checked in ROM code, but we can give better
// error code here
@ -160,9 +208,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
if (left_size > 0) {
uint32_t t = 0xffffffff;
memcpy(((uint8_t *) &t) + (dst - left_off), srcc, left_size);
spi_flash_disable_interrupts_caches_and_other_cpu();
FLASH_GUARD_START(flash_guard);
rc = SPIWrite(left_off, &t, 4);
spi_flash_enable_interrupts_caches_and_other_cpu();
FLASH_GUARD_END(flash_guard);
if (rc != SPI_FLASH_RESULT_OK) {
goto out;
}
@ -178,9 +226,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
bool in_dram = true;
#endif
if (in_dram && (((uintptr_t) srcc) + mid_off) % 4 == 0) {
spi_flash_disable_interrupts_caches_and_other_cpu();
FLASH_GUARD_START(flash_guard);
rc = SPIWrite(dst + mid_off, (const uint32_t *) (srcc + mid_off), mid_size);
spi_flash_enable_interrupts_caches_and_other_cpu();
FLASH_GUARD_END(flash_guard);
if (rc != SPI_FLASH_RESULT_OK) {
goto out;
}
@ -194,9 +242,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
uint32_t t[8];
uint32_t write_size = MIN(mid_size, sizeof(t));
memcpy(t, srcc + mid_off, write_size);
spi_flash_disable_interrupts_caches_and_other_cpu();
FLASH_GUARD_START(flash_guard);
rc = SPIWrite(dst + mid_off, t, write_size);
spi_flash_enable_interrupts_caches_and_other_cpu();
FLASH_GUARD_END(flash_guard);
if (rc != SPI_FLASH_RESULT_OK) {
goto out;
}
@ -209,9 +257,9 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dst, const void *srcv, size_t size)
if (right_size > 0) {
uint32_t t = 0xffffffff;
memcpy(&t, srcc + right_off, right_size);
spi_flash_disable_interrupts_caches_and_other_cpu();
FLASH_GUARD_START(flash_guard);
rc = SPIWrite(dst + right_off, &t, 4);
spi_flash_enable_interrupts_caches_and_other_cpu();
FLASH_GUARD_END(flash_guard);
if (rc != SPI_FLASH_RESULT_OK) {
goto out;
}
@ -259,6 +307,16 @@ esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src,
}
esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size)
{
return spi_flash_read_internal(src, dstv, size, &s_flash_guard_ops);
}
esp_err_t IRAM_ATTR spi_flash_read_panic(size_t src, void *dstv, size_t size)
{
return spi_flash_read_internal(src, dstv, size, &s_flash_guard_panic_ops);
}
static esp_err_t IRAM_ATTR spi_flash_read_internal(size_t src, void *dstv, size_t size, const spi_flash_guard_funcs_t *flash_guard)
{
// Out of bound reads are checked in ROM code, but we can give better
// error code here
@ -271,7 +329,7 @@ esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size)
SpiFlashOpResult rc = SPI_FLASH_RESULT_OK;
COUNTER_START();
spi_flash_disable_interrupts_caches_and_other_cpu();
FLASH_GUARD_START(flash_guard);
/* To simplify boundary checks below, we handle small reads separately. */
if (size < 16) {
uint32_t t[6]; /* Enough for 16 bytes + 4 on either side for padding. */
@ -345,7 +403,7 @@ esp_err_t IRAM_ATTR spi_flash_read(size_t src, void *dstv, size_t size)
memcpy(dstc + pad_right_off, t, pad_right_size);
}
out:
spi_flash_enable_interrupts_caches_and_other_cpu();
FLASH_GUARD_END(flash_guard);
COUNTER_STOP(read);
return spi_flash_translate_rc(rc);
}

View file

@ -69,6 +69,7 @@ typedef enum {
ESP_PARTITION_SUBTYPE_DATA_OTA = 0x00, //!< OTA selection partition
ESP_PARTITION_SUBTYPE_DATA_PHY = 0x01, //!< PHY init data partition
ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02, //!< NVS partition
ESP_PARTITION_SUBTYPE_DATA_COREDUMP = 0x03, //!< COREDUMP partition
ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD = 0x80, //!< ESPHTTPD partition
ESP_PARTITION_SUBTYPE_DATA_FAT = 0x81, //!< FAT partition

View file

@ -173,6 +173,56 @@ void spi_flash_munmap(spi_flash_mmap_handle_t handle);
*/
void spi_flash_mmap_dump();
/**
* @brief Erase a range of flash sectors.
*
* @note This version of function is to be called from panic handler.
* It does not use any OS primitives and IPC and implies that
* only calling CPU is active.
*
* @param start_address Address where erase operation has to start.
* Must be 4kB-aligned
* @param size Size of erased range, in bytes. Must be divisible by 4kB.
*
* @return esp_err_t
*/
esp_err_t spi_flash_erase_range_panic(size_t start_address, size_t size);
/**
* @brief Write data to Flash.
*
* @note This version of function is to be called from panic handler.
* It does not use any OS primitives and IPC and implies that
* only calling CPU is active.
* @note If source address is in DROM, this function will return
* ESP_ERR_INVALID_ARG.
*
* @param dest destination address in Flash. Must be a multiple of 4 bytes.
* @param src pointer to the source buffer.
* @param size length of data, in bytes. Must be a multiple of 4 bytes.
*
* @return esp_err_t
*/
esp_err_t spi_flash_write_panic(size_t dest, const void *src, size_t size);
/**
* @brief Read data from Flash.
*
* @note This version of function is to be called from panic handler.
* It does not use any OS primitives and IPC and implies that
* only calling CPU is active.
*
* @param src source address of the data in Flash.
* @param dest pointer to the destination buffer
* @param size length of data
*
* @return esp_err_t
*/
esp_err_t spi_flash_read_panic(size_t src, void *dest, size_t size);
#if CONFIG_SPI_FLASH_ENABLE_COUNTERS
/**