Merge branch 'feature/core_dump_crc' into 'master'

Core Dump CRC

See merge request idf/esp-idf!1290
This commit is contained in:
Ivan Grokhotkov 2018-11-15 15:05:43 +08:00
commit fb56ce7d5c
13 changed files with 1463 additions and 1252 deletions

View file

@ -272,8 +272,10 @@ config TRACEMEM_RESERVE_DRAM
default 0x4000 if MEMMAP_TRACEMEM && !MEMMAP_TRACEMEM_TWOBANKS
default 0x0
menu "Core dump"
choice ESP32_COREDUMP_TO_FLASH_OR_UART
prompt "Core dump destination"
prompt "Data destination"
default ESP32_ENABLE_COREDUMP_TO_NONE
help
Select place to store core dump: flash, uart or none (to disable core dumps generation).
@ -298,20 +300,22 @@ config ESP32_ENABLE_COREDUMP
help
Enables/disable core dump module.
config ESP32_CORE_DUMP_MAX_TASKS_NUM
int "Maximum number of tasks"
depends on ESP32_ENABLE_COREDUMP
default 64
help
Maximum number of tasks snapshots in core dump.
config ESP32_CORE_DUMP_UART_DELAY
int "Core dump print to UART delay"
int "Delay before print to UART"
depends on ESP32_ENABLE_COREDUMP_TO_UART
default 0
help
Config delay (in ms) before printing core dump to UART.
Delay can be interrupted by pressing Enter key.
config ESP32_CORE_DUMP_LOG_LEVEL
int "Core dump module logging level"
depends on ESP32_ENABLE_COREDUMP
default 1
help
Config core dump module logging level (0-5).
endmenu
choice NUMBER_OF_UNIVERSAL_MAC_ADDRESS
bool "Number of universally administered (by IEEE) MAC address"

View file

@ -24,12 +24,14 @@
#include "esp_panic.h"
#include "esp_partition.h"
#include "esp_clk.h"
#include "esp_core_dump.h"
#include "esp_log.h"
const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump";
typedef uint32_t core_dump_crc_t;
#if CONFIG_ESP32_ENABLE_COREDUMP
#define LOG_LOCAL_LEVEL CONFIG_ESP32_CORE_DUMP_LOG_LEVEL
#include "esp_log.h"
const static DRAM_ATTR char TAG[] = "esp_core_dump";
#define ESP_COREDUMP_LOG( level, format, ... ) if (LOG_LOCAL_LEVEL >= level) { ets_printf(DRAM_STR(format), esp_log_early_timestamp(), (const char *)TAG, ##__VA_ARGS__); }
#define ESP_COREDUMP_LOGE( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_ERROR, LOG_FORMAT(E, format), ##__VA_ARGS__)
#define ESP_COREDUMP_LOGW( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_WARN, LOG_FORMAT(W, format), ##__VA_ARGS__)
@ -43,9 +45,9 @@ const static DRAM_ATTR char TAG[] = "esp_core_dump";
#define ESP_COREDUMP_LOG_PROCESS( format, ... ) do{/*(__VA_ARGS__);*/}while(0)
#endif
// TODO: allow user to set this in menuconfig or get tasks iteratively
#define COREDUMP_MAX_TASKS_NUM 32
#define COREDUMP_MAX_TASK_STACK_SIZE (64*1024)
#define COREDUMP_VERSION 1
typedef esp_err_t (*esp_core_dump_write_prepare_t)(void *priv, uint32_t *data_len);
typedef esp_err_t (*esp_core_dump_write_start_t)(void *priv);
@ -74,6 +76,7 @@ typedef struct _core_dump_write_config_t
typedef struct _core_dump_header_t
{
uint32_t data_len; // data length
uint32_t version; // core dump struct version
uint32_t tasks_num; // number of tasks
uint32_t tcb_sz; // size of TCB
} core_dump_header_t;
@ -101,7 +104,7 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri
{
int cur_task_bad = 0;
esp_err_t err;
TaskSnapshot_t tasks[COREDUMP_MAX_TASKS_NUM];
TaskSnapshot_t tasks[CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM];
UBaseType_t tcb_sz, tcb_sz_padded, task_num;
uint32_t data_len = 0, i, len;
union
@ -110,7 +113,7 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri
core_dump_task_header_t task_hdr;
} dump_data;
task_num = uxTaskGetSnapshotAll(tasks, COREDUMP_MAX_TASKS_NUM, &tcb_sz);
task_num = uxTaskGetSnapshotAll(tasks, CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM, &tcb_sz);
// take TCB padding into account, actual TCB size will be stored in header
if (tcb_sz % sizeof(uint32_t))
tcb_sz_padded = (tcb_sz / sizeof(uint32_t) + 1) * sizeof(uint32_t);
@ -143,9 +146,9 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri
else {
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
XtExcFrame *task_frame2 = (XtExcFrame *)tasks[i].pxTopOfStack;
#endif
ESP_COREDUMP_LOG_PROCESS("Task EXIT/PC/PS/A0/SP %x %x %x %x %x",
task_frame2->exit, task_frame2->pc, task_frame2->ps, task_frame2->a0, task_frame2->a1);
#endif
}
}
len = (uint32_t)tasks[i].pxEndOfStack - (uint32_t)tasks[i].pxTopOfStack;
@ -188,6 +191,7 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri
}
// write header
dump_data.hdr.data_len = data_len;
dump_data.hdr.version = COREDUMP_VERSION;
dump_data.hdr.tasks_num = task_num - write_cfg->bad_tasks_num;
dump_data.hdr.tcb_sz = tcb_sz;
err = write_cfg->write(write_cfg->priv, &dump_data, sizeof(core_dump_header_t));
@ -248,13 +252,10 @@ static void esp_core_dump_write(XtExcFrame *frame, core_dump_write_config_t *wri
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
// magic numbers to control core dump data consistency
#define COREDUMP_FLASH_MAGIC_START 0xE32C04EDUL
#define COREDUMP_FLASH_MAGIC_END 0xE32C04EDUL
typedef struct _core_dump_write_flash_data_t
{
uint32_t off;
uint32_t off; // current offset in partition
core_dump_crc_t crc; // CRC of dumped data
} core_dump_write_flash_data_t;
typedef struct _core_dump_partition_t
@ -267,15 +268,20 @@ typedef struct _core_dump_partition_t
typedef struct _core_dump_flash_config_t
{
// core dump partition start
// core dump partition config
core_dump_partition_t partition;
// core dump partition size
uint32_t crc;
// CRC of core dump partition config
core_dump_crc_t partition_config_crc;
} core_dump_flash_config_t;
// core dump flash data
static core_dump_flash_config_t s_core_flash_config;
static inline core_dump_crc_t esp_core_dump_calc_flash_config_crc(void)
{
return crc32_le(0, (uint8_t const *)&s_core_flash_config.partition, sizeof(s_core_flash_config.partition));
}
static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint32_t data_size)
{
esp_err_t err;
@ -302,8 +308,9 @@ static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint
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++)
for (k = 0; k < len; k++) {
rom_data.data8[k] = *(data + data_len + k);
}
err = spi_flash_write(off + data_len, &rom_data, sizeof(uint32_t));
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to finish write data to flash (%d)!", err);
@ -322,18 +329,19 @@ static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_le
core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv;
// check for available space in partition
// add space for 2 magics. TODO: change to CRC
if ((*data_len + 2*sizeof(uint32_t)) > s_core_flash_config.partition.size) {
if ((*data_len + sizeof(uint32_t)) > s_core_flash_config.partition.size) {
ESP_COREDUMP_LOGE("Not enough space to save core dump!");
return ESP_ERR_NO_MEM;
}
*data_len += 2*sizeof(uint32_t);
// add space for CRC
*data_len += sizeof(core_dump_crc_t);
wr_data->off = 0;
memset(wr_data, 0, sizeof(*wr_data));
sec_num = *data_len / SPI_FLASH_SEC_SIZE;
if (*data_len % SPI_FLASH_SEC_SIZE)
if (*data_len % SPI_FLASH_SEC_SIZE) {
sec_num++;
}
assert(sec_num * SPI_FLASH_SEC_SIZE <= s_core_flash_config.partition.size);
err = spi_flash_erase_range(s_core_flash_config.partition.start + 0, sec_num * SPI_FLASH_SEC_SIZE);
if (err != ESP_OK) {
@ -362,9 +370,7 @@ static esp_err_t esp_core_dump_flash_write_word(core_dump_write_flash_data_t *wr
static esp_err_t esp_core_dump_flash_write_start(void *priv)
{
core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv;
// save magic 1
return esp_core_dump_flash_write_word(wr_data, COREDUMP_FLASH_MAGIC_START);
return ESP_OK;
}
static esp_err_t esp_core_dump_flash_write_end(void *priv)
@ -381,17 +387,16 @@ static esp_err_t esp_core_dump_flash_write_end(void *priv)
if (err != ESP_OK) {
ESP_COREDUMP_LOGE("Failed to read flash (%d)!", err);
return err;
}
else {
} else {
ESP_COREDUMP_LOG_PROCESS("Data from flash:");
for (uint32_t i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) {
ESP_COREDUMP_LOG_PROCESS("%x", rom_data.data32[i]);
}
}
#endif
// save magic 2
return esp_core_dump_flash_write_word(wr_data, COREDUMP_FLASH_MAGIC_END);
// write core dump CRC
ESP_COREDUMP_LOG_PROCESS("Dump data CRC = 0x%x", wr_data->crc);
return esp_core_dump_flash_write_word(wr_data, wr_data->crc);
}
static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_t data_len)
@ -400,10 +405,12 @@ static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_
core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv;
uint32_t len = esp_core_dump_write_flash_padded(s_core_flash_config.partition.start + wr_data->off, data, data_len);
if (len != data_len)
if (len != data_len) {
return ESP_FAIL;
}
wr_data->off += len;
wr_data->crc = crc32_le(wr_data->crc, data, data_len);
return err;
}
@ -413,10 +420,14 @@ void esp_core_dump_to_flash(XtExcFrame *frame)
core_dump_write_config_t wr_cfg;
core_dump_write_flash_data_t wr_data;
uint32_t crc = crc32_le(UINT32_MAX, (uint8_t const *)&s_core_flash_config.partition,
sizeof(s_core_flash_config.partition));
if (s_core_flash_config.crc != crc) {
ESP_COREDUMP_LOGE("Core dump flash config is corrupted! CRC=0x%x instead of 0x%x", crc, s_core_flash_config.crc);
core_dump_crc_t crc = esp_core_dump_calc_flash_config_crc();
if (s_core_flash_config.partition_config_crc != crc) {
ESP_COREDUMP_LOGE("Core dump flash config is corrupted! CRC=0x%x instead of 0x%x", crc, s_core_flash_config.partition_config_crc);
return;
}
// check that partition can hold at least core dump data length
if (s_core_flash_config.partition.start == 0 || s_core_flash_config.partition.size < sizeof(uint32_t)) {
ESP_COREDUMP_LOGE("Invalid flash partition config!");
return;
}
@ -500,10 +511,11 @@ static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t
static int esp_core_dump_uart_get_char() {
int i;
uint32_t reg = (READ_PERI_REG(UART_STATUS_REG(0)) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT;
if (reg)
if (reg) {
i = READ_PERI_REG(UART_FIFO_REG(0));
else
} else {
i = -1;
}
return i;
}
@ -532,8 +544,9 @@ void esp_core_dump_to_uart(XtExcFrame *frame)
ch = esp_core_dump_uart_get_char();
while (!(ch == '\n' || ch == '\r')) {
tm_cur = xthal_get_ccount() / cpu_ticks_per_ms;
if (tm_cur >= tm_end)
if (tm_cur >= tm_end){
break;
}
ch = esp_core_dump_uart_get_char();
}
ESP_COREDUMP_LOGI("Print core dump to uart...");
@ -554,15 +567,66 @@ void esp_core_dump_init()
return;
}
ESP_COREDUMP_LOGI("Found partition '%s' @ %x %d bytes", core_part->label, core_part->address, core_part->size);
s_core_flash_config.partition.start = core_part->address;
s_core_flash_config.partition.size = core_part->size;
s_core_flash_config.crc = crc32_le(UINT32_MAX, (uint8_t const *)&s_core_flash_config.partition,
sizeof(s_core_flash_config.partition));
s_core_flash_config.partition.start = core_part->address;
s_core_flash_config.partition.size = core_part->size;
s_core_flash_config.partition_config_crc = esp_core_dump_calc_flash_config_crc();
#endif
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_UART
ESP_COREDUMP_LOGI("Init core dump to UART");
#endif
}
#endif
esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size)
{
esp_err_t err;
const void *core_data;
spi_flash_mmap_handle_t core_data_handle;
if (out_addr == NULL || out_size == NULL) {
return ESP_ERR_INVALID_ARG;
}
const esp_partition_t *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_FAIL;
}
if (core_part->size < sizeof(uint32_t)) {
ESP_LOGE(TAG, "Too small core dump partition!");
return ESP_FAIL;
}
err = esp_partition_mmap(core_part, 0, sizeof(uint32_t),
SPI_FLASH_MMAP_DATA, &core_data, &core_data_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to mmap core dump data (%d)!", err);
return err;
}
uint32_t *dw = (uint32_t *)core_data;
*out_size = *dw;
spi_flash_munmap(core_data_handle);
// remap full core dump with CRC
err = esp_partition_mmap(core_part, 0, *out_size,
SPI_FLASH_MMAP_DATA, &core_data, &core_data_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to mmap core dump data (%d)!", err);
return err;
}
uint32_t *crc = (uint32_t *)(((uint8_t *)core_data) + *out_size);
crc--; // Point to CRC field
// Calc CRC over core dump data except for CRC field
core_dump_crc_t cur_crc = crc32_le(0, (uint8_t const *)core_data, *out_size - sizeof(core_dump_crc_t));
if (*crc != cur_crc) {
ESP_LOGE(TAG, "Core dump data CRC check failed: 0x%x -> 0x%x!", *crc, cur_crc);
spi_flash_munmap(core_data_handle);
return ESP_FAIL;
}
spi_flash_munmap(core_data_handle);
*out_addr = core_part->address;
return ESP_OK;
}
#endif

View file

@ -382,6 +382,11 @@ void start_cpu0_default(void)
#if CONFIG_ESP32_ENABLE_COREDUMP
esp_core_dump_init();
size_t core_data_sz = 0;
size_t core_data_addr = 0;
if (esp_core_dump_image_get(&core_data_addr, &core_data_sz) == ESP_OK && core_data_sz > 0) {
ESP_LOGI(TAG, "Found core dump %d bytes in flash @ 0x%x", core_data_sz, core_data_addr);
}
#endif
portBASE_TYPE res = xTaskCreatePinnedToCore(&main_task, "main",

View file

@ -14,6 +14,11 @@
#ifndef ESP_CORE_DUMP_H_
#define ESP_CORE_DUMP_H_
/**************************************************************************************/
/******************************** EXCEPTION MODE API **********************************/
/**************************************************************************************/
/**
* @brief Initializes core dump module internal data.
*
@ -25,29 +30,29 @@ void esp_core_dump_init();
* @brief Saves core dump to flash.
*
* The structure of data stored in flash is as follows:
* | 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 |
* | CRC32 |
*
* Core dump in flash consists of header and data for every task in the system at the moment of crash.
* For flash data integrity control two magic numbers are used at the beginning and the end of core dump.
* For flash data integrity control CRC is used at the end of core the dump data.
* The structure of core dump data is described below in details.
* 1) MAGIC1 and MAGIC2 are special numbers stored at the beginning and the end of core dump.
* They are used to control core dump data integrity. Size of every number is 4 bytes.
* 2) Core dump starts with header:
* 2.1) TOTAL_LEN is total length of core dump data in flash including magic numbers. Size is 4 bytes.
* 2.2) TASKS_NUM is the number of tasks for which data are stored. Size is 4 bytes.
* 2.3) TCB_SIZE is the size of task's TCB structure. Size is 4 bytes.
* 3) Core dump header is followed by the data for every task in the system.
* 1) Core dump starts with header:
* 1.1) TOTAL_LEN is total length of core dump data in flash including CRC. Size is 4 bytes.
* 1.2) TASKS_NUM is the number of tasks for which data are stored. Size is 4 bytes.
* 1.3) TCB_SIZE is the size of task's TCB structure. Size is 4 bytes.
* 2) Core dump header is followed by the data for every task in the system.
* Task data are started with task header:
* 3.1) TCB_ADDR is the address of TCB in memory. Size is 4 bytes.
* 3.2) STACK_TOP is the top of task's stack (address of the topmost stack item). Size is 4 bytes.
* 3.2) STACK_END is the end of task's stack (address from which task's stack starts). Size is 4 bytes.
* 4) Task header is followed by TCB data. Size is TCB_SIZE bytes.
* 5) Task's stack is placed after TCB data. Size is (STACK_END - STACK_TOP) bytes.
* 2.1) TCB_ADDR is the address of TCB in memory. Size is 4 bytes.
* 2.2) STACK_TOP is the top of task's stack (address of the topmost stack item). Size is 4 bytes.
* 2.2) STACK_END is the end of task's stack (address from which task's stack starts). Size is 4 bytes.
* 3) Task header is followed by TCB data. Size is TCB_SIZE bytes.
* 4) Task's stack is placed after TCB data. Size is (STACK_END - STACK_TOP) bytes.
* 5) CRC is placed at the end of the data.
*/
void esp_core_dump_to_flash();
@ -55,10 +60,26 @@ void esp_core_dump_to_flash();
* @brief Print base64-encoded core dump to UART.
*
* The structure of core dump data is the same as for data stored in flash (@see esp_core_dump_to_flash) with some notes:
* 1) Magic numbers are not present in core dump printed to UART.
* 2) Since magic numbers are omitted TOTAL_LEN does not include their size.
* 1) CRC is not present in core dump printed to UART.
* 2) Since CRC is omitted TOTAL_LEN does not include its size.
* 3) Printed base64 data are surrounded with special messages to help user recognize the start and end of actual data.
*/
void esp_core_dump_to_uart();
/**************************************************************************************/
/*********************************** USER MODE API ************************************/
/**************************************************************************************/
/**
* @brief Retrieves address and size of coredump data in flash.
* This function is always available, even when core dump is disabled in menuconfig.
*
* @param out_addr pointer to store image address in flash.
* @param out_size pointer to store image size in flash (including CRC). In bytes.
*
* @return ESP_OK on success, otherwise \see esp_err_t
*/
esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size);
#endif

View file

@ -24,6 +24,8 @@ import struct
import array
import errno
import base64
import binascii
import logging
idf_path = os.getenv('IDF_PATH')
if idf_path:
@ -35,7 +37,7 @@ except ImportError:
print("Esptool is not found! Set proper $IDF_PATH in environment.")
sys.exit(2)
__version__ = "0.2-dev"
__version__ = "0.3-dev"
if os.name == 'nt':
CLOSE_FDS = False
@ -327,7 +329,7 @@ class ESPCoreDumpElfFile(esptool.ELFFile):
if len(section_header) == 0:
raise ESPCoreDumpError("No section header found at offset %04x in ELF file." % section_header_offs)
if len(section_header) % LEN_SEC_HEADER != 0:
print('WARNING: Unexpected ELF section header length %04x is not mod-%02x' % (len(section_header),LEN_SEC_HEADER))
logging.warning('Unexpected ELF section header length %04x is not mod-%02x' % (len(section_header),LEN_SEC_HEADER))
# walk through the section header and extract all sections
section_header_offsets = range(0, len(section_header), LEN_SEC_HEADER)
@ -343,7 +345,7 @@ class ESPCoreDumpElfFile(esptool.ELFFile):
raise ESPCoreDumpError("ELF file has no STRTAB section at shstrndx %d" % shstrndx)
_,sec_type,_,_,sec_size,sec_offs = read_section_header(shstrndx * LEN_SEC_HEADER)
if sec_type != esptool.ELFFile.SEC_TYPE_STRTAB:
print('WARNING: ELF file has incorrect STRTAB section type 0x%02x' % sec_type)
logging.warning('ELF file has incorrect STRTAB section type 0x%02x' % sec_type)
f.seek(sec_offs)
string_table = f.read(sec_size)
@ -370,7 +372,7 @@ class ESPCoreDumpElfFile(esptool.ELFFile):
if len(seg_table) == 0:
raise ESPCoreDumpError("No program header table found at offset %04x in ELF file." % seg_table_offs)
if len(seg_table) % LEN_SEG_HEADER != 0:
print('WARNING: Unexpected ELF program header table length %04x is not mod-%02x' % (len(seg_table),LEN_SEG_HEADER))
logging.warning('Unexpected ELF program header table length %04x is not mod-%02x' % (len(seg_table),LEN_SEG_HEADER))
# walk through the program segment table and extract all segments
seg_table_offs = range(0, len(seg_table), LEN_SEG_HEADER)
@ -456,7 +458,8 @@ class ESPCoreDumpLoaderError(ESPCoreDumpError):
class ESPCoreDumpLoader(object):
"""Core dump loader base class
"""
ESP32_COREDUMP_HDR_FMT = '<3L'
ESP32_COREDUMP_VESION = 1
ESP32_COREDUMP_HDR_FMT = '<4L'
ESP32_COREDUMP_HDR_SZ = struct.calcsize(ESP32_COREDUMP_HDR_FMT)
ESP32_COREDUMP_TSK_HDR_FMT = '<3L'
ESP32_COREDUMP_TSK_HDR_SZ = struct.calcsize(ESP32_COREDUMP_TSK_HDR_FMT)
@ -564,7 +567,7 @@ class ESPCoreDumpLoader(object):
os.remove(fname)
except OSError as e:
if e.errno != errno.ENOENT:
print("Warning failed to remove temp file '%s' (%d)!" % (fname, e.errno))
logging.warning("Failed to remove temp file '%s' (%d)!" % (fname, e.errno))
def cleanup(self):
"""Cleans up loader resources
@ -579,7 +582,9 @@ class ESPCoreDumpLoader(object):
"""
core_off = off
data = self.read_data(core_off, self.ESP32_COREDUMP_HDR_SZ)
tot_len,task_num,tcbsz = struct.unpack_from(self.ESP32_COREDUMP_HDR_FMT, data)
tot_len,coredump_ver,task_num,tcbsz = struct.unpack_from(self.ESP32_COREDUMP_HDR_FMT, data)
if coredump_ver > self.ESP32_COREDUMP_VESION:
raise ESPCoreDumpLoaderError("Core dump version '%d' is not supported! Should be up to '%d'." % (coredump_ver, self.ESP32_COREDUMP_VESION))
tcbsz_aligned = tcbsz
if tcbsz_aligned % 4:
tcbsz_aligned = 4*(old_div(tcbsz_aligned,4) + 1)
@ -601,16 +606,25 @@ class ESPCoreDumpLoader(object):
stack_len_aligned = 4*(old_div(stack_len_aligned,4) + 1)
core_off += self.ESP32_COREDUMP_TSK_HDR_SZ
logging.info("Read TCB %d bytes @ 0x%x" % (tcbsz_aligned, tcb_addr))
data = self.read_data(core_off, tcbsz_aligned)
if tcbsz != tcbsz_aligned:
core_elf.add_program_segment(tcb_addr, data[:tcbsz - tcbsz_aligned], ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W)
else:
core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W)
try:
if tcbsz != tcbsz_aligned:
core_elf.add_program_segment(tcb_addr, data[:tcbsz - tcbsz_aligned], ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W)
else:
core_elf.add_program_segment(tcb_addr, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W)
except ESPCoreDumpError as e:
logging.warning("Skip TCB %d bytes @ 0x%x. (Reason: %s)" % (tcbsz_aligned, tcb_addr, e))
core_off += tcbsz_aligned
logging.info("Read stack %d bytes @ 0x%x" % (stack_len_aligned, stack_base))
data = self.read_data(core_off, stack_len_aligned)
if stack_len != stack_len_aligned:
data = data[:stack_len - stack_len_aligned]
core_elf.add_program_segment(stack_base, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W)
try:
core_elf.add_program_segment(stack_base, data, ESPCoreDumpElfFile.PT_LOAD, ESPCoreDumpSegment.PF_R | ESPCoreDumpSegment.PF_W)
except ESPCoreDumpError as e:
logging.warning("Skip task's (%x) stack %d bytes @ 0x%x. (Reason: %s)" % (tcb_addr, stack_len_aligned, stack_base, e))
core_off += stack_len_aligned
try:
task_regs = self._get_registers_from_stack(data, stack_end > stack_top)
@ -624,12 +638,18 @@ class ESPCoreDumpLoader(object):
notes += note
# add notes
core_elf.add_program_segment(0, notes, ESPCoreDumpElfFile.PT_NOTE, 0)
try:
core_elf.add_program_segment(0, notes, ESPCoreDumpElfFile.PT_NOTE, 0)
except ESPCoreDumpError as e:
logging.warning("Skip NOTES segment %d bytes @ 0x%x. (Reason: %s)" % (len(notes), 0, e))
# add ROM text sections
if rom_elf:
for ps in rom_elf.program_segments:
if ps.flags & ESPCoreDumpSegment.PF_X:
core_elf.add_program_segment(ps.addr, ps.data, ESPCoreDumpElfFile.PT_LOAD, ps.flags)
try:
core_elf.add_program_segment(ps.addr, ps.data, ESPCoreDumpElfFile.PT_LOAD, ps.flags)
except ESPCoreDumpError as e:
logging.warning("Skip ROM segment %d bytes @ 0x%x. (Reason: %s)" % (len(ps.data), ps.addr, e))
core_elf.e_type = ESPCoreDumpElfFile.ET_CORE
core_elf.e_machine = ESPCoreDumpElfFile.EM_XTENSA
@ -690,12 +710,10 @@ class ESPCoreDumpFileLoader(ESPCoreDumpLoader):
class ESPCoreDumpFlashLoader(ESPCoreDumpLoader):
"""Core dump flash loader class
"""
ESP32_COREDUMP_FLASH_MAGIC_START = 0xE32C04ED
ESP32_COREDUMP_FLASH_MAGIC_END = 0xE32C04ED
ESP32_COREDUMP_FLASH_MAGIC_FMT = '<L'
ESP32_COREDUMP_FLASH_MAGIC_SZ = struct.calcsize(ESP32_COREDUMP_FLASH_MAGIC_FMT)
ESP32_COREDUMP_FLASH_HDR_FMT = '<4L'
ESP32_COREDUMP_FLASH_HDR_SZ = struct.calcsize(ESP32_COREDUMP_FLASH_HDR_FMT)
ESP32_COREDUMP_FLASH_CRC_FMT = '<L'
ESP32_COREDUMP_FLASH_CRC_SZ = struct.calcsize(ESP32_COREDUMP_FLASH_CRC_FMT)
ESP32_COREDUMP_FLASH_LEN_FMT = '<L'
ESP32_COREDUMP_FLASH_LEN_SZ = struct.calcsize(ESP32_COREDUMP_FLASH_LEN_FMT)
def __init__(self, off, tool_path=None, chip='esp32', port=None, baud=None):
"""Constructor for core dump flash loader
@ -722,7 +740,7 @@ class ESPCoreDumpFlashLoader(ESPCoreDumpLoader):
tool_args.extend(['-p', self.port])
if self.baud:
tool_args.extend(['-b', str(self.baud)])
tool_args.extend(['read_flash', str(off), str(self.ESP32_COREDUMP_FLASH_HDR_SZ), ''])
tool_args.extend(['read_flash', str(off), str(self.ESP32_COREDUMP_FLASH_LEN_SZ), ''])
self.fcore_name = None
try:
@ -737,11 +755,11 @@ class ESPCoreDumpFlashLoader(ESPCoreDumpLoader):
tool_args[-2] = str(self. dump_sz)
et_out = subprocess.check_output(tool_args)
print(et_out.decode('utf-8'))
except subprocess.CalledProcessError as e:
print("esptool script execution failed with err %d" % e.returncode)
print("Command ran: '%s'" % e.cmd)
print("Command out:")
print(e.output)
except subprocess.CalledProcessError as e:
logging.error("esptool script execution failed with err %d" % e.returncode)
logging.info("Command ran: '%s'" % e.cmd)
logging.info("Command out:")
logging.info(e.output)
if self.fcore_name:
self.remove_tmp_file(self.fcore_name)
raise e
@ -750,26 +768,20 @@ class ESPCoreDumpFlashLoader(ESPCoreDumpLoader):
def _read_core_dump_length(self, f):
"""Reads core dump length
"""
data = f.read(4*4)
mag1,tot_len,task_num,tcbsz = struct.unpack_from(self.ESP32_COREDUMP_FLASH_HDR_FMT, data)
if mag1 != self.ESP32_COREDUMP_FLASH_MAGIC_START:
raise ESPCoreDumpLoaderError("Invalid start magic number!")
data = f.read(self.ESP32_COREDUMP_FLASH_LEN_SZ)
tot_len, = struct.unpack_from(self.ESP32_COREDUMP_FLASH_LEN_FMT, data)
return tot_len
def create_corefile(self, core_fname=None, rom_elf=None):
"""Checks flash coredump data integrity and creates ELF file
"""
data = self.read_data(0, self.ESP32_COREDUMP_FLASH_MAGIC_SZ)
mag1, = struct.unpack_from(self.ESP32_COREDUMP_FLASH_MAGIC_FMT, data)
if mag1 != self.ESP32_COREDUMP_FLASH_MAGIC_START:
raise ESPCoreDumpLoaderError("Invalid start marker %x" % mag1)
data = self.read_data(self.dump_sz-self.ESP32_COREDUMP_FLASH_MAGIC_SZ, self.ESP32_COREDUMP_FLASH_MAGIC_SZ)
mag2, = struct.unpack_from(self.ESP32_COREDUMP_FLASH_MAGIC_FMT, data)
if mag2 != self.ESP32_COREDUMP_FLASH_MAGIC_END:
raise ESPCoreDumpLoaderError("Invalid end marker %x" % mag2)
return super(ESPCoreDumpFlashLoader, self).create_corefile(core_fname, off=self.ESP32_COREDUMP_FLASH_MAGIC_SZ, rom_elf=rom_elf)
data = self.read_data(self.dump_sz - self.ESP32_COREDUMP_FLASH_CRC_SZ, self.ESP32_COREDUMP_FLASH_CRC_SZ)
dump_crc, = struct.unpack_from(self.ESP32_COREDUMP_FLASH_CRC_FMT, data)
data = self.read_data(0, self.dump_sz - self.ESP32_COREDUMP_FLASH_CRC_SZ)
data_crc = binascii.crc32(data) & 0xffffffff
if dump_crc != data_crc:
raise ESPCoreDumpLoaderError("Invalid core dump CRC %x, should be %x" % (data_crc, dump_crc))
return super(ESPCoreDumpFlashLoader, self).create_corefile(core_fname)
class GDBMIOutRecordHandler(object):
@ -836,7 +848,7 @@ class GDBMIResultHandler(GDBMIOutRecordHandler):
if self.result_str.startswith(','):
self.result_str = self.result_str[1:]
else:
print("Invalid result format: '%s'" % ln)
logging.error("Invalid result format: '%s'" % ln)
else:
self.result_str = ''
return True
@ -856,7 +868,7 @@ class GDBMIResultHandler(GDBMIOutRecordHandler):
return
if self._parse_rc(ln, self.RC_EXIT):
return
print("Unknown GDB/MI result: '%s'" % ln)
logging.error("Unknown GDB/MI result: '%s'" % ln)
class GDBMIStreamConsoleHandler(GDBMIOutStreamHandler):
@ -886,7 +898,7 @@ def dbg_corefile(args):
loader = ESPCoreDumpFlashLoader(args.off, port=args.port)
core_fname = loader.create_corefile(args.save_core, rom_elf=rom_elf)
if not core_fname:
print("Failed to create corefile!")
logging.error("Failed to create corefile!")
loader.cleanup()
return
else:
@ -895,7 +907,7 @@ def dbg_corefile(args):
loader = ESPCoreDumpFileLoader(core_fname, args.core_format == 'b64')
core_fname = loader.create_corefile(args.save_core, rom_elf=rom_elf)
if not core_fname:
print("Failed to create corefile!")
logging.error("Failed to create corefile!")
loader.cleanup()
return
@ -964,12 +976,12 @@ def info_corefile(args):
p.stdin.write(bytearray("-interpreter-exec console \"%s\"\n" % gdb_cmd, encoding='utf-8'))
gdbmi_read2prompt(p.stdout, handlers)
if not handlers[GDBMIResultHandler.TAG].result_class or handlers[GDBMIResultHandler.TAG].result_class == GDBMIResultHandler.RC_EXIT:
print("GDB exited (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str))
logging.error("GDB exited (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str))
p.wait()
print("Problem occured! GDB exited, restart it.")
logging.error("Problem occured! GDB exited, restart it.")
p = gdbmi_start(handlers, [])
elif handlers[GDBMIResultHandler.TAG].result_class != GDBMIResultHandler.RC_DONE:
print("GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str))
logging.error("GDB/MI command failed (%s / %s)!" % (handlers[GDBMIResultHandler.TAG].result_class, handlers[GDBMIResultHandler.TAG].result_str))
return p
loader = None
@ -978,7 +990,7 @@ def info_corefile(args):
loader = ESPCoreDumpFlashLoader(args.off, port=args.port)
core_fname = loader.create_corefile(args.save_core, rom_elf=rom_elf)
if not core_fname:
print("Failed to create corefile!")
logging.error("Failed to create corefile!")
loader.cleanup()
return
else:
@ -987,7 +999,7 @@ def info_corefile(args):
loader = ESPCoreDumpFileLoader(core_fname, args.core_format == 'b64')
core_fname = loader.create_corefile(args.save_core, rom_elf=rom_elf)
if not core_fname:
print("Failed to create corefile!")
logging.error("Failed to create corefile!")
loader.cleanup()
return
@ -1115,6 +1127,7 @@ def main():
parser_debug_coredump = subparsers.add_parser(
'dbg_corefile',
help='Starts GDB debugging session with specified corefile')
parser_debug_coredump.add_argument('--debug', '-d', help='Log level (0..3)', type=int, default=2)
parser_debug_coredump.add_argument('--gdb', '-g', help='Path to gdb', default='xtensa-esp32-elf-gdb')
parser_debug_coredump.add_argument('--core', '-c', help='Path to core dump file (if skipped core dump will be read from flash)', type=str)
parser_debug_coredump.add_argument('--core-format', '-t', help='(elf, raw or b64). File specified with "-c" is an ELF ("elf"), raw (raw) or base64-encoded (b64) binary', type=str, default='elf')
@ -1126,6 +1139,7 @@ def main():
parser_info_coredump = subparsers.add_parser(
'info_corefile',
help='Print core dump info from file')
parser_info_coredump.add_argument('--debug', '-d', help='Log level (0..3)', type=int, default=0)
parser_info_coredump.add_argument('--gdb', '-g', help='Path to gdb', default='xtensa-esp32-elf-gdb')
parser_info_coredump.add_argument('--core', '-c', help='Path to core dump file (if skipped core dump will be read from flash)', type=str)
parser_info_coredump.add_argument('--core-format', '-t', help='(elf, raw or b64). File specified with "-c" is an ELF ("elf"), raw (raw) or base64-encoded (b64) binary', type=str, default='elf')
@ -1141,6 +1155,21 @@ def main():
args = parser.parse_args()
log_level = logging.CRITICAL
if args.debug == 0:
log_level = logging.CRITICAL
elif args.debug == 1:
log_level = logging.ERROR
elif args.debug == 2:
log_level = logging.WARNING
elif args.debug == 3:
log_level = logging.INFO
else:
log_level = logging.DEBUG
# logging.warning('Watch out!') # will print a message to the console
# logging.info('I told you so') # will not print anything
logging.basicConfig(format='%(levelname)s:%(message)s', level=log_level)
print('espcoredump.py v%s' % __version__)
operation_func = globals()[args.operation]

View file

@ -1,158 +1,166 @@
lBoAAAkAAABkAQAA
BD77PwB7+z/QfPs/
cHv7P3B8+z9tAAAAPC37Pzwt+z8EPvs/NC37PxIAAAClpaWlpaWlpQQ++z8AAAAA
BwAAANR0+z91bmFsaWduZWRfcHRyX3QAAQAAANB8+z8AAAAAIAgGAAcAAAAAAAAA
AAAAAAAAAAAAAAAAjOj6P/To+j9c6fo/AAAAAAAAAAABAAAAAAAAAHg8QD8AAAAA
HBwAAAEAAAAJAAAAZAEAAA==
9ID7P/B++z/sgPs/
QH/7P4CA+z9rAAAATC37P0wt+z/0gPs/RC37PxIAAACjT4txhJQrK/SA+z8AAAAA
BwAAAPB4+z91bmFsaWduZWRfcHRyX3QAAQAAAOyA+z8AAAAAIAgGAAcAAAAAAAAA
AAAAAAAAAAAAAAAArOj6PxTp+j986fo/AAAAAAAAAAABAAAAAAAAAFgvQD8AAAAA
SB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAA=
aDtAP20hDUAwBAYASCENgMB7+z8CAAAANjxAPwB8+z/06Po/AAAAAAAAAAAFAAAA
rf///yAAAABsPvs/AQAAAIAAAAABAAAAAAAAAAAAAAAdAAAABQAAAP0UAEANFQBA
/////wEAAACAAAAAfCEIQIwP+z8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAEAAACAAAAAAQAAAAAAAABIIQ2A8Hv7PwEAAAD06Po/
pycNgPB7+z8KAAAA9Oj6PwB8+z/06Po/AAAAAAAAAACQIQ2AIHz7PwoAAAABAAAA
kztAPx4AAAA1PEA/AQAAACMABgABAAAAIQAGAKA6+z8AAAAAUHz7PwAAAAAAAAAA
AwAAAFB8+z8AAAAAAAAAAGwQ+z8EPvs/AAAAAAAAAAAAAAAAcHz7PwAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHx8+z8AAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
/Fb7P5BV+z/0Vvs/
kFX7P5BW+z/ZclwZcFz7P7As+z/8Vvs/qCz7PxkAAADVIwJJEo189/xW+z8AAAAA
AAAAAPhS+z9JRExFMAAweiWIEpHwtvYAAAAAAPRW+z8AAAAAIAIGAAAAAAAAAAAA
AAAAAAAAAAAAAAAAjOj6P/To+j9c6fo/AAAAAAAAAAABAAAAAAAAAHg8QD8AAAAA
SB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAA8448=
2CEIQIZKDkAwAgYAuFcIgFBW+z8IAAAAAAAAAAEAAABoXPs/AAAAAAEAAACsK/s/
kCv7PwAAAAABAAAAAAAAAAEAAAAhAAYAIwgGAAAAAADcSwiAEFb7PwAAAAAAAAAA
AAAAANEjCEABAAAA6GQIQAHp+j8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAIQAGACMIBgAAAAAAcFb7PwAAAAAAAAAA
AQAAAGhc+z8AAAAAAQAAAAAAAACQVvs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnFb7PwAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAA=
aFz7PwBb+z9gXPs/
AFv7PwBc+z9TsePnsCz7PwRX+z9oXPs/qCz7PxkAAACoWji9Rc4eO2hc+z8AAAAA
AAAAAGRY+z9JRExFMQBwjmCRXQvnOuwAAQAAAGBc+z8AAAAAIA4GAAAAAAAAAAAA
AAAAAAAAAAAAAAAAjOj6P/To+j9c6fo/AAAAAAAAAAABAAAAAAAAAHg8QD8AAAAA
SB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAACbsV8=
2CEIQIZKDkAwDgYAuFcIgMBb+z8IAAAAAQAAAAAAAAD8Vvs/AAAAAAEAAADMK/s/
kCv7PwAAAAABAACAAAAAAAEAAAAhAAYAAAAAAAAAAAAgAAYAAQAAAAAAAAAAAAAA
AAAAANEjCEABAAAA6GQIQBzv+j8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAIQAGAAAAAAAAAAAA4Fv7PwAAAAAAAAAA
AAAAAPxW+z8AAAAAAQAAAAAAAAAAXPs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADFz7PwAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAA==
nDz7P2Bz+z/MdPs/
YHP7P2B0+z/RAAAAnCz7P3Q/+z+cPPs/lCz7PxQAAAAkbPs/JGz7P5w8+z8AAAAA
BQAAANBs+z9iYWRfcHRyX3Rhc2sApaUA////f8x0+z8AAAAAIQAGAAUAAAAAAAAA
AAAAAAAAAAAAAAAAjOj6P/To+j9c6fo/AAAAAAAAAAABAAAAAAAAAHg8QD8AAAAA
SB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAClpaU=
2CEIQJ5VCEAwAgYAEyENgCB0+z/RAAAAAAAAAGwQ+z+cPPs/AAAAAAAAAACeVQiA
AHT7PwAAAADRAAAAIwAGAAEAAAAhAAYAcFv7PwAAAACkJw2A4HP7P/0UAEANFQBA
+f///9EjCEABAAAA6GQIQHwH+z8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAACMABgABAAAAIQAGAHBb+z8AAAAAQHT7PwAAAAAAAAAA
bBD7P5w8+z8AAAAAAAAAAAAAAABgdPs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbHT7PwAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAADTDKk=
HwAAAKkiDUAwBAYAhCINgLB/+z8CAAAAVi9AP/B/+z8U6fo/AAAAAAAAAAAFAAAA
rf///yAAAABcgfs/AQAAAIAAAAABAAAAAQAAAB8AAAAdAAAABQAAAP0UAEANFQBA
+P///wEAAACAAAAAoCEIQLAB+z8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAEAAACAAAAAAQAAAAEAAACEIg2A4H/7PwEAAAAU6fo/
HygNgOB/+z8KAAAAFOn6P/B/+z8U6fo/AAAAAAAAAADMIg2AEID7PwoAAAABAAAA
sy5APx4AAABVL0A/AQAAAAAAAAAAd/s/QCINQAAAAABgTQiAQID7PwAAAAAAAAAA
Y00IgECA+z8AAAAAAAAAAGgQ+z8BAAAAIQAGAPB2+z8AAAAAYID7P7QiDUAAAAAA
IwAGAPSA+z8AAAAAAAAAAAAAAACAgPs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjID7PwAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
bD/7P3CD+z/UhPs/
cIP7P3CE+z/RAAAApDz7P5ws+z9sP/s/lCz7Pw8AAAC4PPs/JGz7P2w/+z8AAAAA
CgAAANh8+z9mYWlsZWRfYXNzZXJ0X3QAAAAAANSE+z8AAAAAIQAGAAoAAAAAAAAA
AAAAAAAAAAAAAAAAjOj6P/To+j9c6fo/AAAAAAAAAAABAAAAAAAAAHg8QD8AAAAA
0Ff7PyBW+z/IV/s/
IFb7P2BX+z9IbutSRF/7P8As+z/QV/s/uCz7PxkAAACbAEY/rLGittBX+z8AAAAA
AAAAAMxR+z9JRExFMADlCigSQ5E2PU0AAAAAAMhX+z8AAAAAIAAGAAAAAAAAAAAA
AAAAAAAAAAAAAAAArOj6PxTp+j986fo/AAAAAAAAAAABAAAAAAAAAFgvQD8AAAAA
SB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAA=
2CEIQJ5VCEAwAgYAoyANgDCE+z/RAAAAAAAAAGwQ+z9sP/s/AAAAAAAAAACeVQiA
EIT7PwAAAADRAAAAIwAGAAEAAAAhAAYAAAAAAAAAAACkJw2A8IP7P/0UAEANFQBA
+P///9EjCEABAAAA6GQIQIwX+z8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAACMABgABAAAAIQAGAAAAAAAAAAAAUIT7PwAAAAAAAAAA
bBD7P2w/+z8AAAAAAAAAAAAAAABwhPs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfIT7PwAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAADCNHc=
/CEIQKa/DUAwAgYAVhYNgOBW+z8AAAAAAQAAAAAAAAABAAAA4ADwPwEAAAA+Ew2A
sFb7PwAAAAABAAAAX08IgGCI+z8DAAAAIwAGAAAAAABgK/s/OBMNQAAAAAAAAAAA
AAAAAPUjCEBgiPs/5GkIQJDY+j8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
AQAAAAAAAAAAAAAAAAAAAP//P7MAAAAAAAAAAAAAAADdXAiAAFf7PwgAAAAAAAAA
AAAAAAEAAADgAPA/AQAAAGBNCIAgV/s/AAAAAAAAAAABAAAAAQAAACEABgAjBAYA
AAAAAEBX+z/UXAhAAAAAACMABgA8X/s/AAAAAAEAAAAAAAAAYFf7PwAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGxX+z8AAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAA=
yGb7P0Bl+z/AZvs/
QGX7P2Bm+z8AAAAAiCz7P4gs+z/IZvs/gCz7PxgAAAD8Xfs//F37P8hm+z/0Xfs/
AQAAAMRe+z9UbXIgU3ZjAFUPn9OBr6IAAAAAAMBm+z8AAAAAIQAGAAEAAAAAAAAA
AAAAAAAAAAAAAAAAjOj6P/To+j9c6fo/AAAAAAAAAAABAAAAAAAAAHg8QD8AAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
PF/7P5Bd+z80X/s/
kF37P9Be+z/fg6T1wCz7P9hX+z88X/s/uCz7PxkAAAC+waeWpT7XiDxf+z8AAAAA
AAAAADhZ+z9JRExFMQDubL2eTufksFwAAQAAADRf+z8AAAAAIA4GAAAAAAAAAAAA
AAAAAAAAAAAAAAAArOj6PxTp+j986fo/AAAAAAAAAAABAAAAAAAAAFgvQD8AAAAA
SB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAADm+Zc=
2CEIQJxiCEAwCAYAj2MIgABm+z+oLvs/AAAAAAEAAAAMUfs/AAAAAAEAAACcYgiA
4GX7PwAAAAAALPs/GF77PwAAAAAAAAAAAAAAAAAAAAClpaWlpaWlpQAAAAAAAAAA
AAAAANEjCEAAAAAA6GQIQHz5+j8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMGb7PwAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAHz5+j8AAAAAAAAAAAAAAAAAAAAAYGb7PwAAAAAAAAAA
AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAABsZvs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAACMeJk=
/CEIQKa/DUAwDgYAVhYNgFBe+z8AAAAAAQAAgAAAAAABAAAAAwAAACMABgA+Ew2A
IF77PwAAAAAjCAYAIAgGAAEAAAAgCAYAIwAGAAAAAAAzEw2AAF77PwAAAAAAAAAA
AAAAAPUjCEABAAAA5GkIQADg+j8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAF9PCICQNvs/AwAAACMABgDdXAiAcF77PwgAAAABAAAA
AAAAAAEAAAADAAAAIwAGAGBNCICQXvs/AAAAAAAAAAABAAAAAQAAACEABgAAAAAA
AAAAALBe+z/UXAhAAAAAACMABgDQV/s/AAAAAAEAAAAAAAAA0F77PwAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANxe+z8AAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
iPr6PwD5+j+A+vo/
APn6PyD6+j/akPlfPDv7P6D9+j+I+vo/JCz7PwMAAABc6vo/XOr6P4j6+j9U6vo/
FgAAAITq+j9lc3BfdGltZXIAH+HpXvQAAAAAAID6+j8AAAAAIQAGABYAAAAAAAAA
AAAAAAAAAAAAAAAAjOj6P/To+j9c6fo/AAAAAAAAAAABAAAAAAAAAHg8QD8AAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
iHf7PwB2+z+Ad/s/
AHb7PyB3+z/PAAAArCz7P2iK+z+Id/s/pCz7PxQAAAB0bvs/dG77P4h3+z8AAAAA
BQAAAIRv+z9iYWRfcHRyX3Rhc2sAciwA////f4B3+z8AAAAAIQAGAAUAAAAAAAAA
AAAAAAAAAAAAAAAArOj6PxTp+j986fo/AAAAAAAAAAABAAAAAAAAAFgvQD8AAAAA
SB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAACISzU=
2CEIQK1GCEAwAAYA+xQNgMD5+j8w6vo/AAAAAHjq+j8AAAAAAQAAAAEAAACtRgiA
oPn6PwAAAACcLvs/nC77PwEAAAAhAAYAAAAAAAAAAAClpaWlpaWlpQAAAAAAAAAA
AAAAANEjCEABAAAA6GQIQDyN+j8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPr6PwAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAP////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAACD6+j8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAs+vo/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAC6vHI=
/CEIQMZaCEAwAgYATyINgMB2+z/PAAAAAAAAAGgQ+z8BAAAAIQAGACMIBgDGWgiA
oHb7PwAAAADPAAAAIwAGAKg4+z8AAAAAAAAAAAAAAAAcKA2AgHb7P/0UAEANFQBA
+f////UjCECoOPs/5GkIQFD4+j8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAACMABgCoOPs/AAAAAAAAAABgTQiA4Hb7PwAAAAAAAAAA
aBD7PwEAAAAhAAYAIwgGAAAAAAAAd/s/QCINQAAAAAAjAAYAiHf7PwAAAAAAAAAA
AAAAACB3+z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAsd/s/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
NDv7P4A5+z8sO/s/
gDn7P8A6+z/APAiILCz7P5D6+j80O/s/JCz7PwEAAAAs//o/LP/6PzQ7+z8k//o/
GAAAADA3+z9pcGMxAMP571b2wMSsI4QAAQAAACw7+z8AAAAAIQAGABgAAAAAAAAA
AAAAAAAAAAAAAAAAjOj6P/To+j9c6fo/AAAAAAAAAAABAAAAAAAAAHg8QD8AAAAA
YIr7P9CI+z9Yivs/
0Ij7P/CJ+z/PAAAAkHf7P6ws+z9givs/pCz7Pw8AAACkd/s/dG77P2CK+z8AAAAA
CgAAAFyC+z9mYWlsZWRfYXNzZXJ0X3QAAAAAAFiK+z8AAAAAIQAGAAoAAAAAAAAA
AAAAAAAAAAAAAAAArOj6PxTp+j986fo/AAAAAAAAAAABAAAAAAAAAFgvQD8AAAAA
SB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAADZjgQ=
2CEIQEoRCEAwAAYArUYIgEA6+z8BAAAAnC77P6Au+z+QfP4/AAAAACMHBgBKEQiA
IDr7P+AA8D8BAAAALBD7PwEAAAAgAAYAIwAGAAAAAABAOvs/AQAAAAAAAAAAAAAA
AAAAANEjCEABAAAA6GQIQNzN+j8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAP//P7MAAAAAAAAAAAAAAACzEQiAYDr7PwD/+j8AAAAA
AQAAAAEAAAAgAAYAIwAGAAAAAACgOvs/AQAAAPgvCEAAAAAACQAAAAAAAAAjBwYA
/////6A6+z8BAAAA+C8IQEj/+j8AAAAAAQAAAAAAAAAAAAAAwDr7PwAAAAAAAAAA
AQAAAAAAAAAAAAAAAAAAACwPCICAff4/KAAAACgAAAAAAAAAAAAAAMw6+z8AAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
mP36P6A1+z8oN/s/
oDX7P8A2+z9UgfpzkPr6Pyws+z+Y/fo/JCz7PwEAAABw/fo/cP36P5j9+j9o/fo/
GAAAACwz+z9pcGMwAIY9HgrUf/61qBQAAAAAACg3+z8AAAAAIQAGABgAAAAAAAAA
AAAAAAAAAAAAAAAAjOj6P/To+j9c6fo/AAAAAAAAAAABAAAAAAAAAHg8QD8AAAAA
SB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAADEsn4=
2CEIQK1GCEAwAAYAsxEIgGA2+z9E/fo/AAAAAIz9+j8AAAAAAQAAAAEAAACtRgiA
QDb7PwAAAACcLvs/nC77P/A6/j8AAAAAAgAAAAAAAAClpaWlpaWlpQAAAAAAAAAA
AAAAANEjCEDwOv4/6GQIQNzJ+j8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoDb7PwAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAP////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAMA2+z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqDgiAoDv+Pwws+z/kLvs/
AAAAAAAAAADMNvs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAACApb8=
/CEIQMZaCEAwAgYA1yENgJCJ+z/PAAAAAAAAAGgQ+z8BAAAAIQAGAAAAAADGWgiA
cIn7PwAAAADPAAAA9QoNgIBP+z8ACAAAAQAAAAAAAAAcKA2AUIn7P/0UAEANFQBA
+P////UjCECAT/s/5GkIQCAL+z8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAPUKDYCAT/s/AAgAAAEAAABgTQiAsIn7PwAAAAAAAAAA
aBD7PwEAAAAhAAYAAAAAAAAAAADQifs/yCENQAAAAAAjAAYAYIr7PwAAAAAAAAAA
AAAAAPCJ+z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAD8ifs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAA=
nGn7P9Bn+z+Uafs/
0Gf7PzBp+z8AAAAAmCz7P5gs+z+cafs/kCz7PxgAAADQYPs/0GD7P5xp+z/IYPs/
AQAAAJhh+z9UbXIgU3ZjAO3yYwslr9QAAAAAAJRp+z8AAAAAIQAGAAEAAAAAAAAA
AAAAAAAAAAAAAAAArOj6PxTp+j986fo/AAAAAAAAAAABAAAAAAAAAFgvQD8AAAAA
SB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAD/LIY=
/CEIQOobCEAwAgYAmGcIgJBo+z8AAAAAECz7P+xg+z8AAAAAAAAAACMABgDqGwiA
cGj7P9wA8D8BAAAAWBD7P6wu+z8DAAAAIwAGAAAAAACQaPs/AAAAAAAAAAAAAAAA
AAAAAPUjCECsLvs/5GkIQGDq+j8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACLaAiAsGj7P7gu+z8AAAAA
AAAAAHxoCEAAAAAAAAAAAGBNCIDgaPs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AQAAAAEAAAAhAAYAAAAAAAAAAAAQafs/fGgIQAAAAAABAAAAAAAAAAAAAAAAAAAA
IwAGAGRQ+z8AAAAAAQAAAAAAAAAwafs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPGn7PwAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAA=
qPj6PwD3+j+g+Po/
APf6P0D4+j/Ae/kysDj7P0Qz+z+o+Po/NCz7PwMAAAB86vo/fOr6P6j4+j906vo/
FgAAAKTq+j9lc3BfdGltZXIACf0y2LkAAAAAAKD4+j8AAAAAIQAGABYAAAAAAAAA
AAAAAAAAAAAAAAAArOj6PxTp+j986fo/AAAAAAAAAAABAAAAAAAAAFgvQD8AAAAA
SB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAACndYo=
/CEIQO1LCEAwAAYAVw0NgMD3+j9Q6vo/AAAAAJjq+j8AAAAAAQAAAAAAAADtSwiA
oPf6PwAAAACsLvs/rC77P0D++j8DAAAAIw4GAAAAAAClpaWlpaWlpQAAAAAAAAAA
AAAAAPUjCEBA/vo/5GkIQHB5+j8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAABEDQ1AAAAAAAAAAABgTQiAAPj6PwAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAP////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAACD4+j9EDQ1AAAAAACMABgCo+Po/AAAAAAEAAAAAAAAAQPj6PwAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEz4+j8AAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
qDj7P+A2+z+gOPs/
4Db7P0A4+z91SCX+PCz7P7D4+j+oOPs/NCz7PwEAAADM//o/zP/6P6g4+z/E//o/
GAAAAKQ0+z9pcGMxALIBlPDJy90p94EAAQAAAKA4+z8AAAAAIQAGABgAAAAAAAAA
AAAAAAAAAAAAAAAArOj6PxTp+j986fo/AAAAAAAAAAABAAAAAAAAAFgvQD8AAAAA
SB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAACbtaM=
/CEIQOobCEAwCAYA7UsIgKA3+z8BAAAArC77P7Au+z8KAAAAAACAABwA9D/qGwiA
gDf7P+AA8D8BAAAAWBD7PwEAAAAgCAYAIwAGAAAAAACgN/s/AQAAAAAAAAAAAAAA
AAAAAPUjCEABAAAA5GkIQHC5+j8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAP//P7MAAAAAAAAAAAAAAAB3IAiAwDf7P6D/+j8AAAAA
AQAAAAEAAAAgCAYAIwAGAGBNCIAAOPs/AQAAAJQ2CEAAAAAABwAAAAAAgAAcAPQ/
/////wA4+z8BAAAAlDYIQOj/+j8AAAAAAQAAAAAAAAAAAAAAIDj7P0ggCEABAAAA
AQAAAKg4+z8AAAAAAAAAAAAAAABAOPs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
oA8IgIB9/j8oAAAAKAAAAAAAAAAAAAAATDj7PwAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAA==
PDP7P/D9+j+Y//o/
8P36PzD/+j/93uTnsPj6Pzws+z88M/s/NCz7PwEAAAB0+/o/dPv6Pzwz+z9s+/o/
GAAAAJz7+j9pcGMwAAEM7FM1T1a8hf4AAAAAAJj/+j8AAAAAIQAGABgAAAAAAAAA
AAAAAAAAAAAAAAAArOj6PxTp+j986fo/AAAAAAAAAAABAAAAAAAAAFgvQD8AAAAA
SB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAACwrJk=
/CEIQO1LCEAwDgYAdyAIgLD++j9I+/o/AAAAAJD7+j8AAAAAAQAAAAIAAADtSwiA
kP76PwAAAACsLvs/rC77P83NAAABAAAAAAAAAAAAAAClpaWlpaWlpQAAAAAAAAAA
AAAAAPUjCEDNzQAA5GkIQGCA+j8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAABIIAhAAAAAAAAAAABgTQiA8P76PwAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAP////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAABD/+j9IIAhAAAAAACMDBgA8M/s/AQAAAAEAAAAAAAAAMP/6PwAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAB4PCICQO/4/HCz7P/Au+z8AAAAAAAAAADz/+j8AAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -300,7 +300,12 @@ extern void vPortCleanUpTCB ( void *pxTCB );
#define configXT_BOARD 1 /* Board mode */
#define configXT_SIMULATOR 0
#define configENABLE_TASK_SNAPSHOT 1
#if CONFIG_ESP32_ENABLE_COREDUMP
#define configENABLE_TASK_SNAPSHOT 1
#endif
#ifndef configENABLE_TASK_SNAPSHOT
#define configENABLE_TASK_SNAPSHOT 1
#endif
#if CONFIG_SYSVIEW_ENABLE
#ifndef __ASSEMBLER__

View file

@ -69,19 +69,26 @@ static spi_flash_counters_t s_flash_stats;
#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS
static esp_err_t spi_flash_translate_rc(esp_rom_spiflash_result_t rc);
static bool is_safe_write_address(size_t addr, size_t size);
const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_default_ops = {
.start = spi_flash_disable_interrupts_caches_and_other_cpu,
.end = spi_flash_enable_interrupts_caches_and_other_cpu,
.op_lock = spi_flash_op_lock,
.op_unlock = spi_flash_op_unlock
.start = spi_flash_disable_interrupts_caches_and_other_cpu,
.end = spi_flash_enable_interrupts_caches_and_other_cpu,
.op_lock = spi_flash_op_lock,
.op_unlock = spi_flash_op_unlock,
#if !CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED
.is_safe_write_address = is_safe_write_address
#endif
};
const DRAM_ATTR spi_flash_guard_funcs_t g_flash_guard_no_os_ops = {
.start = spi_flash_disable_interrupts_caches_and_other_cpu_no_os,
.end = spi_flash_enable_interrupts_caches_no_os,
.op_lock = 0,
.op_unlock = 0
.start = spi_flash_disable_interrupts_caches_and_other_cpu_no_os,
.end = spi_flash_enable_interrupts_caches_no_os,
.op_lock = 0,
.op_unlock = 0,
#if !CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED
.is_safe_write_address = 0
#endif
};
static const spi_flash_guard_funcs_t *s_flash_guard_ops;
@ -100,7 +107,7 @@ static const spi_flash_guard_funcs_t *s_flash_guard_ops;
#define CHECK_WRITE_ADDRESS(ADDR, SIZE)
#else /* FAILS or ABORTS */
#define CHECK_WRITE_ADDRESS(ADDR, SIZE) do { \
if (!is_safe_write_address(ADDR, SIZE)) { \
if (s_flash_guard_ops && s_flash_guard_ops->is_safe_write_address && !s_flash_guard_ops->is_safe_write_address(ADDR, SIZE)) { \
return ESP_ERR_INVALID_ARG; \
} \
} while(0)

View file

@ -313,6 +313,10 @@ typedef void (*spi_flash_op_lock_func_t)(void);
* @brief SPI flash operation unlock function.
*/
typedef void (*spi_flash_op_unlock_func_t)(void);
/**
* @brief Function to protect SPI flash critical regions corruption.
*/
typedef bool (*spi_flash_is_safe_write_address_t)(size_t addr, size_t size);
/**
* Structure holding SPI flash access critical sections management functions.
@ -332,6 +336,9 @@ typedef void (*spi_flash_op_unlock_func_t)(void);
* - 'op_unlock' unlocks access to flash API internal data.
* These two functions are recursive and can be used around the outside of multiple calls to
* 'start' & 'end', in order to create atomic multi-part flash operations.
* 3) When CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is disabled, flash writing/erasing
* API checks for addresses provided by user to avoid corruption of critical flash regions
* (bootloader, partition table, running application etc.).
*
* Different versions of the guarding functions should be used depending on the context of
* execution (with or without functional OS). In normal conditions when flash API is called
@ -343,10 +350,13 @@ typedef void (*spi_flash_op_unlock_func_t)(void);
* For example structure can be placed in DRAM and functions in IRAM sections.
*/
typedef struct {
spi_flash_guard_start_func_t start; /**< critical section start function. */
spi_flash_guard_end_func_t end; /**< critical section end function. */
spi_flash_op_lock_func_t op_lock; /**< flash access API lock function.*/
spi_flash_op_unlock_func_t op_unlock; /**< flash access API unlock function.*/
spi_flash_guard_start_func_t start; /**< critical section start function. */
spi_flash_guard_end_func_t end; /**< critical section end function. */
spi_flash_op_lock_func_t op_lock; /**< flash access API lock function.*/
spi_flash_op_unlock_func_t op_unlock; /**< flash access API unlock function.*/
#if !CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED
spi_flash_is_safe_write_address_t is_safe_write_address; /**< checks flash write addresses.*/
#endif
} spi_flash_guard_funcs_t;
/**
@ -359,7 +369,6 @@ typedef struct {
*/
void spi_flash_guard_set(const spi_flash_guard_funcs_t* funcs);
/**
* @brief Get the guard functions used for flash access
*

View file

@ -18,15 +18,15 @@ Configuration
There are a number of core dump related configuration options which user can choose in configuration menu of the application (`make menuconfig`).
1. Core dump data destination (`Components -> ESP32-specific config -> Core dump destination`):
1. Core dump data destination (`Components -> ESP32-specific config -> Core dump -> Data destination`):
* Disable core dump generation
* Save core dump to flash
* Print core dump to UART
2. Logging level of core dump module (`Components -> ESP32-specific config -> Core dump module logging level`). Value is a number from 0 (no output) to 5 (most verbose).
2. Maximum number of tasks snapshots in core dump (`Components -> ESP32-specific config -> Core dump -> Maximum number of tasks`).
3. Delay before core dump will be printed to UART (`Components -> ESP32-specific config -> Core dump print to UART delay`). Value is in ms.
3. Delay before core dump is printed to UART (`Components -> ESP32-specific config -> Core dump -> Delay before print to UART`). Value is in ms.
Save core dump to flash
@ -89,6 +89,7 @@ Generic command syntax:
* info_corefile. Retrieve core dump and print useful info.
* dbg_corefile. Retrieve core dump and start GDB session with it.
:Command Arguments:
* --debug,-d DEBUG. Log level (0..3).
* --gdb,-g GDB. Path to gdb to use for data retrieval.
* --core,-c CORE. Path to core dump file to use (if skipped core dump will be read from flash).
* --core-format,-t CORE_FORMAT. Specifies that file passed with "-c" is an ELF ("elf"), dumped raw binary ("raw") or base64-encoded ("b64") format.