Merge branch 'feature/coredump_data_in_elf_format' into 'master'
coredump: change data format to ELF Closes IDF-43 See merge request espressif/esp-idf!4398
This commit is contained in:
commit
f64ee5aba3
18 changed files with 3911 additions and 1203 deletions
|
@ -1,8 +1,9 @@
|
||||||
idf_component_register(SRCS "src/core_dump_common.c"
|
idf_component_register(SRCS "src/core_dump_common.c"
|
||||||
"src/core_dump_flash.c"
|
"src/core_dump_flash.c"
|
||||||
"src/core_dump_port.c"
|
"src/core_dump_port.c"
|
||||||
"src/core_dump_uart.c"
|
"src/core_dump_uart.c"
|
||||||
|
"src/core_dump_elf.c"
|
||||||
INCLUDE_DIRS "include"
|
INCLUDE_DIRS "include"
|
||||||
PRIV_INCLUDE_DIRS "include_core_dump"
|
PRIV_INCLUDE_DIRS "include_core_dump"
|
||||||
LDFRAGMENTS linker.lf
|
LDFRAGMENTS linker.lf
|
||||||
PRIV_REQUIRES spi_flash soc)
|
PRIV_REQUIRES spi_flash app_update mbedtls esp_rom soc)
|
||||||
|
|
|
@ -20,6 +20,35 @@ menu "Core dump"
|
||||||
bool "None"
|
bool "None"
|
||||||
endchoice
|
endchoice
|
||||||
|
|
||||||
|
choice ESP32_COREDUMP_DATA_FORMAT
|
||||||
|
prompt "Core dump data format"
|
||||||
|
default ESP32_COREDUMP_DATA_FORMAT_ELF
|
||||||
|
depends on !ESP32_ENABLE_COREDUMP_TO_NONE
|
||||||
|
help
|
||||||
|
Select the data format for core dump.
|
||||||
|
config ESP32_COREDUMP_DATA_FORMAT_BIN
|
||||||
|
bool "Binary format"
|
||||||
|
select ESP32_ENABLE_COREDUMP
|
||||||
|
config ESP32_COREDUMP_DATA_FORMAT_ELF
|
||||||
|
bool "ELF format"
|
||||||
|
select ESP32_ENABLE_COREDUMP
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
choice ESP32_COREDUMP_CHECKSUM
|
||||||
|
prompt "Core dump data integrity check"
|
||||||
|
default ESP32_COREDUMP_CHECKSUM_CRC32
|
||||||
|
depends on !ESP32_ENABLE_COREDUMP_TO_NONE
|
||||||
|
help
|
||||||
|
Select the integrity check for the core dump.
|
||||||
|
config ESP32_COREDUMP_CHECKSUM_CRC32
|
||||||
|
bool "Use CRC32 for integrity verification"
|
||||||
|
select ESP32_ENABLE_COREDUMP
|
||||||
|
config ESP32_COREDUMP_CHECKSUM_SHA256
|
||||||
|
bool "Use SHA256 for integrity verification"
|
||||||
|
select ESP32_ENABLE_COREDUMP
|
||||||
|
depends on ESP32_COREDUMP_DATA_FORMAT_ELF
|
||||||
|
endchoice
|
||||||
|
|
||||||
config ESP32_ENABLE_COREDUMP
|
config ESP32_ENABLE_COREDUMP
|
||||||
bool
|
bool
|
||||||
default F
|
default F
|
||||||
|
@ -41,5 +70,15 @@ menu "Core dump"
|
||||||
Config delay (in ms) before printing core dump to UART.
|
Config delay (in ms) before printing core dump to UART.
|
||||||
Delay can be interrupted by pressing Enter key.
|
Delay can be interrupted by pressing Enter key.
|
||||||
|
|
||||||
|
config ESP32_CORE_DUMP_STACK_SIZE
|
||||||
|
int "Reserved stack size"
|
||||||
|
depends on ESP32_ENABLE_COREDUMP
|
||||||
|
default 0
|
||||||
|
help
|
||||||
|
Size of the memory to be reserved for core dump stack. If 0 core dump process will run on
|
||||||
|
the stack of crashed task/ISR, otherwise special stack will be allocated.
|
||||||
|
To ensure that core dump itself will not overflow task/ISR stack set this to the value above 800.
|
||||||
|
NOTE: It eats DRAM.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -70,7 +70,6 @@ void esp_core_dump_to_flash(XtExcFrame *frame);
|
||||||
*/
|
*/
|
||||||
void esp_core_dump_to_uart(XtExcFrame *frame);
|
void esp_core_dump_to_uart(XtExcFrame *frame);
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************************/
|
/**************************************************************************************/
|
||||||
/*********************************** USER MODE API ************************************/
|
/*********************************** USER MODE API ************************************/
|
||||||
/**************************************************************************************/
|
/**************************************************************************************/
|
||||||
|
|
21
components/espcoredump/include_core_dump/core_dump_elf.h
Normal file
21
components/espcoredump/include_core_dump/core_dump_elf.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2015-2019 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_ELF_H_
|
||||||
|
#define ESP_CORE_DUMP_ELF_H_
|
||||||
|
|
||||||
|
#include "esp_core_dump_priv.h"
|
||||||
|
|
||||||
|
esp_err_t esp_core_dump_write_elf(void *frame, core_dump_write_config_t *write_cfg);
|
||||||
|
|
||||||
|
#endif
|
359
components/espcoredump/include_core_dump/elf.h
Normal file
359
components/espcoredump/include_core_dump/elf.h
Normal file
|
@ -0,0 +1,359 @@
|
||||||
|
/****************************************************************************
|
||||||
|
****************************************************************************
|
||||||
|
***
|
||||||
|
*** This header was automatically generated from a Linux kernel header
|
||||||
|
*** of the same name, to make information necessary for userspace to
|
||||||
|
*** call into the kernel available to libc. It contains only constants,
|
||||||
|
*** structures, and macros generated from the original header, and thus,
|
||||||
|
*** contains no copyrightable information.
|
||||||
|
***
|
||||||
|
****************************************************************************
|
||||||
|
****************************************************************************/
|
||||||
|
#ifndef _LINUX_ELF_H
|
||||||
|
#define _LINUX_ELF_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifndef elf_read_implies_exec
|
||||||
|
#define elf_read_implies_exec(ex, have_pt_gnu_stack) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef uint32_t Elf32_Addr;
|
||||||
|
typedef uint16_t Elf32_Half;
|
||||||
|
typedef uint32_t Elf32_Off;
|
||||||
|
typedef int32_t Elf32_Sword;
|
||||||
|
typedef uint32_t Elf32_Word;
|
||||||
|
|
||||||
|
typedef uint64_t Elf64_Addr;
|
||||||
|
typedef uint16_t Elf64_Half;
|
||||||
|
typedef int16_t Elf64_SHalf;
|
||||||
|
typedef uint64_t Elf64_Off;
|
||||||
|
typedef int32_t Elf64_Sword;
|
||||||
|
typedef uint32_t Elf64_Word;
|
||||||
|
typedef uint64_t Elf64_Xword;
|
||||||
|
typedef int64_t Elf64_Sxword;
|
||||||
|
|
||||||
|
#define PT_NULL 0
|
||||||
|
#define PT_LOAD 1
|
||||||
|
#define PT_DYNAMIC 2
|
||||||
|
#define PT_INTERP 3
|
||||||
|
#define PT_NOTE 4
|
||||||
|
#define PT_SHLIB 5
|
||||||
|
#define PT_PHDR 6
|
||||||
|
#define PT_TLS 7
|
||||||
|
#define PT_LOOS 0x60000000
|
||||||
|
#define PT_HIOS 0x6fffffff
|
||||||
|
#define PT_LOPROC 0x70000000
|
||||||
|
#define PT_HIPROC 0x7fffffff
|
||||||
|
#define PT_GNU_EH_FRAME 0x6474e550
|
||||||
|
|
||||||
|
#define PT_GNU_STACK (PT_LOOS + 0x474e551)
|
||||||
|
|
||||||
|
#define ET_NONE 0
|
||||||
|
#define ET_REL 1
|
||||||
|
#define ET_EXEC 2
|
||||||
|
#define ET_DYN 3
|
||||||
|
#define ET_CORE 4
|
||||||
|
#define ET_LOPROC 0xff00
|
||||||
|
#define ET_HIPROC 0xffff
|
||||||
|
|
||||||
|
#define DT_NULL 0
|
||||||
|
#define DT_NEEDED 1
|
||||||
|
#define DT_PLTRELSZ 2
|
||||||
|
#define DT_PLTGOT 3
|
||||||
|
#define DT_HASH 4
|
||||||
|
#define DT_STRTAB 5
|
||||||
|
#define DT_SYMTAB 6
|
||||||
|
#define DT_RELA 7
|
||||||
|
#define DT_RELASZ 8
|
||||||
|
#define DT_RELAENT 9
|
||||||
|
#define DT_STRSZ 10
|
||||||
|
#define DT_SYMENT 11
|
||||||
|
#define DT_INIT 12
|
||||||
|
#define DT_FINI 13
|
||||||
|
#define DT_SONAME 14
|
||||||
|
#define DT_RPATH 15
|
||||||
|
#define DT_SYMBOLIC 16
|
||||||
|
#define DT_REL 17
|
||||||
|
#define DT_RELSZ 18
|
||||||
|
#define DT_RELENT 19
|
||||||
|
#define DT_PLTREL 20
|
||||||
|
#define DT_DEBUG 21
|
||||||
|
#define DT_TEXTREL 22
|
||||||
|
#define DT_JMPREL 23
|
||||||
|
#define DT_LOPROC 0x70000000
|
||||||
|
#define DT_HIPROC 0x7fffffff
|
||||||
|
|
||||||
|
#define STB_LOCAL 0
|
||||||
|
#define STB_GLOBAL 1
|
||||||
|
#define STB_WEAK 2
|
||||||
|
|
||||||
|
#define STT_NOTYPE 0
|
||||||
|
#define STT_OBJECT 1
|
||||||
|
#define STT_FUNC 2
|
||||||
|
#define STT_SECTION 3
|
||||||
|
#define STT_FILE 4
|
||||||
|
#define STT_COMMON 5
|
||||||
|
#define STT_TLS 6
|
||||||
|
|
||||||
|
#define ELF_ST_BIND(x) ((x) >> 4)
|
||||||
|
#define ELF_ST_TYPE(x) (((unsigned int) x) & 0xf)
|
||||||
|
#define ELF32_ST_BIND(x) ELF_ST_BIND(x)
|
||||||
|
#define ELF32_ST_TYPE(x) ELF_ST_TYPE(x)
|
||||||
|
#define ELF64_ST_BIND(x) ELF_ST_BIND(x)
|
||||||
|
#define ELF64_ST_TYPE(x) ELF_ST_TYPE(x)
|
||||||
|
|
||||||
|
typedef struct dynamic{
|
||||||
|
Elf32_Sword d_tag;
|
||||||
|
union{
|
||||||
|
Elf32_Sword d_val;
|
||||||
|
Elf32_Addr d_ptr;
|
||||||
|
} d_un;
|
||||||
|
} Elf32_Dyn;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Elf64_Sxword d_tag;
|
||||||
|
union {
|
||||||
|
Elf64_Xword d_val;
|
||||||
|
Elf64_Addr d_ptr;
|
||||||
|
} d_un;
|
||||||
|
} Elf64_Dyn;
|
||||||
|
|
||||||
|
#define ELF32_R_SYM(x) ((x) >> 8)
|
||||||
|
#define ELF32_R_TYPE(x) ((x) & 0xff)
|
||||||
|
|
||||||
|
#define ELF64_R_SYM(i) ((i) >> 32)
|
||||||
|
#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
|
||||||
|
|
||||||
|
typedef struct elf32_rel {
|
||||||
|
Elf32_Addr r_offset;
|
||||||
|
Elf32_Word r_info;
|
||||||
|
} Elf32_Rel;
|
||||||
|
|
||||||
|
typedef struct elf64_rel {
|
||||||
|
Elf64_Addr r_offset;
|
||||||
|
Elf64_Xword r_info;
|
||||||
|
} Elf64_Rel;
|
||||||
|
|
||||||
|
typedef struct elf32_rela{
|
||||||
|
Elf32_Addr r_offset;
|
||||||
|
Elf32_Word r_info;
|
||||||
|
Elf32_Sword r_addend;
|
||||||
|
} Elf32_Rela;
|
||||||
|
|
||||||
|
typedef struct elf64_rela {
|
||||||
|
Elf64_Addr r_offset;
|
||||||
|
Elf64_Xword r_info;
|
||||||
|
Elf64_Sxword r_addend;
|
||||||
|
} Elf64_Rela;
|
||||||
|
|
||||||
|
typedef struct elf32_sym{
|
||||||
|
Elf32_Word st_name;
|
||||||
|
Elf32_Addr st_value;
|
||||||
|
Elf32_Word st_size;
|
||||||
|
unsigned char st_info;
|
||||||
|
unsigned char st_other;
|
||||||
|
Elf32_Half st_shndx;
|
||||||
|
} Elf32_Sym;
|
||||||
|
|
||||||
|
typedef struct elf64_sym {
|
||||||
|
Elf64_Word st_name;
|
||||||
|
unsigned char st_info;
|
||||||
|
unsigned char st_other;
|
||||||
|
Elf64_Half st_shndx;
|
||||||
|
Elf64_Addr st_value;
|
||||||
|
Elf64_Xword st_size;
|
||||||
|
} Elf64_Sym;
|
||||||
|
|
||||||
|
#define EI_NIDENT 16
|
||||||
|
|
||||||
|
typedef struct elf32_hdr{
|
||||||
|
unsigned char e_ident[EI_NIDENT];
|
||||||
|
Elf32_Half e_type;
|
||||||
|
Elf32_Half e_machine;
|
||||||
|
Elf32_Word e_version;
|
||||||
|
Elf32_Addr e_entry;
|
||||||
|
Elf32_Off e_phoff;
|
||||||
|
Elf32_Off e_shoff;
|
||||||
|
Elf32_Word e_flags;
|
||||||
|
Elf32_Half e_ehsize;
|
||||||
|
Elf32_Half e_phentsize;
|
||||||
|
Elf32_Half e_phnum;
|
||||||
|
Elf32_Half e_shentsize;
|
||||||
|
Elf32_Half e_shnum;
|
||||||
|
Elf32_Half e_shstrndx;
|
||||||
|
} Elf32_Ehdr;
|
||||||
|
|
||||||
|
typedef struct elf64_hdr {
|
||||||
|
unsigned char e_ident[16];
|
||||||
|
Elf64_Half e_type;
|
||||||
|
Elf64_Half e_machine;
|
||||||
|
Elf64_Word e_version;
|
||||||
|
Elf64_Addr e_entry;
|
||||||
|
Elf64_Off e_phoff;
|
||||||
|
Elf64_Off e_shoff;
|
||||||
|
Elf64_Word e_flags;
|
||||||
|
Elf64_Half e_ehsize;
|
||||||
|
Elf64_Half e_phentsize;
|
||||||
|
Elf64_Half e_phnum;
|
||||||
|
Elf64_Half e_shentsize;
|
||||||
|
Elf64_Half e_shnum;
|
||||||
|
Elf64_Half e_shstrndx;
|
||||||
|
} Elf64_Ehdr;
|
||||||
|
|
||||||
|
#define PF_R 0x4
|
||||||
|
#define PF_W 0x2
|
||||||
|
#define PF_X 0x1
|
||||||
|
|
||||||
|
typedef struct elf32_phdr{
|
||||||
|
Elf32_Word p_type;
|
||||||
|
Elf32_Off p_offset;
|
||||||
|
Elf32_Addr p_vaddr;
|
||||||
|
Elf32_Addr p_paddr;
|
||||||
|
Elf32_Word p_filesz;
|
||||||
|
Elf32_Word p_memsz;
|
||||||
|
Elf32_Word p_flags;
|
||||||
|
Elf32_Word p_align;
|
||||||
|
} Elf32_Phdr;
|
||||||
|
|
||||||
|
typedef struct elf64_phdr {
|
||||||
|
Elf64_Word p_type;
|
||||||
|
Elf64_Word p_flags;
|
||||||
|
Elf64_Off p_offset;
|
||||||
|
Elf64_Addr p_vaddr;
|
||||||
|
Elf64_Addr p_paddr;
|
||||||
|
Elf64_Xword p_filesz;
|
||||||
|
Elf64_Xword p_memsz;
|
||||||
|
Elf64_Xword p_align;
|
||||||
|
} Elf64_Phdr;
|
||||||
|
|
||||||
|
#define SHT_NULL 0
|
||||||
|
#define SHT_PROGBITS 1
|
||||||
|
#define SHT_SYMTAB 2
|
||||||
|
#define SHT_STRTAB 3
|
||||||
|
#define SHT_RELA 4
|
||||||
|
#define SHT_HASH 5
|
||||||
|
#define SHT_DYNAMIC 6
|
||||||
|
#define SHT_NOTE 7
|
||||||
|
#define SHT_NOBITS 8
|
||||||
|
#define SHT_REL 9
|
||||||
|
#define SHT_SHLIB 10
|
||||||
|
#define SHT_DYNSYM 11
|
||||||
|
#define SHT_NUM 12
|
||||||
|
#define SHT_LOPROC 0x70000000
|
||||||
|
#define SHT_HIPROC 0x7fffffff
|
||||||
|
#define SHT_LOUSER 0x80000000
|
||||||
|
#define SHT_HIUSER 0xffffffff
|
||||||
|
|
||||||
|
#define SHF_WRITE 0x1
|
||||||
|
#define SHF_ALLOC 0x2
|
||||||
|
#define SHF_EXECINSTR 0x4
|
||||||
|
#define SHF_MASKPROC 0xf0000000
|
||||||
|
|
||||||
|
#define SHN_UNDEF 0
|
||||||
|
#define SHN_LORESERVE 0xff00
|
||||||
|
#define SHN_LOPROC 0xff00
|
||||||
|
#define SHN_HIPROC 0xff1f
|
||||||
|
#define SHN_ABS 0xfff1
|
||||||
|
#define SHN_COMMON 0xfff2
|
||||||
|
#define SHN_HIRESERVE 0xffff
|
||||||
|
|
||||||
|
typedef struct elf32_shdr{
|
||||||
|
Elf32_Word sh_name;
|
||||||
|
Elf32_Word sh_type;
|
||||||
|
Elf32_Word sh_flags;
|
||||||
|
Elf32_Addr sh_addr;
|
||||||
|
Elf32_Off sh_offset;
|
||||||
|
Elf32_Word sh_size;
|
||||||
|
Elf32_Word sh_link;
|
||||||
|
Elf32_Word sh_info;
|
||||||
|
Elf32_Word sh_addralign;
|
||||||
|
Elf32_Word sh_entsize;
|
||||||
|
} Elf32_Shdr;
|
||||||
|
|
||||||
|
typedef struct elf64_shdr {
|
||||||
|
Elf64_Word sh_name;
|
||||||
|
Elf64_Word sh_type;
|
||||||
|
Elf64_Xword sh_flags;
|
||||||
|
Elf64_Addr sh_addr;
|
||||||
|
Elf64_Off sh_offset;
|
||||||
|
Elf64_Xword sh_size;
|
||||||
|
Elf64_Word sh_link;
|
||||||
|
Elf64_Word sh_info;
|
||||||
|
Elf64_Xword sh_addralign;
|
||||||
|
Elf64_Xword sh_entsize;
|
||||||
|
} Elf64_Shdr;
|
||||||
|
|
||||||
|
#define EI_MAG0 0
|
||||||
|
#define EI_MAG1 1
|
||||||
|
#define EI_MAG2 2
|
||||||
|
#define EI_MAG3 3
|
||||||
|
#define EI_CLASS 4
|
||||||
|
#define EI_DATA 5
|
||||||
|
#define EI_VERSION 6
|
||||||
|
#define EI_OSABI 7
|
||||||
|
#define EI_PAD 8
|
||||||
|
|
||||||
|
#define ELFMAG0 0x7f
|
||||||
|
#define ELFMAG1 'E'
|
||||||
|
#define ELFMAG2 'L'
|
||||||
|
#define ELFMAG3 'F'
|
||||||
|
#define ELFMAG "\177ELF"
|
||||||
|
#define SELFMAG 4
|
||||||
|
|
||||||
|
#define ELFCLASSNONE 0
|
||||||
|
#define ELFCLASS32 1
|
||||||
|
#define ELFCLASS64 2
|
||||||
|
#define ELFCLASSNUM 3
|
||||||
|
|
||||||
|
#define ELFDATANONE 0
|
||||||
|
#define ELFDATA2LSB 1
|
||||||
|
#define ELFDATA2MSB 2
|
||||||
|
|
||||||
|
#define EV_NONE 0
|
||||||
|
#define EV_CURRENT 1
|
||||||
|
#define EV_NUM 2
|
||||||
|
|
||||||
|
#define ELFOSABI_NONE 0
|
||||||
|
#define ELFOSABI_LINUX 3
|
||||||
|
|
||||||
|
#ifndef ELF_OSABI
|
||||||
|
#define ELF_OSABI ELFOSABI_NONE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NT_PRSTATUS 1
|
||||||
|
#define NT_PRFPREG 2
|
||||||
|
#define NT_PRPSINFO 3
|
||||||
|
#define NT_TASKSTRUCT 4
|
||||||
|
#define NT_AUXV 6
|
||||||
|
#define NT_PRXFPREG 0x46e62b7f
|
||||||
|
|
||||||
|
typedef struct elf32_note {
|
||||||
|
Elf32_Word n_namesz;
|
||||||
|
Elf32_Word n_descsz;
|
||||||
|
Elf32_Word n_type;
|
||||||
|
} Elf32_Nhdr;
|
||||||
|
|
||||||
|
typedef struct elf64_note {
|
||||||
|
Elf64_Word n_namesz;
|
||||||
|
Elf64_Word n_descsz;
|
||||||
|
Elf64_Word n_type;
|
||||||
|
} Elf64_Nhdr;
|
||||||
|
|
||||||
|
#if ELF_CLASS == ELFCLASS32
|
||||||
|
|
||||||
|
#define elfhdr Elf32_Ehdr
|
||||||
|
#define elf_phdr Elf32_Phdr
|
||||||
|
#define elf_note Elf32_Nhdr
|
||||||
|
#define elf_shdr Elf32_Shdr
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define elfhdr Elf64_Ehdr
|
||||||
|
#define elf_phdr Elf64_Phdr
|
||||||
|
#define elf_note Elf64_Nhdr
|
||||||
|
#define elf_shdr Elf64_Shdr
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
122
components/espcoredump/include_core_dump/esp_core_dump_port.h
Normal file
122
components/espcoredump/include_core_dump/esp_core_dump_port.h
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
// Copyright 2015-2019 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_PORT_H_
|
||||||
|
#define ESP_CORE_DUMP_PORT_H_
|
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#if CONFIG_ESP32_COREDUMP_CHECKSUM_CRC32
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
#include "esp32/rom/crc.h"
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32S2BETA
|
||||||
|
#include "esp32s2beta/rom/crc.h"
|
||||||
|
#endif
|
||||||
|
#elif CONFIG_ESP32_COREDUMP_CHECKSUM_SHA256
|
||||||
|
#include "mbedtls/sha256.h"
|
||||||
|
#endif
|
||||||
|
#include "esp_core_dump_priv.h"
|
||||||
|
#include "soc/cpu.h"
|
||||||
|
#include "esp_debug_helpers.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define COREDUMP_TCB_SIZE sizeof(StaticTask_t)
|
||||||
|
|
||||||
|
// Gets RTOS tasks snapshot
|
||||||
|
uint32_t esp_core_dump_get_tasks_snapshot(core_dump_task_header_t* const tasks,
|
||||||
|
const uint32_t snapshot_size);
|
||||||
|
|
||||||
|
// Checks TCB consistency
|
||||||
|
bool esp_core_dump_tcb_addr_is_sane(uint32_t addr);
|
||||||
|
// Checks stack address
|
||||||
|
bool esp_core_dump_task_stack_end_is_sane(uint32_t sp);
|
||||||
|
bool esp_core_dump_mem_seg_is_sane(uint32_t addr, uint32_t sz);
|
||||||
|
void *esp_core_dump_get_current_task_handle(void);
|
||||||
|
bool esp_core_dump_check_task(void *frame, core_dump_task_header_t *task_snaphort, bool* is_current, bool* stack_is_valid);
|
||||||
|
bool esp_core_dump_check_stack(uint32_t stack_start, uint32_t stack_end);
|
||||||
|
uint32_t esp_core_dump_get_stack(core_dump_task_header_t* task_snapshot, uint32_t* stk_base, uint32_t* stk_len);
|
||||||
|
|
||||||
|
uint16_t esp_core_dump_get_arch_id(void);
|
||||||
|
uint32_t esp_core_dump_get_task_regs_dump(core_dump_task_header_t *task, void **reg_dump);
|
||||||
|
void esp_core_dump_init_extra_info(void);
|
||||||
|
uint32_t esp_core_dump_get_extra_info(void **info);
|
||||||
|
|
||||||
|
// Data integrity check functions
|
||||||
|
void esp_core_dump_checksum_init(core_dump_write_data_t* wr_data);
|
||||||
|
void esp_core_dump_checksum_update(core_dump_write_data_t* wr_data, void* data, size_t data_len);
|
||||||
|
size_t esp_core_dump_checksum_finish(core_dump_write_data_t* wr_data, void** chs_ptr);
|
||||||
|
|
||||||
|
#if CONFIG_ESP32_COREDUMP_CHECKSUM_SHA256
|
||||||
|
void esp_core_dump_print_sha256(const char* msg, const uint8_t* sha_output);
|
||||||
|
int esp_core_dump_sha(mbedtls_sha256_context *ctx,
|
||||||
|
const unsigned char *input, size_t ilen, unsigned char output[32]);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define esp_core_dump_in_isr_context() xPortInterruptedFromISRContext()
|
||||||
|
uint32_t esp_core_dump_get_isr_stack_end(void);
|
||||||
|
|
||||||
|
#if CONFIG_ESP32_CORE_DUMP_STACK_SIZE > 0
|
||||||
|
#if LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG
|
||||||
|
// increase stack size in verbose mode
|
||||||
|
#define ESP32_CORE_DUMP_STACK_SIZE (CONFIG_ESP32_CORE_DUMP_STACK_SIZE+100)
|
||||||
|
#else
|
||||||
|
#define ESP32_CORE_DUMP_STACK_SIZE CONFIG_ESP32_CORE_DUMP_STACK_SIZE
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void esp_core_dump_report_stack_usage(void);
|
||||||
|
|
||||||
|
#if ESP32_CORE_DUMP_STACK_SIZE > 0
|
||||||
|
#define COREDUMP_STACK_FILL_BYTE (0xa5U)
|
||||||
|
extern uint8_t s_coredump_stack[];
|
||||||
|
extern uint8_t *s_core_dump_sp;
|
||||||
|
|
||||||
|
#if LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG
|
||||||
|
#define esp_core_dump_fill_stack() \
|
||||||
|
memset(s_coredump_stack, COREDUMP_STACK_FILL_BYTE, ESP32_CORE_DUMP_STACK_SIZE)
|
||||||
|
#else
|
||||||
|
#define esp_core_dump_fill_stack()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define esp_core_dump_setup_stack() \
|
||||||
|
{ \
|
||||||
|
s_core_dump_sp = (uint8_t *)((uint32_t)(s_coredump_stack + ESP32_CORE_DUMP_STACK_SIZE - 1) & ~0xf); \
|
||||||
|
esp_core_dump_fill_stack(); \
|
||||||
|
/* watchpoint 1 can be used for task stack overflow detection, re-use it, it is no more necessary */ \
|
||||||
|
esp_clear_watchpoint(1); \
|
||||||
|
esp_set_watchpoint(1, s_coredump_stack, 1, ESP_WATCHPOINT_STORE); \
|
||||||
|
asm volatile ("mov sp, %0" :: "r"(s_core_dump_sp)); \
|
||||||
|
ESP_COREDUMP_LOGD("Use core dump stack @ 0x%x", get_sp()); \
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define esp_core_dump_setup_stack() \
|
||||||
|
{ \
|
||||||
|
/* if we are in ISR set watchpoint to the end of ISR stack */ \
|
||||||
|
if (xPortInterruptedFromISRContext()) { \
|
||||||
|
extern uint8_t port_IntStack; \
|
||||||
|
esp_clear_watchpoint(1); \
|
||||||
|
esp_set_watchpoint(1, &port_IntStack+xPortGetCoreID()*configISR_STACK_SIZE, 1, ESP_WATCHPOINT_STORE); \
|
||||||
|
} else { \
|
||||||
|
/* for tasks user should enable stack overflow detection in menuconfig
|
||||||
|
TODO: if not enabled in menuconfig enable it ourselves */ \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -14,10 +14,18 @@
|
||||||
#ifndef ESP_CORE_DUMP_H_
|
#ifndef ESP_CORE_DUMP_H_
|
||||||
#define ESP_CORE_DUMP_H_
|
#define ESP_CORE_DUMP_H_
|
||||||
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#ifdef __cplusplus
|
||||||
#include "freertos/task.h"
|
extern "C" {
|
||||||
#include "sdkconfig.h"
|
#endif
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_attr.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#if CONFIG_ESP32_COREDUMP_CHECKSUM_SHA256
|
||||||
|
// TODO: move this to portable part of the code
|
||||||
|
#include "mbedtls/sha256.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#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_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_LOGE( format, ... ) ESP_COREDUMP_LOG(ESP_LOG_ERROR, LOG_FORMAT(E, format), ##__VA_ARGS__)
|
||||||
|
@ -33,18 +41,50 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define COREDUMP_MAX_TASK_STACK_SIZE (64*1024)
|
#define COREDUMP_MAX_TASK_STACK_SIZE (64*1024)
|
||||||
#define COREDUMP_VERSION 1
|
#define COREDUMP_VERSION_BIN 1
|
||||||
|
#define COREDUMP_VERSION_ELF_CRC32 2
|
||||||
|
#define COREDUMP_VERSION_ELF_SHA256 3
|
||||||
|
#define COREDUMP_CURR_TASK_MARKER 0xDEADBEEF
|
||||||
|
#define COREDUMP_CURR_TASK_NOT_FOUND -1
|
||||||
|
|
||||||
typedef uint32_t core_dump_crc_t;
|
#if CONFIG_ESP32_COREDUMP_DATA_FORMAT_ELF
|
||||||
|
#if CONFIG_ESP32_COREDUMP_CHECKSUM_CRC32
|
||||||
#if CONFIG_ESP32_ENABLE_COREDUMP
|
#define COREDUMP_VERSION COREDUMP_VERSION_ELF_CRC32
|
||||||
|
#elif CONFIG_ESP32_COREDUMP_CHECKSUM_SHA256
|
||||||
|
#define COREDUMP_VERSION COREDUMP_VERSION_ELF_SHA256
|
||||||
|
#define COREDUMP_SHA256_LEN 32
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define COREDUMP_VERSION COREDUMP_VERSION_BIN
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef esp_err_t (*esp_core_dump_write_prepare_t)(void *priv, uint32_t *data_len);
|
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);
|
typedef esp_err_t (*esp_core_dump_write_start_t)(void *priv);
|
||||||
typedef esp_err_t (*esp_core_dump_write_end_t)(void *priv);
|
typedef esp_err_t (*esp_core_dump_write_end_t)(void *priv);
|
||||||
typedef esp_err_t (*esp_core_dump_flash_write_data_t)(void *priv, void * data, uint32_t data_len);
|
typedef esp_err_t (*esp_core_dump_flash_write_data_t)(void *priv, void * data, uint32_t data_len);
|
||||||
|
|
||||||
/** core dump emitter control structure */
|
typedef uint32_t core_dump_crc_t;
|
||||||
|
|
||||||
|
typedef struct _core_dump_write_data_t
|
||||||
|
{
|
||||||
|
// TODO: move flash related data to flash-specific code
|
||||||
|
uint32_t off; // current offset in partition
|
||||||
|
union
|
||||||
|
{
|
||||||
|
uint8_t data8[4];
|
||||||
|
uint32_t data32;
|
||||||
|
} cached_data;
|
||||||
|
uint8_t cached_bytes;
|
||||||
|
#if CONFIG_ESP32_COREDUMP_CHECKSUM_SHA256
|
||||||
|
// TODO: move this to portable part of the code
|
||||||
|
mbedtls_sha256_context ctx;
|
||||||
|
char sha_output[COREDUMP_SHA256_LEN];
|
||||||
|
#elif CONFIG_ESP32_COREDUMP_CHECKSUM_CRC32
|
||||||
|
core_dump_crc_t crc; // CRC of dumped data
|
||||||
|
#endif
|
||||||
|
} core_dump_write_data_t;
|
||||||
|
|
||||||
|
// core dump emitter control structure
|
||||||
typedef struct _core_dump_write_config_t
|
typedef struct _core_dump_write_config_t
|
||||||
{
|
{
|
||||||
// this function is called before core dump data writing
|
// this function is called before core dump data writing
|
||||||
|
@ -69,37 +109,34 @@ typedef struct _core_dump_header_t
|
||||||
uint32_t version; // core dump struct version
|
uint32_t version; // core dump struct version
|
||||||
uint32_t tasks_num; // number of tasks
|
uint32_t tasks_num; // number of tasks
|
||||||
uint32_t tcb_sz; // size of TCB
|
uint32_t tcb_sz; // size of TCB
|
||||||
|
uint32_t mem_segs_num; // number of memory segments
|
||||||
} core_dump_header_t;
|
} core_dump_header_t;
|
||||||
|
|
||||||
/** core dump task data header */
|
/** core dump task data header */
|
||||||
typedef struct _core_dump_task_header_t
|
typedef struct _core_dump_task_header_t
|
||||||
{
|
{
|
||||||
void * tcb_addr; // TCB address
|
void* tcb_addr; // TCB address
|
||||||
uint32_t stack_start; // stack start address
|
uint32_t stack_start; // stack start address
|
||||||
uint32_t stack_end; // stack end address
|
uint32_t stack_end; // stack end address
|
||||||
} core_dump_task_header_t;
|
} core_dump_task_header_t;
|
||||||
|
|
||||||
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
|
/** core dump memory segment header */
|
||||||
|
typedef struct _core_dump_mem_seg_header_t
|
||||||
|
{
|
||||||
|
uint32_t start; // memory region start address
|
||||||
|
uint32_t size; // memory region size
|
||||||
|
} core_dump_mem_seg_header_t;
|
||||||
|
|
||||||
// Core dump flash init function
|
// Core dump flash init function
|
||||||
void esp_core_dump_flash_init(void);
|
void esp_core_dump_flash_init(void);
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Common core dump write function
|
// Common core dump write function
|
||||||
void esp_core_dump_write(void *frame, core_dump_write_config_t *write_cfg);
|
void esp_core_dump_write(void *frame, core_dump_write_config_t *write_cfg);
|
||||||
|
|
||||||
// Gets RTOS tasks snapshot
|
#include "esp_core_dump_port.h"
|
||||||
uint32_t esp_core_dump_get_tasks_snapshot(core_dump_task_header_t* const tasks,
|
|
||||||
const uint32_t snapshot_size, uint32_t* const tcb_sz);
|
|
||||||
|
|
||||||
// Checks TCB consistency
|
|
||||||
bool esp_tcb_addr_is_sane(uint32_t addr, uint32_t sz);
|
|
||||||
|
|
||||||
bool esp_core_dump_process_tcb(void *frame, core_dump_task_header_t *task_snaphort, uint32_t tcb_sz);
|
|
||||||
|
|
||||||
bool esp_core_dump_process_stack(core_dump_task_header_t* task_snaphort, uint32_t *length);
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -6,6 +6,7 @@ entries:
|
||||||
core_dump_flash (noflash_text)
|
core_dump_flash (noflash_text)
|
||||||
core_dump_common (noflash_text)
|
core_dump_common (noflash_text)
|
||||||
core_dump_port (noflash_text)
|
core_dump_port (noflash_text)
|
||||||
|
core_dump_elf (noflash_text)
|
||||||
else:
|
else:
|
||||||
* (default)
|
* (default)
|
||||||
|
|
||||||
|
@ -17,3 +18,14 @@ entries:
|
||||||
esp_flash_spi_init (noflash_text)
|
esp_flash_spi_init (noflash_text)
|
||||||
else:
|
else:
|
||||||
* (default)
|
* (default)
|
||||||
|
|
||||||
|
[mapping:mbedtls]
|
||||||
|
archive: libmbedtls.a
|
||||||
|
entries:
|
||||||
|
if ESP32_COREDUMP_CHECKSUM_SHA256 = y :
|
||||||
|
if MBEDTLS_HARDWARE_SHA = n:
|
||||||
|
sha256 (noflash_text)
|
||||||
|
else:
|
||||||
|
esp_sha256 (noflash_text)
|
||||||
|
else:
|
||||||
|
* (default)
|
||||||
|
|
|
@ -13,145 +13,248 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "esp32/rom/crc.h"
|
|
||||||
#include "esp_debug_helpers.h"
|
|
||||||
#include "esp_partition.h"
|
#include "esp_partition.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
#include "esp_core_dump_priv.h"
|
#include "esp_core_dump_priv.h"
|
||||||
|
#include "core_dump_elf.h"
|
||||||
|
|
||||||
const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_common";
|
const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_common";
|
||||||
|
|
||||||
#if CONFIG_ESP32_ENABLE_COREDUMP
|
#if CONFIG_ESP32_COREDUMP_DATA_FORMAT_BIN
|
||||||
|
|
||||||
|
static inline uint32_t esp_core_dump_get_tcb_len()
|
||||||
|
{
|
||||||
|
if (COREDUMP_TCB_SIZE % sizeof(uint32_t)) {
|
||||||
|
return ((COREDUMP_TCB_SIZE / sizeof(uint32_t) + 1) * sizeof(uint32_t));
|
||||||
|
}
|
||||||
|
return COREDUMP_TCB_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t esp_core_dump_get_stack_len(uint32_t stack_start, uint32_t stack_end)
|
||||||
|
{
|
||||||
|
uint32_t len = stack_end - stack_start;
|
||||||
|
// Take stack padding into account
|
||||||
|
return (len + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_core_dump_save_task(core_dump_write_config_t *write_cfg,
|
||||||
|
core_dump_task_header_t *task)
|
||||||
|
{
|
||||||
|
esp_err_t err = ESP_FAIL;
|
||||||
|
uint32_t stk_vaddr, stk_len;
|
||||||
|
uint32_t stk_paddr = esp_core_dump_get_stack(task, &stk_vaddr, &stk_len);
|
||||||
|
|
||||||
|
stk_len = esp_core_dump_get_stack_len(stk_vaddr, stk_vaddr+stk_len);
|
||||||
|
|
||||||
|
// Save TCB address, stack base and stack top addr
|
||||||
|
err = write_cfg->write(write_cfg->priv, (void*)task, sizeof(core_dump_task_header_t));
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_COREDUMP_LOGE("Failed to write task header, error=%d!", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
// Save TCB block
|
||||||
|
err = write_cfg->write(write_cfg->priv, task->tcb_addr, esp_core_dump_get_tcb_len());
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_COREDUMP_LOGE("Failed to write TCB, error=%d!", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
// Save task stack
|
||||||
|
err = write_cfg->write(write_cfg->priv, (void*)stk_paddr, stk_len);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_COREDUMP_LOGE("Failed to write stack for task (TCB:%x), stack_start=%x, error=%d!",
|
||||||
|
task->tcb_addr,
|
||||||
|
stk_vaddr,
|
||||||
|
err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Task (TCB:%x) dump is saved.",
|
||||||
|
task->tcb_addr);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_core_dump_save_mem_segment(core_dump_write_config_t* write_cfg,
|
||||||
|
core_dump_mem_seg_header_t* seg)
|
||||||
|
{
|
||||||
|
esp_err_t err = ESP_FAIL;
|
||||||
|
|
||||||
|
if (!esp_core_dump_mem_seg_is_sane(seg->start, seg->size)) {
|
||||||
|
ESP_COREDUMP_LOGE("Failed to write memory segment, (%x, %lu)!",
|
||||||
|
seg->start, seg->size);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
// Save TCB address, stack base and stack top addr
|
||||||
|
err = write_cfg->write(write_cfg->priv, (void*)seg, sizeof(core_dump_mem_seg_header_t));
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_COREDUMP_LOGE("Failed to write memory segment header, error=%d!", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
// Save memory contents
|
||||||
|
err = write_cfg->write(write_cfg->priv, (void*)seg->start, seg->size);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_COREDUMP_LOGE("Failed to write memory segment, (%x, %lu), error=%d!",
|
||||||
|
seg->start, seg->size, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Memory segment (%x, %lu) is saved.",
|
||||||
|
seg->start, seg->size);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static esp_err_t esp_core_dump_write_binary(void *frame, core_dump_write_config_t *write_cfg)
|
static esp_err_t esp_core_dump_write_binary(void *frame, core_dump_write_config_t *write_cfg)
|
||||||
{
|
{
|
||||||
esp_err_t err;
|
esp_err_t err;
|
||||||
core_dump_task_header_t tasks[CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM];
|
static core_dump_task_header_t tasks[CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM];
|
||||||
uint32_t tcb_sz, task_num, tcb_sz_padded;
|
uint32_t task_num, tcb_sz = esp_core_dump_get_tcb_len();
|
||||||
bool task_is_valid = false;
|
uint32_t data_len = 0, task_id;
|
||||||
uint32_t data_len = 0, i;
|
int curr_task_index = COREDUMP_CURR_TASK_NOT_FOUND;
|
||||||
union
|
core_dump_header_t hdr;
|
||||||
{
|
core_dump_mem_seg_header_t interrupted_task_stack;
|
||||||
core_dump_header_t hdr;
|
|
||||||
core_dump_task_header_t task_hdr;
|
|
||||||
} dump_data;
|
|
||||||
|
|
||||||
task_num = esp_core_dump_get_tasks_snapshot(tasks, CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM, &tcb_sz);
|
task_num = esp_core_dump_get_tasks_snapshot(tasks, CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM);
|
||||||
ESP_COREDUMP_LOGI("Found tasks: (%d)!", task_num);
|
ESP_COREDUMP_LOGI("Found tasks: %d!", task_num);
|
||||||
|
|
||||||
// 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);
|
|
||||||
else
|
|
||||||
tcb_sz_padded = tcb_sz;
|
|
||||||
|
|
||||||
// Verifies all tasks in the snapshot
|
// Verifies all tasks in the snapshot
|
||||||
for (i = 0; i < task_num; i++) {
|
for (task_id = 0; task_id < task_num; task_id++) {
|
||||||
task_is_valid = esp_core_dump_process_tcb(frame, &tasks[i], tcb_sz);
|
bool is_current_task = false, stack_is_valid = false;
|
||||||
// Check if task tcb is corrupted
|
bool tcb_is_valid = esp_core_dump_check_task(frame, &tasks[task_id], &is_current_task, &stack_is_valid);
|
||||||
if (!task_is_valid) {
|
// Check if task tcb or stack is corrupted
|
||||||
write_cfg->bad_tasks_num++;
|
if (!tcb_is_valid || !stack_is_valid) {
|
||||||
continue;
|
// If tcb or stack for task is corrupted count task as broken
|
||||||
} else {
|
|
||||||
data_len += (tcb_sz_padded + sizeof(core_dump_task_header_t));
|
|
||||||
}
|
|
||||||
uint32_t len = 0;
|
|
||||||
task_is_valid = esp_core_dump_process_stack(&tasks[i], &len);
|
|
||||||
if (task_is_valid) {
|
|
||||||
// Increase core dump size by task stack size
|
|
||||||
data_len += len;
|
|
||||||
} else {
|
|
||||||
// If task tcb is ok but stack is corrupted
|
|
||||||
write_cfg->bad_tasks_num++;
|
write_cfg->bad_tasks_num++;
|
||||||
}
|
}
|
||||||
|
if (is_current_task) {
|
||||||
|
curr_task_index = task_id; // save current crashed task index in the snapshot
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Task #%d (TCB:%x) is first crashed task.",
|
||||||
|
task_id,
|
||||||
|
tasks[task_id].tcb_addr);
|
||||||
|
}
|
||||||
|
// Increase core dump size by task stack size
|
||||||
|
uint32_t stk_vaddr, stk_len;
|
||||||
|
esp_core_dump_get_stack(&tasks[task_id], &stk_vaddr, &stk_len);
|
||||||
|
data_len += esp_core_dump_get_stack_len(stk_vaddr, stk_vaddr+stk_len);
|
||||||
|
// Add tcb size
|
||||||
|
data_len += (tcb_sz + sizeof(core_dump_task_header_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (esp_core_dump_in_isr_context()) {
|
||||||
|
interrupted_task_stack.start = tasks[curr_task_index].stack_start;
|
||||||
|
interrupted_task_stack.size = esp_core_dump_get_stack_len(tasks[curr_task_index].stack_start, tasks[curr_task_index].stack_end);
|
||||||
|
// size of the task's stack has been already taken into account, also addresses have also been checked
|
||||||
|
data_len += sizeof(core_dump_mem_seg_header_t);
|
||||||
|
tasks[curr_task_index].stack_start = (uint32_t)frame;
|
||||||
|
tasks[curr_task_index].stack_end = esp_core_dump_get_isr_stack_end();
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Add ISR stack %lu to %lu", tasks[curr_task_index].stack_end - tasks[curr_task_index].stack_start, data_len);
|
||||||
|
// take into account size of the ISR stack
|
||||||
|
data_len += esp_core_dump_get_stack_len(tasks[curr_task_index].stack_start, tasks[curr_task_index].stack_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if current task TCB is broken
|
||||||
|
if (curr_task_index == COREDUMP_CURR_TASK_NOT_FOUND) {
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("The current crashed task is broken.");
|
||||||
|
curr_task_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Add core dump header size
|
// Add core dump header size
|
||||||
data_len += sizeof(core_dump_header_t);
|
data_len += sizeof(core_dump_header_t);
|
||||||
ESP_COREDUMP_LOG_PROCESS("Core dump len = %lu (%d %d)", data_len, task_num, write_cfg->bad_tasks_num);
|
|
||||||
|
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Core dump length=%lu, tasks processed: %d, broken tasks: %d",
|
||||||
|
data_len, task_num, write_cfg->bad_tasks_num);
|
||||||
// Prepare write
|
// Prepare write
|
||||||
if (write_cfg->prepare) {
|
if (write_cfg->prepare) {
|
||||||
err = write_cfg->prepare(write_cfg->priv, &data_len);
|
err = write_cfg->prepare(write_cfg->priv, &data_len);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_COREDUMP_LOGE("Failed to prepare core dump (%d)!", err);
|
ESP_COREDUMP_LOGE("Failed to prepare core dump, error=%d!", err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write start
|
// Write start
|
||||||
if (write_cfg->start) {
|
if (write_cfg->start) {
|
||||||
err = write_cfg->start(write_cfg->priv);
|
err = write_cfg->start(write_cfg->priv);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_COREDUMP_LOGE("Failed to start core dump (%d)!", err);
|
ESP_COREDUMP_LOGE("Failed to start core dump, error=%d!", err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 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));
|
|
||||||
if (err != ESP_OK) {
|
|
||||||
ESP_COREDUMP_LOGE("Failed to write core dump header (%d)!", err);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
// Write tasks
|
|
||||||
for (i = 0; i < task_num; i++) {
|
|
||||||
if (!esp_tcb_addr_is_sane((uint32_t)tasks[i].tcb_addr, tcb_sz)) {
|
|
||||||
ESP_COREDUMP_LOG_PROCESS("Skip TCB with bad addr %x!", tasks[i].tcb_addr);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ESP_COREDUMP_LOG_PROCESS("Dump task %x", tasks[i].tcb_addr);
|
|
||||||
// Save TCB address, stack base and stack top addr
|
|
||||||
dump_data.task_hdr.tcb_addr = tasks[i].tcb_addr;
|
|
||||||
dump_data.task_hdr.stack_start = tasks[i].stack_start;
|
|
||||||
dump_data.task_hdr.stack_end = tasks[i].stack_end;
|
|
||||||
err = write_cfg->write(write_cfg->priv, (void*)&dump_data, sizeof(core_dump_task_header_t));
|
|
||||||
if (err != ESP_OK) {
|
|
||||||
ESP_COREDUMP_LOGE("Failed to write task header (%d)!", err);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
// Save TCB
|
|
||||||
err = write_cfg->write(write_cfg->priv, tasks[i].tcb_addr, tcb_sz);
|
|
||||||
if (err != ESP_OK) {
|
|
||||||
ESP_COREDUMP_LOGE("Failed to write TCB (%d)!", err);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
// Save task stack
|
|
||||||
if (tasks[i].stack_start != 0 && tasks[i].stack_end != 0) {
|
|
||||||
err = write_cfg->write(write_cfg->priv, (void*)tasks[i].stack_start,
|
|
||||||
tasks[i].stack_end - tasks[i].stack_start);
|
|
||||||
if (err != ESP_OK) {
|
|
||||||
ESP_COREDUMP_LOGE("Failed to write task stack (%d)!", err);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ESP_COREDUMP_LOG_PROCESS("Skip corrupted task %x stack!", tasks[i].tcb_addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// write end
|
// Write header
|
||||||
|
hdr.data_len = data_len;
|
||||||
|
hdr.version = COREDUMP_VERSION;
|
||||||
|
hdr.tasks_num = task_num; // save all the tasks in snapshot even broken
|
||||||
|
hdr.mem_segs_num = 0;
|
||||||
|
if (xPortInterruptedFromISRContext()) {
|
||||||
|
hdr.mem_segs_num++; // stack of interrupted task
|
||||||
|
}
|
||||||
|
hdr.tcb_sz = tcb_sz;
|
||||||
|
err = write_cfg->write(write_cfg->priv, &hdr, sizeof(core_dump_header_t));
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_COREDUMP_LOGE("Failed to write core dump header error=%d!", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write first crashed task data first (not always first task in the snapshot)
|
||||||
|
err = esp_core_dump_save_task(write_cfg, &tasks[curr_task_index]);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_COREDUMP_LOGE("Failed to save first crashed task #%d (TCB:%x), error=%d!",
|
||||||
|
curr_task_index, tasks[curr_task_index].tcb_addr, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write all other tasks in the snapshot
|
||||||
|
for (task_id = 0; task_id < task_num; task_id++) {
|
||||||
|
// Skip first crashed task
|
||||||
|
if (task_id == curr_task_index) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
err = esp_core_dump_save_task(write_cfg, &tasks[task_id]);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_COREDUMP_LOGE("Failed to save core dump task #%d (TCB:%x), error=%d!",
|
||||||
|
task_id, tasks[curr_task_index].tcb_addr, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (xPortInterruptedFromISRContext()) {
|
||||||
|
err = esp_core_dump_save_mem_segment(write_cfg, &interrupted_task_stack);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_COREDUMP_LOGE("Failed to save interrupted task stack, error=%d!", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write end
|
||||||
if (write_cfg->end) {
|
if (write_cfg->end) {
|
||||||
err = write_cfg->end(write_cfg->priv);
|
err = write_cfg->end(write_cfg->priv);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_COREDUMP_LOGE("Failed to end core dump (%d)!", err);
|
ESP_COREDUMP_LOGE("Failed to end core dump error=%d!", err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (write_cfg->bad_tasks_num) {
|
if (write_cfg->bad_tasks_num) {
|
||||||
ESP_COREDUMP_LOGE("Skipped %d tasks with bad TCB!", write_cfg->bad_tasks_num);
|
ESP_COREDUMP_LOGE("Found %d broken tasks!", write_cfg->bad_tasks_num);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
inline void esp_core_dump_write(void *frame, core_dump_write_config_t *write_cfg)
|
inline void esp_core_dump_write(void *frame, core_dump_write_config_t *write_cfg)
|
||||||
{
|
{
|
||||||
esp_err_t err = esp_core_dump_write_binary(frame, write_cfg);
|
esp_core_dump_setup_stack();
|
||||||
if (err != ESP_OK) {
|
|
||||||
ESP_COREDUMP_LOGE("Core dump write binary failed with error: %d", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#ifndef CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE
|
||||||
|
esp_err_t err = ESP_ERR_NOT_SUPPORTED;
|
||||||
|
#if CONFIG_ESP32_COREDUMP_DATA_FORMAT_BIN
|
||||||
|
err = esp_core_dump_write_binary(frame, write_cfg);
|
||||||
|
#elif CONFIG_ESP32_COREDUMP_DATA_FORMAT_ELF
|
||||||
|
err = esp_core_dump_write_elf(frame, write_cfg);
|
||||||
#endif
|
#endif
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_COREDUMP_LOGE("Core dump write binary failed with error=%d", err);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
esp_core_dump_report_stack_usage();
|
||||||
|
}
|
||||||
|
|
||||||
void esp_core_dump_init(void)
|
void esp_core_dump_init(void)
|
||||||
{
|
{
|
||||||
|
@ -173,14 +276,16 @@ esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size)
|
||||||
return ESP_ERR_INVALID_ARG;
|
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);
|
const esp_partition_t *core_part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||||
|
ESP_PARTITION_SUBTYPE_DATA_COREDUMP,
|
||||||
|
NULL);
|
||||||
if (!core_part) {
|
if (!core_part) {
|
||||||
ESP_LOGE(TAG, "No core dump partition found!");
|
ESP_LOGE(TAG, "No core dump partition found!");
|
||||||
return ESP_FAIL;
|
return ESP_ERR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
if (core_part->size < sizeof(uint32_t)) {
|
if (core_part->size < sizeof(uint32_t)) {
|
||||||
ESP_LOGE(TAG, "Too small core dump partition!");
|
ESP_LOGE(TAG, "Too small core dump partition!");
|
||||||
return ESP_FAIL;
|
return ESP_ERR_INVALID_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = esp_partition_mmap(core_part, 0, sizeof(uint32_t),
|
err = esp_partition_mmap(core_part, 0, sizeof(uint32_t),
|
||||||
|
@ -189,9 +294,14 @@ esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size)
|
||||||
ESP_LOGE(TAG, "Failed to mmap core dump data (%d)!", err);
|
ESP_LOGE(TAG, "Failed to mmap core dump data (%d)!", err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t *dw = (uint32_t *)core_data;
|
uint32_t *dw = (uint32_t *)core_data;
|
||||||
*out_size = *dw;
|
*out_size = *dw;
|
||||||
spi_flash_munmap(core_data_handle);
|
spi_flash_munmap(core_data_handle);
|
||||||
|
if ((*out_size < sizeof(uint32_t)) || (*out_size > core_part->size)) {
|
||||||
|
ESP_LOGE(TAG, "Incorrect size of core dump image: %d", *out_size);
|
||||||
|
return ESP_ERR_INVALID_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
// remap full core dump with CRC
|
// remap full core dump with CRC
|
||||||
err = esp_partition_mmap(core_part, 0, *out_size,
|
err = esp_partition_mmap(core_part, 0, *out_size,
|
||||||
|
@ -200,16 +310,38 @@ esp_err_t esp_core_dump_image_get(size_t* out_addr, size_t *out_size)
|
||||||
ESP_LOGE(TAG, "Failed to mmap core dump data (%d)!", err);
|
ESP_LOGE(TAG, "Failed to mmap core dump data (%d)!", err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
#if CONFIG_ESP32_COREDUMP_CHECKSUM_CRC32
|
||||||
uint32_t *crc = (uint32_t *)(((uint8_t *)core_data) + *out_size);
|
uint32_t *crc = (uint32_t *)(((uint8_t *)core_data) + *out_size);
|
||||||
crc--; // Point to CRC field
|
crc--; // Point to CRC field
|
||||||
// Calc CRC over core dump data except for CRC field
|
|
||||||
|
// Calculate 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));
|
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) {
|
if (*crc != cur_crc) {
|
||||||
|
ESP_LOGD(TAG, "Core dump CRC offset 0x%x, data size: %u",
|
||||||
|
(uint32_t)((uint32_t)crc - (uint32_t)core_data), *out_size);
|
||||||
ESP_LOGE(TAG, "Core dump data CRC check failed: 0x%x -> 0x%x!", *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);
|
spi_flash_munmap(core_data_handle);
|
||||||
return ESP_FAIL;
|
return ESP_ERR_INVALID_CRC;
|
||||||
}
|
}
|
||||||
|
#elif CONFIG_ESP32_COREDUMP_CHECKSUM_SHA256
|
||||||
|
uint8_t* sha256_ptr = (uint8_t*)(((uint8_t *)core_data) + *out_size);
|
||||||
|
sha256_ptr -= COREDUMP_SHA256_LEN;
|
||||||
|
ESP_LOGD(TAG, "Core dump data offset, size: %d, %u!",
|
||||||
|
(uint32_t)((uint32_t)sha256_ptr - (uint32_t)core_data), *out_size);
|
||||||
|
unsigned char sha_output[COREDUMP_SHA256_LEN];
|
||||||
|
mbedtls_sha256_context ctx;
|
||||||
|
ESP_LOGI(TAG, "Calculate SHA256 for coredump:");
|
||||||
|
(void)esp_core_dump_sha(&ctx, core_data, *out_size - COREDUMP_SHA256_LEN, sha_output);
|
||||||
|
if (memcmp((uint8_t*)sha256_ptr, (uint8_t*)sha_output, COREDUMP_SHA256_LEN) != 0) {
|
||||||
|
ESP_LOGE(TAG, "Core dump data SHA256 check failed:");
|
||||||
|
esp_core_dump_print_sha256("Calculated SHA256", (uint8_t*)sha_output);
|
||||||
|
esp_core_dump_print_sha256("Image SHA256",(uint8_t*)sha256_ptr);
|
||||||
|
spi_flash_munmap(core_data_handle);
|
||||||
|
return ESP_ERR_INVALID_CRC;
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "Core dump data SHA256 is correct");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
spi_flash_munmap(core_data_handle);
|
spi_flash_munmap(core_data_handle);
|
||||||
|
|
||||||
*out_addr = core_part->address;
|
*out_addr = core_part->address;
|
||||||
|
|
670
components/espcoredump/src/core_dump_elf.c
Normal file
670
components/espcoredump/src/core_dump_elf.c
Normal file
|
@ -0,0 +1,670 @@
|
||||||
|
// Copyright 2015-2019 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 <string.h>
|
||||||
|
#include "esp_attr.h"
|
||||||
|
#include "esp_partition.h"
|
||||||
|
#include "esp_ota_ops.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "core_dump_elf.h"
|
||||||
|
|
||||||
|
#define ELF_CLASS ELFCLASS32
|
||||||
|
|
||||||
|
#include "elf.h" // for ELF file types
|
||||||
|
|
||||||
|
#define ELF_SEG_HEADERS_COUNT(_self_, _task_num_) (uint32_t)((_task_num_) * 2/*stack + tcb*/ \
|
||||||
|
+ 1/* regs notes */ + 1/* ver info + extra note */ + ((_self_)->interrupted_task.stack_start ? 1 : 0))
|
||||||
|
|
||||||
|
#define ELF_HLEN 52
|
||||||
|
#define ELF_CORE_SEC_TYPE 1
|
||||||
|
#define ELF_PR_STATUS_SEG_NUM 0
|
||||||
|
#define ELF_ESP_CORE_DUMP_INFO_TYPE 8266
|
||||||
|
#define ELF_ESP_CORE_DUMP_EXTRA_INFO_TYPE 677
|
||||||
|
#define ELF_NOTE_NAME_MAX_SIZE 32
|
||||||
|
#define ELF_APP_SHA256_SIZE 66
|
||||||
|
|
||||||
|
#define ELF_CHECK_ERR(a, ret_val, str, ...) \
|
||||||
|
if (!(a)) { \
|
||||||
|
ESP_COREDUMP_LOGE("%s(%u): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||||
|
return (ret_val); \
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
ELF_STAGE_CALC_SPACE = 0,
|
||||||
|
ELF_STAGE_PLACE_HEADERS = 1,
|
||||||
|
ELF_STAGE_PLACE_DATA = 2
|
||||||
|
} core_dump_elf_stages_t;
|
||||||
|
|
||||||
|
typedef enum _elf_err_t
|
||||||
|
{
|
||||||
|
ELF_PROC_ERR_SKIP_HEADER = 0,
|
||||||
|
ELF_PROC_ERR_STACK_CORRUPTED = -1,
|
||||||
|
ELF_PROC_ERR_WRITE_FAIL = -2,
|
||||||
|
ELF_PROC_ERR_OTHER = -3
|
||||||
|
} core_dump_elf_proc_err_t;
|
||||||
|
|
||||||
|
typedef struct _core_dump_task_info_t
|
||||||
|
{
|
||||||
|
elf_phdr* phdr;
|
||||||
|
void* frame;
|
||||||
|
core_dump_task_header_t* task_hdr;
|
||||||
|
uint32_t task_id;
|
||||||
|
size_t tcb_sz;
|
||||||
|
int* size_ptr;
|
||||||
|
} core_dump_task_data_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t version; // coredump version
|
||||||
|
uint8_t app_elf_sha256[ELF_APP_SHA256_SIZE]; // sha256 of elf file
|
||||||
|
} core_dump_elf_version_info_t;
|
||||||
|
|
||||||
|
const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_elf";
|
||||||
|
|
||||||
|
// Main ELF handle type
|
||||||
|
typedef struct _core_dump_elf_t
|
||||||
|
{
|
||||||
|
core_dump_elf_version_info_t elf_version_info;
|
||||||
|
uint16_t elf_stage;
|
||||||
|
uint32_t elf_next_data_offset;
|
||||||
|
uint32_t bad_tasks_num;
|
||||||
|
core_dump_task_header_t interrupted_task;
|
||||||
|
core_dump_write_config_t * write_cfg;
|
||||||
|
} core_dump_elf_t;
|
||||||
|
|
||||||
|
// Represents lightweight implementation to save core dump data into ELF formatted binary
|
||||||
|
|
||||||
|
#define ALIGN(b, var) var = align(b, var)
|
||||||
|
|
||||||
|
#if CONFIG_ESP32_COREDUMP_DATA_FORMAT_ELF
|
||||||
|
|
||||||
|
static inline uint32_t align(uint32_t width, uint32_t in)
|
||||||
|
{
|
||||||
|
return (in + (width - 1)) & -width;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds elf header and check all data offsets
|
||||||
|
static int elf_write_file_header(core_dump_elf_t *self, uint32_t seg_count)
|
||||||
|
{
|
||||||
|
elfhdr elf_hdr; // declare as static to save stack space
|
||||||
|
|
||||||
|
if (self->elf_stage == ELF_STAGE_PLACE_HEADERS) {
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Segment count %u", seg_count);
|
||||||
|
memset(&elf_hdr, 0, sizeof(elfhdr));
|
||||||
|
elf_hdr.e_ident[0] = ELFMAG0;
|
||||||
|
elf_hdr.e_ident[1] = ELFMAG1;
|
||||||
|
elf_hdr.e_ident[2] = ELFMAG2;
|
||||||
|
elf_hdr.e_ident[3] = ELFMAG3;
|
||||||
|
elf_hdr.e_ident[4] = ELFCLASS32;
|
||||||
|
elf_hdr.e_ident[5] = ELFDATA2LSB;
|
||||||
|
elf_hdr.e_ident[6] = EV_CURRENT;
|
||||||
|
elf_hdr.e_ident[7] = ELFOSABI_NONE;
|
||||||
|
elf_hdr.e_ident[8] = 0;
|
||||||
|
elf_hdr.e_type = ET_CORE;
|
||||||
|
elf_hdr.e_machine = esp_core_dump_get_arch_id();
|
||||||
|
elf_hdr.e_flags = 0;
|
||||||
|
elf_hdr.e_version = EV_CURRENT;
|
||||||
|
elf_hdr.e_entry = 0;
|
||||||
|
_Static_assert(sizeof(elfhdr) == ELF_HLEN, "Invalid ELF header struct length!");
|
||||||
|
elf_hdr.e_phoff = sizeof(elfhdr); // program header table's file offset in bytes.
|
||||||
|
elf_hdr.e_phentsize = sizeof(elf_phdr); // size in bytes of one entry in the file program header table
|
||||||
|
elf_hdr.e_phnum = seg_count; // number of program segments
|
||||||
|
elf_hdr.e_shoff = 0; // section header table's file offset in bytes.
|
||||||
|
elf_hdr.e_ehsize = sizeof(elfhdr); // elf header size
|
||||||
|
elf_hdr.e_shentsize = sizeof(elf_shdr); // section header's size in bytes.
|
||||||
|
elf_hdr.e_shnum = 0; // initial section counter is 0
|
||||||
|
elf_hdr.e_shstrndx = SHN_UNDEF; // do not use string table
|
||||||
|
// write built elf header into elf image
|
||||||
|
esp_err_t err = self->write_cfg->write(self->write_cfg->priv, (void*)&elf_hdr, sizeof(elf_hdr));
|
||||||
|
ELF_CHECK_ERR((err == ESP_OK), ELF_PROC_ERR_WRITE_FAIL,
|
||||||
|
"Write ELF header failure (%d)", err);
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Add file header %u bytes", sizeof(elf_hdr));
|
||||||
|
}
|
||||||
|
|
||||||
|
return self->elf_stage == ELF_STAGE_PLACE_DATA ? 0 : sizeof(elf_hdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elf_write_segment_header(core_dump_elf_t *self, elf_phdr* phdr)
|
||||||
|
{
|
||||||
|
ELF_CHECK_ERR(phdr, ELF_PROC_ERR_SKIP_HEADER,
|
||||||
|
"Header is skipped, stage=(%d).", self->elf_stage);
|
||||||
|
|
||||||
|
phdr->p_offset = self->elf_next_data_offset;
|
||||||
|
// set segment data information and write it into image
|
||||||
|
esp_err_t err = self->write_cfg->write(self->write_cfg->priv, (void*)phdr, sizeof(elf_phdr));
|
||||||
|
ELF_CHECK_ERR((err == ESP_OK), ELF_PROC_ERR_WRITE_FAIL,
|
||||||
|
"Write ELF segment header failure (%d)", err);
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Add segment header %u bytes: type %d, sz %u, off = 0x%x",
|
||||||
|
sizeof(elf_phdr), phdr->p_type, phdr->p_filesz, phdr->p_offset);
|
||||||
|
|
||||||
|
return sizeof(elf_phdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elf_add_segment(core_dump_elf_t *self,
|
||||||
|
uint32_t type, uint32_t vaddr,
|
||||||
|
void* data, uint32_t data_sz)
|
||||||
|
{
|
||||||
|
esp_err_t err = ESP_FAIL;
|
||||||
|
elf_phdr seg_hdr = { 0 };
|
||||||
|
int data_len = data_sz;
|
||||||
|
|
||||||
|
ELF_CHECK_ERR((data != NULL), ELF_PROC_ERR_OTHER,
|
||||||
|
"Invalid data for segment.");
|
||||||
|
|
||||||
|
ALIGN(4, data_len);
|
||||||
|
|
||||||
|
if (self->elf_stage == ELF_STAGE_CALC_SPACE) {
|
||||||
|
return data_len + sizeof(elf_phdr);
|
||||||
|
}
|
||||||
|
if (self->elf_stage == ELF_STAGE_PLACE_HEADERS) {
|
||||||
|
seg_hdr.p_type = type;
|
||||||
|
seg_hdr.p_vaddr = vaddr;
|
||||||
|
seg_hdr.p_paddr = vaddr;
|
||||||
|
seg_hdr.p_filesz = data_len;
|
||||||
|
seg_hdr.p_memsz = data_len;
|
||||||
|
seg_hdr.p_flags = (PF_R | PF_W);
|
||||||
|
int ret = elf_write_segment_header(self, &seg_hdr);
|
||||||
|
ELF_CHECK_ERR((ret > 0), ret,
|
||||||
|
"Write ELF segment data failure (%d)", ret);
|
||||||
|
self->elf_next_data_offset += data_len;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Add segment size=%u, start_off=0x%x",
|
||||||
|
(uint32_t)data_len, self->elf_next_data_offset);
|
||||||
|
// write segment data only when write function is set and phdr = NULL
|
||||||
|
// write data into segment
|
||||||
|
err = self->write_cfg->write(self->write_cfg->priv, data, (uint32_t)data_len);
|
||||||
|
ELF_CHECK_ERR((err == ESP_OK), ELF_PROC_ERR_WRITE_FAIL,
|
||||||
|
"Write ELF segment data failure (%d)", err);
|
||||||
|
self->elf_next_data_offset += data_len;
|
||||||
|
return data_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elf_write_note(core_dump_elf_t *self,
|
||||||
|
const char* name,
|
||||||
|
uint32_t type,
|
||||||
|
void* data,
|
||||||
|
uint32_t data_sz)
|
||||||
|
{
|
||||||
|
esp_err_t err = ESP_FAIL;
|
||||||
|
// temporary buffer for note name
|
||||||
|
static char name_buffer[ELF_NOTE_NAME_MAX_SIZE] = { 0 };
|
||||||
|
elf_note note_hdr = { 0 };
|
||||||
|
uint32_t name_len = strlen(name) + 1; // get name length including terminator
|
||||||
|
uint32_t data_len = data_sz;
|
||||||
|
|
||||||
|
ELF_CHECK_ERR(data, ELF_PROC_ERR_OTHER,
|
||||||
|
"Invalid data pointer %x.", (uint32_t)data);
|
||||||
|
ELF_CHECK_ERR((name_len <= ELF_NOTE_NAME_MAX_SIZE), 0,
|
||||||
|
"Segment note name is too long %d.", name_len);
|
||||||
|
|
||||||
|
ALIGN(4, data_len);
|
||||||
|
ALIGN(4, name_len);
|
||||||
|
uint32_t note_size = name_len + data_len + sizeof(elf_note);
|
||||||
|
ALIGN(4, note_size);
|
||||||
|
|
||||||
|
// write segment data during second pass
|
||||||
|
if (self->elf_stage == ELF_STAGE_PLACE_DATA) {
|
||||||
|
memcpy((void*)name_buffer, (void*)name, name_len);
|
||||||
|
note_hdr.n_namesz = name_len;
|
||||||
|
note_hdr.n_descsz = data_sz;
|
||||||
|
note_hdr.n_type = type;
|
||||||
|
// write note header
|
||||||
|
err = self->write_cfg->write(self->write_cfg->priv, (void*)¬e_hdr, sizeof(note_hdr));
|
||||||
|
ELF_CHECK_ERR((err == ESP_OK), ELF_PROC_ERR_WRITE_FAIL,
|
||||||
|
"Write ELF note header failure (%d)", err);
|
||||||
|
// write note name
|
||||||
|
err = self->write_cfg->write(self->write_cfg->priv, (void*)name_buffer, name_len);
|
||||||
|
ELF_CHECK_ERR((err == ESP_OK), ELF_PROC_ERR_WRITE_FAIL,
|
||||||
|
"Write ELF note name failure (%d)", err);
|
||||||
|
// write note data
|
||||||
|
err = self->write_cfg->write(self->write_cfg->priv, (void*)data, data_len);
|
||||||
|
ELF_CHECK_ERR((err == ESP_OK), ELF_PROC_ERR_WRITE_FAIL,
|
||||||
|
"Write ELF note data failure (%d)", err);
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Add note size=%d, start_off=0x%x",
|
||||||
|
note_size, self->elf_next_data_offset);
|
||||||
|
}
|
||||||
|
return note_size; // return actual note size
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elf_add_note(core_dump_elf_t *self,
|
||||||
|
const char* name,
|
||||||
|
uint32_t type,
|
||||||
|
void* data,
|
||||||
|
uint32_t data_sz)
|
||||||
|
{
|
||||||
|
ELF_CHECK_ERR((data != NULL), ELF_PROC_ERR_OTHER,
|
||||||
|
"Invalid data pointer for segment");
|
||||||
|
|
||||||
|
int note_size = elf_write_note(self, name, type, data, data_sz);
|
||||||
|
ELF_CHECK_ERR((note_size > 0), note_size,
|
||||||
|
"Write ELF note data failure, returned (%d)", note_size);
|
||||||
|
return note_size; // return actual note segment size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append note with registers dump to segment note
|
||||||
|
static int elf_add_regs(core_dump_elf_t *self, core_dump_task_header_t *task)
|
||||||
|
{
|
||||||
|
void *reg_dump;
|
||||||
|
|
||||||
|
uint32_t len = esp_core_dump_get_task_regs_dump(task, ®_dump);
|
||||||
|
if (len == 0) {
|
||||||
|
ESP_COREDUMP_LOGE("Zero size register dump for task 0x%x!", task->tcb_addr);
|
||||||
|
return ELF_PROC_ERR_OTHER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// append note data with dump to existing note
|
||||||
|
return elf_add_note(self,
|
||||||
|
"CORE", // note name
|
||||||
|
ELF_CORE_SEC_TYPE, // note type for reg dump
|
||||||
|
reg_dump, // register dump with pr_status
|
||||||
|
len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elf_add_stack(core_dump_elf_t *self, core_dump_task_header_t *task)
|
||||||
|
{
|
||||||
|
uint32_t stack_vaddr, stack_len = 0, stack_paddr = 0;
|
||||||
|
|
||||||
|
ELF_CHECK_ERR((task), ELF_PROC_ERR_OTHER, "Invalid task pointer.");
|
||||||
|
|
||||||
|
stack_paddr = esp_core_dump_get_stack(task, &stack_vaddr, &stack_len);
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Add stack for task 0x%x: addr 0x%x, sz %u",
|
||||||
|
task->tcb_addr, stack_vaddr, stack_len);
|
||||||
|
int ret = elf_add_segment(self, PT_LOAD,
|
||||||
|
(uint32_t)stack_vaddr,
|
||||||
|
(void*)stack_paddr,
|
||||||
|
(uint32_t) stack_len);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elf_add_tcb(core_dump_elf_t *self, core_dump_task_header_t *task)
|
||||||
|
{
|
||||||
|
ELF_CHECK_ERR((task), ELF_PROC_ERR_OTHER, "Invalid task pointer.");
|
||||||
|
// add task tcb data into program segment of ELF
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Add TCB for task 0x%x: addr 0x%x, sz %u",
|
||||||
|
task->tcb_addr, task->tcb_addr, COREDUMP_TCB_SIZE);
|
||||||
|
int ret = elf_add_segment(self, PT_LOAD,
|
||||||
|
(uint32_t)task->tcb_addr,
|
||||||
|
(void*)task->tcb_addr,
|
||||||
|
COREDUMP_TCB_SIZE);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get index of current crashed task (not always first task in the snapshot)
|
||||||
|
static int elf_get_current_task_index(core_dump_task_header_t *tasks,
|
||||||
|
uint32_t task_num)
|
||||||
|
{
|
||||||
|
int task_id;
|
||||||
|
int curr_task_index = COREDUMP_CURR_TASK_NOT_FOUND;
|
||||||
|
void* curr_task_handle = esp_core_dump_get_current_task_handle();
|
||||||
|
|
||||||
|
// get index of current crashed task (not always first task in the snapshot)
|
||||||
|
for (task_id = 0; task_id < task_num; task_id++) {
|
||||||
|
bool tcb_is_valid = esp_core_dump_tcb_addr_is_sane((uint32_t)tasks[task_id].tcb_addr);
|
||||||
|
bool stack_is_valid = esp_core_dump_check_stack(tasks[task_id].stack_start, tasks[task_id].stack_end);
|
||||||
|
if (stack_is_valid && tcb_is_valid && curr_task_handle == tasks[task_id].tcb_addr) {
|
||||||
|
curr_task_index = task_id; // save current crashed task index in the snapshot
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Task #%d, (TCB:%x) is current crashed task.",
|
||||||
|
task_id,
|
||||||
|
tasks[task_id].tcb_addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return curr_task_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elf_process_task_regdump(core_dump_elf_t *self, void *frame, core_dump_task_header_t *task)
|
||||||
|
{
|
||||||
|
bool task_is_valid = false;
|
||||||
|
bool task_is_current = false;
|
||||||
|
|
||||||
|
ELF_CHECK_ERR((task), ELF_PROC_ERR_OTHER, "Invalid input data.");
|
||||||
|
|
||||||
|
if (self->elf_stage == ELF_STAGE_CALC_SPACE) {
|
||||||
|
// Check if task tcb is corrupted (do not update the header, save as is)
|
||||||
|
task_is_valid = esp_core_dump_check_task(frame, task, &task_is_current, NULL);
|
||||||
|
if (!task_is_valid) {
|
||||||
|
if (task_is_current) {
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Task has incorrect (TCB:%x)!",
|
||||||
|
task->tcb_addr);
|
||||||
|
} else {
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("The current crashed task has broken (TCB:%x)!",
|
||||||
|
task->tcb_addr);
|
||||||
|
}
|
||||||
|
self->bad_tasks_num++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// extract registers from stack and apply elf data size for stack section
|
||||||
|
return elf_add_regs(self, task);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elf_process_task_tcb(core_dump_elf_t *self, core_dump_task_header_t *task)
|
||||||
|
{
|
||||||
|
int ret = ELF_PROC_ERR_OTHER;
|
||||||
|
|
||||||
|
ELF_CHECK_ERR((task), ELF_PROC_ERR_OTHER, "Invalid input data.");
|
||||||
|
|
||||||
|
// save tcb of the task as is and apply segment size
|
||||||
|
ret = elf_add_tcb(self, task);
|
||||||
|
if (ret > 0) {
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Task (TCB:%x) processing completed.",
|
||||||
|
task->tcb_addr);
|
||||||
|
} else {
|
||||||
|
ESP_COREDUMP_LOGE("Task (TCB:%x) processing failure = %d",
|
||||||
|
task->tcb_addr,
|
||||||
|
ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elf_process_task_stack(core_dump_elf_t *self, core_dump_task_header_t *task)
|
||||||
|
{
|
||||||
|
int ret = ELF_PROC_ERR_OTHER;
|
||||||
|
|
||||||
|
ELF_CHECK_ERR((task), ELF_PROC_ERR_OTHER, "Invalid input data.");
|
||||||
|
|
||||||
|
ret = elf_add_stack(self, task);
|
||||||
|
if (ret > 0) {
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Task (TCB:%x), (Stack:%x) stack is processed.",
|
||||||
|
task->tcb_addr,
|
||||||
|
task->stack_start);
|
||||||
|
} else {
|
||||||
|
ESP_COREDUMP_LOGE("Task (TCB:%x), (Stack:%x), stack processing failure = %d.",
|
||||||
|
task->tcb_addr,
|
||||||
|
task->stack_start,
|
||||||
|
ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elf_process_note_segment(core_dump_elf_t *self, int notes_size)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
elf_phdr seg_hdr = { 0 };
|
||||||
|
|
||||||
|
if (self->elf_stage == ELF_STAGE_PLACE_HEADERS) {
|
||||||
|
// segment header for PR_STATUS notes
|
||||||
|
seg_hdr.p_type = PT_NOTE;
|
||||||
|
seg_hdr.p_vaddr = 0;
|
||||||
|
seg_hdr.p_paddr = 0;
|
||||||
|
seg_hdr.p_filesz = notes_size;
|
||||||
|
seg_hdr.p_memsz = notes_size;
|
||||||
|
seg_hdr.p_flags = (PF_R | PF_W);
|
||||||
|
ret = elf_write_segment_header(self, &seg_hdr);
|
||||||
|
ELF_CHECK_ERR((ret > 0), ret, "NOTE segment header write failure, returned (%d).", ret);
|
||||||
|
self->elf_next_data_offset += notes_size;
|
||||||
|
return sizeof(seg_hdr);
|
||||||
|
} else if (self->elf_stage == ELF_STAGE_CALC_SPACE) {
|
||||||
|
notes_size += sizeof(seg_hdr);
|
||||||
|
} else {
|
||||||
|
// in "Place Data" phase segment body is been already filled by other functions
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Add NOTE segment, size=%d, start_off=0x%x",
|
||||||
|
notes_size, self->elf_next_data_offset);
|
||||||
|
self->elf_next_data_offset += notes_size;
|
||||||
|
}
|
||||||
|
return (int)notes_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elf_process_tasks_regs(core_dump_elf_t *self, void* frame,
|
||||||
|
core_dump_task_header_t* tasks,
|
||||||
|
uint32_t task_num)
|
||||||
|
{
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
uint32_t curr_task_index = elf_get_current_task_index(tasks, task_num);
|
||||||
|
if (curr_task_index == COREDUMP_CURR_TASK_NOT_FOUND) {
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("The current crashed task is broken.");
|
||||||
|
curr_task_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// place current task dump first
|
||||||
|
int ret = elf_process_task_regdump(self, frame, &tasks[curr_task_index]);
|
||||||
|
if (self->elf_stage == ELF_STAGE_PLACE_HEADERS) {
|
||||||
|
// when writing segments headers this function writes nothing
|
||||||
|
ELF_CHECK_ERR((ret >= 0), ret, "Task #%d, PR_STATUS write failed, return (%d).", curr_task_index, ret);
|
||||||
|
} else {
|
||||||
|
ELF_CHECK_ERR((ret > 0), ret, "Task #%d, PR_STATUS write failed, return (%d).", curr_task_index, ret);
|
||||||
|
}
|
||||||
|
len += ret;
|
||||||
|
|
||||||
|
// processes PR_STATUS and register dump for each task
|
||||||
|
// each call to the processing function appends PR_STATUS note into note segment
|
||||||
|
// and writes data or updates the segment note header accordingly (if phdr is set)
|
||||||
|
for (int task_id = 0; task_id < task_num; task_id++) {
|
||||||
|
if (task_id == curr_task_index) {
|
||||||
|
continue; // skip current task (already processed)
|
||||||
|
}
|
||||||
|
ret = elf_process_task_regdump(self, frame, &tasks[task_id]);
|
||||||
|
if (self->elf_stage == ELF_STAGE_PLACE_HEADERS) {
|
||||||
|
// when writing segments headers this function writes nothing
|
||||||
|
ELF_CHECK_ERR((ret >= 0), ret, "Task #%d, PR_STATUS write failed, return (%d).", task_id, ret);
|
||||||
|
} else {
|
||||||
|
ELF_CHECK_ERR((ret > 0), ret, "Task #%d, PR_STATUS write failed, return (%d).", task_id, ret);
|
||||||
|
}
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
ret = elf_process_note_segment(self, len);
|
||||||
|
ELF_CHECK_ERR((ret > 0), ret,
|
||||||
|
"PR_STATUS note segment processing failure, returned(%d).", ret);
|
||||||
|
|
||||||
|
if (esp_core_dump_in_isr_context()) {
|
||||||
|
if (self->elf_stage == ELF_STAGE_CALC_SPACE) {
|
||||||
|
// in this stage we can safely replace task's stack with IRQ's one
|
||||||
|
// if task had corrupted stack it was replaced with fake one in HW dependent code called by elf_process_task_regdump()
|
||||||
|
// in the "write data" stage registers from ISR's stack will be saved in PR_STATUS
|
||||||
|
self->interrupted_task.stack_start = tasks[curr_task_index].stack_start;
|
||||||
|
self->interrupted_task.stack_end = tasks[curr_task_index].stack_end;
|
||||||
|
uint32_t isr_stk_end = esp_core_dump_get_isr_stack_end();
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Add ISR stack %lu (%x - %x)", isr_stk_end - (uint32_t)frame, (uint32_t)frame, isr_stk_end);
|
||||||
|
tasks[curr_task_index].stack_start = (uint32_t)frame;
|
||||||
|
tasks[curr_task_index].stack_end = isr_stk_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// actually we write current task's stack here which was replaced by ISR's
|
||||||
|
len = elf_add_stack(self, &self->interrupted_task);
|
||||||
|
ELF_CHECK_ERR((len > 0), len, "Interrupted task stack write failed, return (%d).", len);
|
||||||
|
ret += len;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elf_write_tasks_data(core_dump_elf_t *self, void* frame,
|
||||||
|
core_dump_task_header_t* tasks,
|
||||||
|
uint32_t task_num)
|
||||||
|
{
|
||||||
|
int elf_len = 0;
|
||||||
|
int task_id;
|
||||||
|
int ret = ELF_PROC_ERR_OTHER;
|
||||||
|
|
||||||
|
ELF_CHECK_ERR((frame && tasks), ELF_PROC_ERR_OTHER, "Invalid input data.");
|
||||||
|
|
||||||
|
ret = elf_process_tasks_regs(self, frame, tasks, task_num);
|
||||||
|
ELF_CHECK_ERR((ret > 0), ret, "Tasks regs addition failed, return (%d).", ret);
|
||||||
|
elf_len += ret;
|
||||||
|
self->bad_tasks_num = 0; // reset bad task counter
|
||||||
|
|
||||||
|
// processes all task's stack data and writes segment data into partition
|
||||||
|
// if flash configuration is set
|
||||||
|
for (task_id = 0; task_id < task_num; task_id++) {
|
||||||
|
ret = elf_process_task_tcb(self, &tasks[task_id]);
|
||||||
|
ELF_CHECK_ERR((ret > 0), ret,
|
||||||
|
"Task #%d, TCB write failed, return (%d).", task_id, ret);
|
||||||
|
elf_len += ret;
|
||||||
|
ret = elf_process_task_stack(self, &tasks[task_id]);
|
||||||
|
ELF_CHECK_ERR((ret != ELF_PROC_ERR_WRITE_FAIL), ELF_PROC_ERR_WRITE_FAIL,
|
||||||
|
"Task #%d, stack write failed, return (%d).", task_id, ret);
|
||||||
|
elf_len += ret;
|
||||||
|
}
|
||||||
|
return elf_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int elf_write_core_dump_info(core_dump_elf_t *self)
|
||||||
|
{
|
||||||
|
void *extra_info;
|
||||||
|
|
||||||
|
int data_len = (int)sizeof(self->elf_version_info.app_elf_sha256);
|
||||||
|
data_len = esp_ota_get_app_elf_sha256((char*)self->elf_version_info.app_elf_sha256, (size_t)data_len);
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Application SHA256='%s', length=%d.",
|
||||||
|
self->elf_version_info.app_elf_sha256, data_len);
|
||||||
|
self->elf_version_info.version = COREDUMP_VERSION;
|
||||||
|
int ret = elf_add_note(self,
|
||||||
|
"ESP_CORE_DUMP_INFO",
|
||||||
|
ELF_ESP_CORE_DUMP_INFO_TYPE,
|
||||||
|
&self->elf_version_info,
|
||||||
|
sizeof(self->elf_version_info));
|
||||||
|
ELF_CHECK_ERR((ret > 0), ret, "Version info note write failed. Returned (%d).", ret);
|
||||||
|
data_len = ret;
|
||||||
|
|
||||||
|
uint32_t extra_info_len = esp_core_dump_get_extra_info(&extra_info);
|
||||||
|
if (extra_info_len == 0) {
|
||||||
|
ESP_COREDUMP_LOGE("Zero size extra info!");
|
||||||
|
return ELF_PROC_ERR_OTHER;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = elf_add_note(self,
|
||||||
|
"EXTRA_INFO",
|
||||||
|
ELF_ESP_CORE_DUMP_EXTRA_INFO_TYPE,
|
||||||
|
extra_info,
|
||||||
|
extra_info_len);
|
||||||
|
ELF_CHECK_ERR((ret > 0), ret, "Extra info note write failed. Returned (%d).", ret);
|
||||||
|
data_len += ret;
|
||||||
|
|
||||||
|
ret = elf_process_note_segment(self, data_len);
|
||||||
|
ELF_CHECK_ERR((ret > 0), ret,
|
||||||
|
"EXTRA_INFO note segment processing failure, returned(%d).", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int esp_core_dump_do_write_elf_pass(core_dump_elf_t *self, void* frame,
|
||||||
|
core_dump_task_header_t* tasks,
|
||||||
|
uint32_t task_num)
|
||||||
|
{
|
||||||
|
int tot_len = 0;
|
||||||
|
|
||||||
|
int data_sz = elf_write_file_header(self, ELF_SEG_HEADERS_COUNT(self, task_num));
|
||||||
|
if (self->elf_stage == ELF_STAGE_PLACE_DATA) {
|
||||||
|
ELF_CHECK_ERR((data_sz >= 0), data_sz, "ELF header writing error, returned (%d).", data_sz);
|
||||||
|
} else {
|
||||||
|
ELF_CHECK_ERR((data_sz > 0), data_sz, "ELF header writing error, returned (%d).", data_sz);
|
||||||
|
}
|
||||||
|
tot_len += data_sz;
|
||||||
|
// Calculate whole size include headers for all tasks and main elf header
|
||||||
|
data_sz = elf_write_tasks_data(self, frame, tasks, task_num);
|
||||||
|
ELF_CHECK_ERR((data_sz > 0), data_sz, "ELF Size writing error, returned (%d).", data_sz);
|
||||||
|
tot_len += data_sz;
|
||||||
|
// write data with version control information and some extra info
|
||||||
|
// this should go after tasks processing
|
||||||
|
data_sz = elf_write_core_dump_info(self);
|
||||||
|
ELF_CHECK_ERR((data_sz > 0), data_sz, "Version info writing failed. Returned (%d).", data_sz);
|
||||||
|
tot_len += data_sz;
|
||||||
|
|
||||||
|
return tot_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_core_dump_write_elf(void *frame, core_dump_write_config_t *write_cfg)
|
||||||
|
{
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
static core_dump_task_header_t tasks[CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM];
|
||||||
|
static core_dump_elf_t self;
|
||||||
|
core_dump_header_t dump_hdr;
|
||||||
|
uint32_t tcb_sz = COREDUMP_TCB_SIZE, task_num;
|
||||||
|
int tot_len = sizeof(dump_hdr);
|
||||||
|
int write_len = sizeof(dump_hdr);
|
||||||
|
|
||||||
|
ELF_CHECK_ERR((frame && write_cfg), ESP_ERR_INVALID_ARG, "Invalid input data.");
|
||||||
|
|
||||||
|
task_num = esp_core_dump_get_tasks_snapshot(tasks, CONFIG_ESP32_CORE_DUMP_MAX_TASKS_NUM);
|
||||||
|
ESP_COREDUMP_LOGI("Found tasks: %d", task_num);
|
||||||
|
|
||||||
|
self.write_cfg = write_cfg;
|
||||||
|
|
||||||
|
esp_core_dump_init_extra_info();
|
||||||
|
// On first pass (do not write actual data), but calculate data length needed to allocate memory
|
||||||
|
self.elf_stage = ELF_STAGE_CALC_SPACE;
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("================= Calc data size ===============");
|
||||||
|
int ret = esp_core_dump_do_write_elf_pass(&self, frame, tasks, task_num);
|
||||||
|
if (ret < 0) return ret;
|
||||||
|
tot_len += ret;
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Core dump tot_len=%lu, tasks processed: %d, broken tasks: %d",
|
||||||
|
tot_len, task_num, self.bad_tasks_num);
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("============== Data size = %d bytes ============", tot_len);
|
||||||
|
|
||||||
|
// Prepare write elf
|
||||||
|
if (write_cfg->prepare) {
|
||||||
|
err = write_cfg->prepare(write_cfg->priv, (uint32_t*)&tot_len);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_COREDUMP_LOGE("Failed to prepare core dump storage (%d)!", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write start
|
||||||
|
if (write_cfg->start) {
|
||||||
|
err = write_cfg->start(write_cfg->priv);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_COREDUMP_LOGE("Failed to start core dump (%d)!", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write_cfg->bad_tasks_num = self.bad_tasks_num;
|
||||||
|
|
||||||
|
// Write core dump header
|
||||||
|
ALIGN(4, tot_len);
|
||||||
|
ALIGN(4, tcb_sz);
|
||||||
|
dump_hdr.data_len = tot_len;
|
||||||
|
dump_hdr.version = COREDUMP_VERSION;
|
||||||
|
dump_hdr.tasks_num = task_num; // broken tasks are repaired
|
||||||
|
dump_hdr.tcb_sz = tcb_sz;
|
||||||
|
dump_hdr.mem_segs_num = 0;
|
||||||
|
err = write_cfg->write(write_cfg->priv,
|
||||||
|
(void*)&dump_hdr,
|
||||||
|
sizeof(core_dump_header_t));
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_COREDUMP_LOGE("Failed to write core dump header (%d)!", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.elf_stage = ELF_STAGE_PLACE_HEADERS;
|
||||||
|
// set initial offset to elf segments data area
|
||||||
|
self.elf_next_data_offset = sizeof(elfhdr) + ELF_SEG_HEADERS_COUNT(&self, task_num) * sizeof(elf_phdr);
|
||||||
|
ret = esp_core_dump_do_write_elf_pass(&self, frame, tasks, task_num);
|
||||||
|
if (ret < 0) return ret;
|
||||||
|
write_len += ret;
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("============== Headers size = %d bytes ============", write_len);
|
||||||
|
|
||||||
|
self.elf_stage = ELF_STAGE_PLACE_DATA;
|
||||||
|
// set initial offset to elf segments data area, this is not necessary in this stage, just for pretty debug output
|
||||||
|
self.elf_next_data_offset = sizeof(elfhdr) + ELF_SEG_HEADERS_COUNT(&self, task_num) * sizeof(elf_phdr);
|
||||||
|
ret = esp_core_dump_do_write_elf_pass(&self, frame, tasks, task_num);
|
||||||
|
if (ret < 0) return ret;
|
||||||
|
write_len += ret;
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("=========== Data written size = %d bytes ==========", write_len);
|
||||||
|
|
||||||
|
// Get checksum size
|
||||||
|
write_len += esp_core_dump_checksum_finish(write_cfg->priv, NULL);
|
||||||
|
if (write_len != tot_len) {
|
||||||
|
ESP_COREDUMP_LOGD("Write ELF failed (wrong length): %d != %d.", tot_len, write_len);
|
||||||
|
}
|
||||||
|
// Write end, update checksum
|
||||||
|
if (write_cfg->end) {
|
||||||
|
err = write_cfg->end(write_cfg->priv);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_COREDUMP_LOGE("Failed to end core dump (%d)!", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //CONFIG_ESP32_COREDUMP_DATA_FORMAT_ELF
|
|
@ -12,23 +12,15 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "esp32/rom/crc.h"
|
|
||||||
#include "esp_partition.h"
|
#include "esp_partition.h"
|
||||||
|
#include "esp32/rom/crc.h"
|
||||||
#include "esp_core_dump_priv.h"
|
#include "esp_core_dump_priv.h"
|
||||||
#include "esp_flash_internal.h"
|
#include "esp_flash_internal.h"
|
||||||
|
|
||||||
#include "sdkconfig.h"
|
|
||||||
|
|
||||||
const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_flash";
|
const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_flash";
|
||||||
|
|
||||||
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
|
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
|
||||||
|
|
||||||
typedef struct _core_dump_write_flash_data_t
|
|
||||||
{
|
|
||||||
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
|
typedef struct _core_dump_partition_t
|
||||||
{
|
{
|
||||||
// core dump partition start
|
// core dump partition start
|
||||||
|
@ -48,6 +40,17 @@ typedef struct _core_dump_flash_config_t
|
||||||
// core dump flash data
|
// core dump flash data
|
||||||
static core_dump_flash_config_t s_core_flash_config;
|
static core_dump_flash_config_t s_core_flash_config;
|
||||||
|
|
||||||
|
#ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
|
||||||
|
#define ESP_COREDUMP_FLASH_WRITE(_off_, _data_, _len_) spi_flash_write(_off_, _data_, _len_)
|
||||||
|
#define ESP_COREDUMP_FLASH_READ(_off_, _data_, _len_) spi_flash_read(_off_, _data_, _len_)
|
||||||
|
#define ESP_COREDUMP_FLASH_ERASE(_off_, _len_) spi_flash_erase_range(_off_, _len_)
|
||||||
|
#else
|
||||||
|
#define ESP_COREDUMP_FLASH_WRITE(_off_, _data_, _len_) esp_flash_write(esp_flash_default_chip, _data_, _off_, _len_)
|
||||||
|
#define ESP_COREDUMP_FLASH_READ(_off_, _data_, _len_) esp_flash_read(esp_flash_default_chip, _data_, _off_, _len_)
|
||||||
|
#define ESP_COREDUMP_FLASH_ERASE(_off_, _len_) esp_flash_erase_region(esp_flash_default_chip, _off_, _len_)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static inline core_dump_crc_t esp_core_dump_calc_flash_config_crc(void)
|
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));
|
return crc32_le(0, (uint8_t const *)&s_core_flash_config.partition, sizeof(s_core_flash_config.partition));
|
||||||
|
@ -69,80 +72,87 @@ void esp_core_dump_flash_init(void)
|
||||||
s_core_flash_config.partition_config_crc = esp_core_dump_calc_flash_config_crc();
|
s_core_flash_config.partition_config_crc = esp_core_dump_calc_flash_config_crc();
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t esp_core_dump_write_flash_padded(size_t off, uint8_t *data, uint32_t data_size)
|
static esp_err_t esp_core_dump_flash_write_data(void *priv, uint8_t *data, uint32_t data_size)
|
||||||
{
|
{
|
||||||
esp_err_t err;
|
esp_err_t err;
|
||||||
uint32_t data_len = 0, k, len;
|
core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv;
|
||||||
union
|
uint32_t written = 0, wr_sz;
|
||||||
{
|
|
||||||
uint8_t data8[4];
|
|
||||||
uint32_t data32;
|
|
||||||
} rom_data;
|
|
||||||
|
|
||||||
data_len = (data_size / sizeof(uint32_t)) * sizeof(uint32_t);
|
assert((wr_data->off + data_size) < s_core_flash_config.partition.size);
|
||||||
|
|
||||||
assert(off >= s_core_flash_config.partition.start);
|
if (wr_data->cached_bytes) {
|
||||||
assert((off + data_len + (data_size % sizeof(uint32_t) ? sizeof(uint32_t) : 0)) <=
|
if ((sizeof(wr_data->cached_data)-wr_data->cached_bytes) > data_size)
|
||||||
s_core_flash_config.partition.start + s_core_flash_config.partition.size);
|
wr_sz = data_size;
|
||||||
|
else
|
||||||
#ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
|
wr_sz = sizeof(wr_data->cached_data)-wr_data->cached_bytes;
|
||||||
err = spi_flash_write(off, data, data_len);
|
// append to data cache
|
||||||
#else
|
memcpy(&wr_data->cached_data.data8[wr_data->cached_bytes], data, wr_sz);
|
||||||
err = esp_flash_write(esp_flash_default_chip, data, off, data_len);
|
wr_data->cached_bytes += wr_sz;
|
||||||
#endif
|
if (wr_data->cached_bytes == sizeof(wr_data->cached_data)) {
|
||||||
if (err != ESP_OK) {
|
err = ESP_COREDUMP_FLASH_WRITE(s_core_flash_config.partition.start + wr_data->off, &wr_data->cached_data, sizeof(wr_data->cached_data));
|
||||||
ESP_COREDUMP_LOGE("Failed to write data to flash (%d)!", err);
|
if (err != ESP_OK) {
|
||||||
return 0;
|
ESP_COREDUMP_LOGE("Failed to write cached data to flash (%d)!", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
// update checksum according to padding
|
||||||
|
esp_core_dump_checksum_update(wr_data, &wr_data->cached_data, sizeof(wr_data->cached_data));
|
||||||
|
// reset data cache
|
||||||
|
wr_data->cached_bytes = 0;
|
||||||
|
memset(&wr_data->cached_data, 0, sizeof(wr_data->cached_data));
|
||||||
|
}
|
||||||
|
wr_data->off += sizeof(wr_data->cached_data);
|
||||||
|
written += wr_sz;
|
||||||
|
data_size -= wr_sz;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = data_size % sizeof(uint32_t);
|
wr_sz = (data_size / sizeof(wr_data->cached_data)) * sizeof(wr_data->cached_data);
|
||||||
if (len) {
|
if (wr_sz) {
|
||||||
// write last bytes with padding, actual TCB len can be retrieved by esptool from core dump header
|
err = ESP_COREDUMP_FLASH_WRITE(s_core_flash_config.partition.start + wr_data->off, data + written, wr_sz);
|
||||||
rom_data.data32 = 0;
|
|
||||||
for (k = 0; k < len; k++) {
|
|
||||||
rom_data.data8[k] = *(data + data_len + k);
|
|
||||||
}
|
|
||||||
#ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
|
|
||||||
err = spi_flash_write(off + data_len, &rom_data, sizeof(uint32_t));
|
|
||||||
#else
|
|
||||||
err = esp_flash_write(esp_flash_default_chip, &rom_data, off + data_len, sizeof(uint32_t));
|
|
||||||
#endif
|
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_COREDUMP_LOGE("Failed to finish write data to flash (%d)!", err);
|
ESP_COREDUMP_LOGE("Failed to write data to flash (%d)!", err);
|
||||||
return 0;
|
return err;
|
||||||
}
|
}
|
||||||
data_len += sizeof(uint32_t);
|
// update checksum of data written
|
||||||
|
esp_core_dump_checksum_update(wr_data, data + written, wr_sz);
|
||||||
|
wr_data->off += wr_sz;
|
||||||
|
written += wr_sz;
|
||||||
|
data_size -= wr_sz;
|
||||||
}
|
}
|
||||||
|
|
||||||
return data_len;
|
if (data_size > 0) {
|
||||||
|
// append to data cache
|
||||||
|
memcpy(&wr_data->cached_data, data + written, data_size);
|
||||||
|
wr_data->cached_bytes = data_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_len)
|
static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_len)
|
||||||
{
|
{
|
||||||
esp_err_t err;
|
esp_err_t err;
|
||||||
uint32_t sec_num;
|
uint32_t sec_num;
|
||||||
core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv;
|
core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv;
|
||||||
|
uint32_t cs_len;
|
||||||
|
cs_len = esp_core_dump_checksum_finish(wr_data, NULL);
|
||||||
|
|
||||||
// check for available space in partition
|
// check for available space in partition
|
||||||
if ((*data_len + sizeof(uint32_t)) > s_core_flash_config.partition.size) {
|
if ((*data_len + cs_len) > s_core_flash_config.partition.size) {
|
||||||
ESP_COREDUMP_LOGE("Not enough space to save core dump!");
|
ESP_COREDUMP_LOGE("Not enough space to save core dump!");
|
||||||
return ESP_ERR_NO_MEM;
|
return ESP_ERR_NO_MEM;
|
||||||
}
|
}
|
||||||
// add space for CRC
|
// add space for checksum
|
||||||
*data_len += sizeof(core_dump_crc_t);
|
*data_len += cs_len;
|
||||||
|
|
||||||
memset(wr_data, 0, sizeof(*wr_data));
|
memset(wr_data, 0, sizeof(core_dump_write_data_t));
|
||||||
|
|
||||||
sec_num = *data_len / SPI_FLASH_SEC_SIZE;
|
sec_num = *data_len / SPI_FLASH_SEC_SIZE;
|
||||||
if (*data_len % SPI_FLASH_SEC_SIZE) {
|
if (*data_len % SPI_FLASH_SEC_SIZE) {
|
||||||
sec_num++;
|
sec_num++;
|
||||||
}
|
}
|
||||||
|
ESP_COREDUMP_LOGI("Erase flash %d bytes @ 0x%x", sec_num * SPI_FLASH_SEC_SIZE, s_core_flash_config.partition.start + 0);
|
||||||
assert(sec_num * SPI_FLASH_SEC_SIZE <= s_core_flash_config.partition.size);
|
assert(sec_num * SPI_FLASH_SEC_SIZE <= s_core_flash_config.partition.size);
|
||||||
#ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
|
err = ESP_COREDUMP_FLASH_ERASE(s_core_flash_config.partition.start + 0, sec_num * SPI_FLASH_SEC_SIZE);
|
||||||
err = spi_flash_erase_range(s_core_flash_config.partition.start + 0, sec_num * SPI_FLASH_SEC_SIZE);
|
|
||||||
#else
|
|
||||||
err = esp_flash_erase_region(esp_flash_default_chip, s_core_flash_config.partition.start + 0, sec_num * SPI_FLASH_SEC_SIZE);
|
|
||||||
#endif
|
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_COREDUMP_LOGE("Failed to erase flash (%d)!", err);
|
ESP_COREDUMP_LOGE("Failed to erase flash (%d)!", err);
|
||||||
return err;
|
return err;
|
||||||
|
@ -150,81 +160,70 @@ static esp_err_t esp_core_dump_flash_write_prepare(void *priv, uint32_t *data_le
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t esp_core_dump_flash_write_word(core_dump_write_flash_data_t *wr_data, uint32_t word)
|
|
||||||
{
|
|
||||||
esp_err_t err = ESP_OK;
|
|
||||||
uint32_t data32 = word;
|
|
||||||
|
|
||||||
assert(wr_data->off + sizeof(uint32_t) <= s_core_flash_config.partition.size);
|
|
||||||
#ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
|
|
||||||
err = spi_flash_write(s_core_flash_config.partition.start + wr_data->off, &data32, sizeof(uint32_t));
|
|
||||||
#else
|
|
||||||
err = esp_flash_write(esp_flash_default_chip, &data32, s_core_flash_config.partition.start + wr_data->off, sizeof(uint32_t));
|
|
||||||
#endif
|
|
||||||
if (err != ESP_OK) {
|
|
||||||
ESP_COREDUMP_LOGE("Failed to write to flash (%d)!", err);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
wr_data->off += sizeof(uint32_t);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t esp_core_dump_flash_write_start(void *priv)
|
static esp_err_t esp_core_dump_flash_write_start(void *priv)
|
||||||
{
|
{
|
||||||
|
core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv;
|
||||||
|
esp_core_dump_checksum_init(wr_data);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t esp_core_dump_flash_write_end(void *priv)
|
static esp_err_t esp_core_dump_flash_write_end(void *priv)
|
||||||
{
|
{
|
||||||
core_dump_write_flash_data_t *wr_data = (core_dump_write_flash_data_t *)priv;
|
esp_err_t err;
|
||||||
|
core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv;
|
||||||
|
void* checksum;
|
||||||
|
uint32_t cs_len = esp_core_dump_checksum_finish(wr_data, &checksum);
|
||||||
|
|
||||||
|
// flush cached bytes with zero padding
|
||||||
|
if (wr_data->cached_bytes) {
|
||||||
|
err = ESP_COREDUMP_FLASH_WRITE(s_core_flash_config.partition.start + wr_data->off, &wr_data->cached_data, sizeof(wr_data->cached_data));
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_COREDUMP_LOGE("Failed to flush cached data to flash (%d)!", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
// update checksum according to padding
|
||||||
|
esp_core_dump_checksum_update(wr_data, &wr_data->cached_data, sizeof(wr_data->cached_data));
|
||||||
|
wr_data->off += sizeof(wr_data->cached_data);
|
||||||
|
}
|
||||||
|
err = ESP_COREDUMP_FLASH_WRITE(s_core_flash_config.partition.start + wr_data->off, checksum, cs_len);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_COREDUMP_LOGE("Failed to flush cached data to flash (%d)!", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
wr_data->off += cs_len;
|
||||||
|
ESP_COREDUMP_LOGI("Write end offset 0x%x, check sum length %d", wr_data->off, cs_len);
|
||||||
#if LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG
|
#if LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
uint8_t data8[16];
|
uint8_t data8[sizeof(core_dump_header_t)];
|
||||||
uint32_t data32[4];
|
uint32_t data32[sizeof(core_dump_header_t)/sizeof(uint32_t)];
|
||||||
} rom_data;
|
} rom_data;
|
||||||
|
err = ESP_COREDUMP_FLASH_READ(s_core_flash_config.partition.start + 0, &rom_data, sizeof(rom_data));
|
||||||
#ifdef CONFIG_SPI_FLASH_USE_LEGACY_IMPL
|
|
||||||
esp_err_t err = spi_flash_read(s_core_flash_config.partition.start + 0, &rom_data, sizeof(rom_data));
|
|
||||||
#else
|
|
||||||
esp_err_t err = esp_flash_read(esp_flash_default_chip, &rom_data, s_core_flash_config.partition.start + 0, sizeof(rom_data));
|
|
||||||
#endif
|
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_COREDUMP_LOGE("Failed to read flash (%d)!", err);
|
ESP_COREDUMP_LOGE("Failed to read back coredump header (%d)!", err);
|
||||||
return err;
|
return err;
|
||||||
} else {
|
} else {
|
||||||
ESP_COREDUMP_LOG_PROCESS("Data from flash:");
|
ESP_COREDUMP_LOG_PROCESS("Core dump header words from flash:");
|
||||||
for (uint32_t i = 0; i < sizeof(rom_data)/sizeof(rom_data.data32[0]); i++) {
|
for (uint32_t i = 0; i < sizeof(rom_data)/sizeof(uint32_t); i++) {
|
||||||
ESP_COREDUMP_LOG_PROCESS("%x", rom_data.data32[i]);
|
ESP_COREDUMP_LOG_PROCESS("0x%x", rom_data.data32[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
uint32_t crc;
|
||||||
// write core dump CRC
|
err = ESP_COREDUMP_FLASH_READ(s_core_flash_config.partition.start + wr_data->off - cs_len, &crc, sizeof(crc));
|
||||||
ESP_COREDUMP_LOG_PROCESS("Dump data CRC = 0x%x", wr_data->crc);
|
if (err != ESP_OK) {
|
||||||
return esp_core_dump_flash_write_word(wr_data, wr_data->crc);
|
ESP_COREDUMP_LOGE("Failed to read back checksum word (%d)!", err);
|
||||||
}
|
return err;
|
||||||
|
} else {
|
||||||
static esp_err_t esp_core_dump_flash_write_data(void *priv, void * data, uint32_t data_len)
|
ESP_COREDUMP_LOG_PROCESS("Checksum word from flash: 0x%x @ 0x%x", crc, wr_data->off - cs_len);
|
||||||
{
|
|
||||||
esp_err_t err = ESP_OK;
|
|
||||||
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) {
|
|
||||||
return ESP_FAIL;
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
wr_data->off += len;
|
|
||||||
wr_data->crc = crc32_le(wr_data->crc, data, data_len);
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
void esp_core_dump_to_flash(XtExcFrame *frame)
|
void esp_core_dump_to_flash(void *frame)
|
||||||
{
|
{
|
||||||
static core_dump_write_config_t wr_cfg;
|
static core_dump_write_config_t wr_cfg;
|
||||||
static core_dump_write_flash_data_t wr_data;
|
static core_dump_write_data_t wr_data;
|
||||||
|
|
||||||
core_dump_crc_t crc = esp_core_dump_calc_flash_config_crc();
|
core_dump_crc_t crc = esp_core_dump_calc_flash_config_crc();
|
||||||
if (s_core_flash_config.partition_config_crc != crc) {
|
if (s_core_flash_config.partition_config_crc != crc) {
|
||||||
|
@ -245,11 +244,11 @@ void esp_core_dump_to_flash(XtExcFrame *frame)
|
||||||
wr_cfg.prepare = esp_core_dump_flash_write_prepare;
|
wr_cfg.prepare = esp_core_dump_flash_write_prepare;
|
||||||
wr_cfg.start = esp_core_dump_flash_write_start;
|
wr_cfg.start = esp_core_dump_flash_write_start;
|
||||||
wr_cfg.end = esp_core_dump_flash_write_end;
|
wr_cfg.end = esp_core_dump_flash_write_end;
|
||||||
wr_cfg.write = esp_core_dump_flash_write_data;
|
wr_cfg.write = (esp_core_dump_flash_write_data_t)esp_core_dump_flash_write_data;
|
||||||
wr_cfg.priv = &wr_data;
|
wr_cfg.priv = &wr_data;
|
||||||
|
|
||||||
ESP_COREDUMP_LOGI("Save core dump to flash...");
|
ESP_COREDUMP_LOGI("Save core dump to flash...");
|
||||||
esp_core_dump_write((void*)frame, &wr_cfg);
|
esp_core_dump_write(frame, &wr_cfg);
|
||||||
ESP_COREDUMP_LOGI("Core dump has been saved to flash.");
|
ESP_COREDUMP_LOGI("Core dump has been saved to flash.");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -14,92 +14,544 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "soc/soc_memory_layout.h"
|
#include "soc/soc_memory_layout.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/xtensa_context.h" // for exception register stack structure
|
||||||
#include "esp_core_dump_priv.h"
|
#include "esp_core_dump_priv.h"
|
||||||
|
|
||||||
const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_port";
|
const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_port";
|
||||||
|
|
||||||
|
#define COREDUMP_EM_XTENSA 0x5E
|
||||||
|
#define COREDUMP_INVALID_CAUSE_VALUE 0xFFFF
|
||||||
|
#define COREDUMP_EXTRA_REG_NUM 16
|
||||||
|
#define COREDUMP_FAKE_STACK_START 0x20000000
|
||||||
|
#define COREDUMP_FAKE_STACK_LIMIT 0x30000000
|
||||||
|
|
||||||
|
#define COREDUMP_GET_REG_PAIR(reg_idx, reg_ptr) { *(uint32_t*)(reg_ptr++) = (uint32_t)reg_idx; \
|
||||||
|
RSR(reg_idx, *(uint32_t*)(reg_ptr++)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enumeration of registers of exception stack frame
|
||||||
|
// and solicited stack frame
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
// XT_SOL_EXIT = 0,
|
||||||
|
XT_SOL_PC = 1,
|
||||||
|
XT_SOL_PS = 2,
|
||||||
|
// XT_SOL_NEXT = 3,
|
||||||
|
XT_SOL_AR_START = 4,
|
||||||
|
XT_SOL_AR_NUM = 4,
|
||||||
|
// XT_SOL_FRMSZ = 8,
|
||||||
|
XT_STK_EXIT = 0,
|
||||||
|
XT_STK_PC = 1,
|
||||||
|
XT_STK_PS = 2,
|
||||||
|
XT_STK_AR_START = 3,
|
||||||
|
XT_STK_AR_NUM = 16,
|
||||||
|
XT_STK_SAR = 19,
|
||||||
|
XT_STK_EXCCAUSE = 20,
|
||||||
|
XT_STK_EXCVADDR = 21,
|
||||||
|
XT_STK_LBEG = 22,
|
||||||
|
XT_STK_LEND = 23,
|
||||||
|
XT_STK_LCOUNT = 24,
|
||||||
|
//XT_STK_FRMSZ = 25,
|
||||||
|
} stk_frame_t;
|
||||||
|
|
||||||
|
// Xtensa ELF core file register set representation ('.reg' section).
|
||||||
|
// Copied from target-side ELF header <xtensa/elf.h>.
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t pc;
|
||||||
|
uint32_t ps;
|
||||||
|
uint32_t lbeg;
|
||||||
|
uint32_t lend;
|
||||||
|
uint32_t lcount;
|
||||||
|
uint32_t sar;
|
||||||
|
uint32_t windowstart;
|
||||||
|
uint32_t windowbase;
|
||||||
|
uint32_t reserved[8+48];
|
||||||
|
uint32_t ar[XCHAL_NUM_AREGS];
|
||||||
|
} __attribute__((packed)) xtensa_gregset_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t reg_index;
|
||||||
|
uint32_t reg_val;
|
||||||
|
} __attribute__((packed)) core_dump_reg_pair_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t crashed_task_tcb;
|
||||||
|
core_dump_reg_pair_t exccause;
|
||||||
|
core_dump_reg_pair_t excvaddr;
|
||||||
|
core_dump_reg_pair_t extra_regs[COREDUMP_EXTRA_REG_NUM];
|
||||||
|
} __attribute__((packed)) xtensa_extra_info_t;
|
||||||
|
|
||||||
|
// Xtensa Program Status for GDB
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32_t si_signo;
|
||||||
|
uint32_t si_code;
|
||||||
|
uint32_t si_errno;
|
||||||
|
uint16_t pr_cursig;
|
||||||
|
uint16_t pr_pad0;
|
||||||
|
uint32_t pr_sigpend;
|
||||||
|
uint32_t pr_sighold;
|
||||||
|
uint32_t pr_pid;
|
||||||
|
uint32_t pr_ppid;
|
||||||
|
uint32_t pr_pgrp;
|
||||||
|
uint32_t pr_sid;
|
||||||
|
uint64_t pr_utime;
|
||||||
|
uint64_t pr_stime;
|
||||||
|
uint64_t pr_cutime;
|
||||||
|
uint64_t pr_cstime;
|
||||||
|
} __attribute__((packed)) xtensa_pr_status_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
xtensa_pr_status_t pr_status;
|
||||||
|
xtensa_gregset_t regs;
|
||||||
|
// Todo: acc to xtensa_gregset_t number of regs must be 128,
|
||||||
|
// but gdb complains when it less than 129
|
||||||
|
uint32_t reserved;
|
||||||
|
} __attribute__((packed)) xtensa_elf_reg_dump_t;
|
||||||
|
|
||||||
|
extern uint8_t port_IntStack;
|
||||||
|
|
||||||
#if CONFIG_ESP32_ENABLE_COREDUMP
|
#if CONFIG_ESP32_ENABLE_COREDUMP
|
||||||
|
|
||||||
inline bool esp_task_stack_start_is_sane(uint32_t sp)
|
static uint32_t s_total_length = 0;
|
||||||
|
|
||||||
|
static XtExcFrame s_fake_stack_frame = {
|
||||||
|
.pc = (UBaseType_t) COREDUMP_FAKE_STACK_START, // task entrypoint fake_ptr
|
||||||
|
.a0 = (UBaseType_t) 0, // to terminate GDB backtrace
|
||||||
|
.a1 = (UBaseType_t) (COREDUMP_FAKE_STACK_START + sizeof(XtExcFrame)), // physical top of stack frame
|
||||||
|
.exit = (UBaseType_t) 0, // user exception exit dispatcher
|
||||||
|
.ps = (PS_UM | PS_EXCM),
|
||||||
|
.exccause = (UBaseType_t) COREDUMP_INVALID_CAUSE_VALUE,
|
||||||
|
};
|
||||||
|
static uint32_t s_fake_stacks_num;
|
||||||
|
|
||||||
|
static xtensa_extra_info_t s_extra_info;
|
||||||
|
|
||||||
|
#if ESP32_CORE_DUMP_STACK_SIZE > 0
|
||||||
|
uint8_t s_coredump_stack[ESP32_CORE_DUMP_STACK_SIZE];
|
||||||
|
uint8_t *s_core_dump_sp;
|
||||||
|
|
||||||
|
static uint32_t esp_core_dump_free_stack_space(const uint8_t *pucStackByte)
|
||||||
{
|
{
|
||||||
return !(sp < 0x3ffae010UL || sp > 0x3fffffffUL);
|
uint32_t ulCount = 0U;
|
||||||
|
while( *pucStackByte == (uint8_t)COREDUMP_STACK_FILL_BYTE ) {
|
||||||
|
pucStackByte -= portSTACK_GROWTH;
|
||||||
|
ulCount++;
|
||||||
|
}
|
||||||
|
ulCount /= (uint32_t)sizeof(uint8_t);
|
||||||
|
return (uint32_t)ulCount;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void esp_core_dump_report_stack_usage(void)
|
||||||
|
{
|
||||||
|
#if ESP32_CORE_DUMP_STACK_SIZE > 0
|
||||||
|
uint32_t bytes_free = esp_core_dump_free_stack_space(s_coredump_stack);
|
||||||
|
ESP_COREDUMP_LOGD("Core dump used %u bytes on stack. %u bytes left free.",
|
||||||
|
s_core_dump_sp - s_coredump_stack - bytes_free, bytes_free);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool esp_tcb_addr_is_sane(uint32_t addr, uint32_t sz)
|
#if CONFIG_ESP32_COREDUMP_CHECKSUM_SHA256
|
||||||
|
|
||||||
|
// function to calculate SHA256 for solid data array
|
||||||
|
int esp_core_dump_sha(mbedtls_sha256_context *ctx,
|
||||||
|
const unsigned char *input,
|
||||||
|
size_t ilen,
|
||||||
|
unsigned char output[32] )
|
||||||
{
|
{
|
||||||
//TODO: currently core dump supports TCBs in DRAM only, external SRAM not supported yet
|
assert(input);
|
||||||
return !(addr < 0x3ffae000UL || (addr + sz) > 0x40000000UL);
|
mbedtls_sha256_init(ctx);
|
||||||
|
if((mbedtls_sha256_starts_ret(ctx, 0) != 0)) goto exit;
|
||||||
|
#if CONFIG_MBEDTLS_HARDWARE_SHA
|
||||||
|
// set software mode for SHA calculation
|
||||||
|
ctx->mode = ESP_MBEDTLS_SHA256_SOFTWARE;
|
||||||
|
#endif
|
||||||
|
if((mbedtls_sha256_update_ret(ctx, input, ilen) != 0)) goto exit;
|
||||||
|
if((mbedtls_sha256_finish_ret(ctx, output) != 0)) goto exit;
|
||||||
|
esp_core_dump_print_sha256(DRAM_STR("Coredump SHA256"), (void*)output);
|
||||||
|
s_total_length = ilen;
|
||||||
|
exit:
|
||||||
|
mbedtls_sha256_free(ctx);
|
||||||
|
return ilen;
|
||||||
|
}
|
||||||
|
|
||||||
|
void esp_core_dump_print_sha256(const char* msg, const uint8_t* sha_output)
|
||||||
|
{
|
||||||
|
ets_printf(DRAM_STR("%s='"), msg);
|
||||||
|
for (int i = 0; i < COREDUMP_SHA256_LEN; i++) {
|
||||||
|
ets_printf(DRAM_STR("%02x"), sha_output[i]);
|
||||||
|
}
|
||||||
|
ets_printf(DRAM_STR("'\r\n"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void esp_core_dump_checksum_init(core_dump_write_data_t* wr_data)
|
||||||
|
{
|
||||||
|
if (wr_data) {
|
||||||
|
#if CONFIG_ESP32_COREDUMP_CHECKSUM_CRC32
|
||||||
|
wr_data->crc = 0;
|
||||||
|
#elif CONFIG_ESP32_COREDUMP_CHECKSUM_SHA256
|
||||||
|
mbedtls_sha256_init(&wr_data->ctx);
|
||||||
|
(void)mbedtls_sha256_starts_ret(&wr_data->ctx, 0);
|
||||||
|
#endif
|
||||||
|
s_total_length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void esp_core_dump_checksum_update(core_dump_write_data_t* wr_data, void* data, size_t data_len)
|
||||||
|
{
|
||||||
|
if (wr_data && data) {
|
||||||
|
#if CONFIG_ESP32_COREDUMP_CHECKSUM_CRC32
|
||||||
|
wr_data->crc = crc32_le(wr_data->crc, data, data_len);
|
||||||
|
#elif CONFIG_ESP32_COREDUMP_CHECKSUM_SHA256
|
||||||
|
#if CONFIG_MBEDTLS_HARDWARE_SHA
|
||||||
|
// set software mode of SHA calculation
|
||||||
|
wr_data->ctx.mode = ESP_MBEDTLS_SHA256_SOFTWARE;
|
||||||
|
#endif
|
||||||
|
(void)mbedtls_sha256_update_ret(&wr_data->ctx, data, data_len);
|
||||||
|
#endif
|
||||||
|
s_total_length += data_len; // keep counter of cashed bytes
|
||||||
|
} else {
|
||||||
|
ESP_COREDUMP_LOGE("Wrong write data info!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t esp_core_dump_checksum_finish(core_dump_write_data_t* wr_data, void** chs_ptr)
|
||||||
|
{
|
||||||
|
// get core dump checksum
|
||||||
|
uint32_t chs_len = 0;
|
||||||
|
#if CONFIG_ESP32_COREDUMP_CHECKSUM_CRC32
|
||||||
|
if (chs_ptr) {
|
||||||
|
wr_data->crc = wr_data->crc;
|
||||||
|
*chs_ptr = (void*)&wr_data->crc;
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Dump data CRC = 0x%x, offset = 0x%x", wr_data->crc, wr_data->off);
|
||||||
|
}
|
||||||
|
chs_len = sizeof(wr_data->crc);
|
||||||
|
#elif CONFIG_ESP32_COREDUMP_CHECKSUM_SHA256
|
||||||
|
if (chs_ptr) {
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Dump data offset = %d", wr_data->off);
|
||||||
|
(void)mbedtls_sha256_finish_ret(&wr_data->ctx, (uint8_t*)&wr_data->sha_output);
|
||||||
|
*chs_ptr = (void*)&wr_data->sha_output[0];
|
||||||
|
mbedtls_sha256_free(&wr_data->ctx);
|
||||||
|
}
|
||||||
|
chs_len = sizeof(wr_data->sha_output);
|
||||||
|
#endif
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Total length of hashed data: %d!", s_total_length);
|
||||||
|
return chs_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint16_t esp_core_dump_get_arch_id()
|
||||||
|
{
|
||||||
|
return COREDUMP_EM_XTENSA;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool esp_core_dump_mem_seg_is_sane(uint32_t addr, uint32_t sz)
|
||||||
|
{
|
||||||
|
//TODO: currently core dump supports memory segments in DRAM only, external SRAM not supported yet
|
||||||
|
return esp_ptr_in_dram((void *)addr) && esp_ptr_in_dram((void *)(addr+sz-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool esp_core_dump_task_stack_end_is_sane(uint32_t sp)
|
||||||
|
{
|
||||||
|
//TODO: currently core dump supports stacks in DRAM only, external SRAM not supported yet
|
||||||
|
return esp_ptr_in_dram((void *)sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool esp_core_dump_tcb_addr_is_sane(uint32_t addr)
|
||||||
|
{
|
||||||
|
return esp_core_dump_mem_seg_is_sane(addr, COREDUMP_TCB_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t esp_core_dump_get_tasks_snapshot(core_dump_task_header_t* const tasks,
|
uint32_t esp_core_dump_get_tasks_snapshot(core_dump_task_header_t* const tasks,
|
||||||
const uint32_t snapshot_size, uint32_t* const tcb_sz)
|
const uint32_t snapshot_size)
|
||||||
{
|
{
|
||||||
uint32_t task_num = (uint32_t)uxTaskGetSnapshotAll((TaskSnapshot_t*)tasks, (UBaseType_t)snapshot_size, (UBaseType_t*)tcb_sz);
|
uint32_t tcb_sz; // unused
|
||||||
|
uint32_t task_num = (uint32_t)uxTaskGetSnapshotAll((TaskSnapshot_t*)tasks,
|
||||||
|
(UBaseType_t)snapshot_size,
|
||||||
|
(UBaseType_t*)&tcb_sz);
|
||||||
return task_num;
|
return task_num;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool esp_core_dump_process_tcb(void *frame, core_dump_task_header_t *task_snaphort, uint32_t tcb_sz)
|
inline uint32_t esp_core_dump_get_isr_stack_end(void)
|
||||||
{
|
{
|
||||||
XtExcFrame *exc_frame = (XtExcFrame*)frame;
|
return (uint32_t)((uint8_t *)&port_IntStack + (xPortGetCoreID()+1)*configISR_STACK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
if (!esp_tcb_addr_is_sane((uint32_t)task_snaphort->tcb_addr, tcb_sz)) {
|
uint32_t esp_core_dump_get_stack(core_dump_task_header_t *task_snapshot,
|
||||||
ESP_COREDUMP_LOG_PROCESS("Bad TCB addr %x!", task_snaphort->tcb_addr);
|
uint32_t *stk_vaddr, uint32_t *stk_len)
|
||||||
|
{
|
||||||
|
if (task_snapshot->stack_end > task_snapshot->stack_start) {
|
||||||
|
*stk_len = task_snapshot->stack_end - task_snapshot->stack_start;
|
||||||
|
*stk_vaddr = task_snapshot->stack_start;
|
||||||
|
} else {
|
||||||
|
*stk_len = task_snapshot->stack_start - task_snapshot->stack_end;
|
||||||
|
*stk_vaddr = task_snapshot->stack_end;
|
||||||
|
}
|
||||||
|
if (*stk_vaddr >= COREDUMP_FAKE_STACK_START && *stk_vaddr < COREDUMP_FAKE_STACK_LIMIT) {
|
||||||
|
return (uint32_t)&s_fake_stack_frame;
|
||||||
|
}
|
||||||
|
return *stk_vaddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The function creates small fake stack for task as deep as exception frame size
|
||||||
|
// It is required for gdb to take task into account but avoid back trace of stack.
|
||||||
|
// The espcoredump.py script is able to recognize that task is broken
|
||||||
|
static void *esp_core_dump_get_fake_stack(uint32_t *stk_len)
|
||||||
|
{
|
||||||
|
*stk_len = sizeof(s_fake_stack_frame);
|
||||||
|
return (uint8_t*)COREDUMP_FAKE_STACK_START + sizeof(s_fake_stack_frame)*s_fake_stacks_num++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static core_dump_reg_pair_t *esp_core_dump_get_epc_regs(core_dump_reg_pair_t* src)
|
||||||
|
{
|
||||||
|
uint32_t* reg_ptr = (uint32_t*)src;
|
||||||
|
// get InterruptException program counter registers
|
||||||
|
COREDUMP_GET_REG_PAIR(EPC_1, reg_ptr);
|
||||||
|
COREDUMP_GET_REG_PAIR(EPC_2, reg_ptr);
|
||||||
|
COREDUMP_GET_REG_PAIR(EPC_3, reg_ptr);
|
||||||
|
COREDUMP_GET_REG_PAIR(EPC_4, reg_ptr);
|
||||||
|
COREDUMP_GET_REG_PAIR(EPC_5, reg_ptr);
|
||||||
|
COREDUMP_GET_REG_PAIR(EPC_6, reg_ptr);
|
||||||
|
COREDUMP_GET_REG_PAIR(EPC_7, reg_ptr);
|
||||||
|
return (core_dump_reg_pair_t*)reg_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static core_dump_reg_pair_t *esp_core_dump_get_eps_regs(core_dump_reg_pair_t* src)
|
||||||
|
{
|
||||||
|
uint32_t* reg_ptr = (uint32_t*)src;
|
||||||
|
// get InterruptException processor state registers
|
||||||
|
COREDUMP_GET_REG_PAIR(EPS_2, reg_ptr);
|
||||||
|
COREDUMP_GET_REG_PAIR(EPS_3, reg_ptr);
|
||||||
|
COREDUMP_GET_REG_PAIR(EPS_4, reg_ptr);
|
||||||
|
COREDUMP_GET_REG_PAIR(EPS_5, reg_ptr);
|
||||||
|
COREDUMP_GET_REG_PAIR(EPS_6, reg_ptr);
|
||||||
|
COREDUMP_GET_REG_PAIR(EPS_7, reg_ptr);
|
||||||
|
return (core_dump_reg_pair_t*)reg_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns list of registers (in GDB format) from xtensa stack frame
|
||||||
|
static esp_err_t esp_core_dump_get_regs_from_stack(void* stack_addr,
|
||||||
|
size_t size,
|
||||||
|
xtensa_gregset_t* regs)
|
||||||
|
{
|
||||||
|
XtExcFrame* exc_frame = (XtExcFrame*)stack_addr;
|
||||||
|
uint32_t* stack_arr = (uint32_t*)stack_addr;
|
||||||
|
|
||||||
|
if (size < sizeof(XtExcFrame)) {
|
||||||
|
ESP_COREDUMP_LOGE("Too small stack to keep frame: %d bytes!", size);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stack frame type indicator is always the first item
|
||||||
|
uint32_t rc = exc_frame->exit;
|
||||||
|
|
||||||
|
// is this current crashed task?
|
||||||
|
if (rc == COREDUMP_CURR_TASK_MARKER)
|
||||||
|
{
|
||||||
|
s_extra_info.exccause.reg_val = exc_frame->exccause;
|
||||||
|
s_extra_info.exccause.reg_index = EXCCAUSE;
|
||||||
|
s_extra_info.excvaddr.reg_val = exc_frame->excvaddr;
|
||||||
|
s_extra_info.excvaddr.reg_index = EXCVADDR;
|
||||||
|
// get InterruptException registers into extra_info
|
||||||
|
core_dump_reg_pair_t *regs_ptr = esp_core_dump_get_eps_regs(s_extra_info.extra_regs);
|
||||||
|
esp_core_dump_get_epc_regs(regs_ptr);
|
||||||
|
} else {
|
||||||
|
// initialize EXCCAUSE and EXCVADDR members of frames for all the tasks,
|
||||||
|
// except for the crashed one
|
||||||
|
exc_frame->exccause = COREDUMP_INVALID_CAUSE_VALUE;
|
||||||
|
exc_frame->excvaddr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc != 0) {
|
||||||
|
regs->pc = exc_frame->pc;
|
||||||
|
regs->ps = exc_frame->ps;
|
||||||
|
for (int i = 0; i < XT_STK_AR_NUM; i++) {
|
||||||
|
regs->ar[i] = stack_arr[XT_STK_AR_START + i];
|
||||||
|
}
|
||||||
|
regs->sar = exc_frame->sar;
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
regs->lbeg = exc_frame->lbeg;
|
||||||
|
regs->lend = exc_frame->lend;
|
||||||
|
regs->lcount = exc_frame->lcount;
|
||||||
|
#endif
|
||||||
|
// FIXME: crashed and some running tasks (e.g. prvIdleTask) have EXCM bit set
|
||||||
|
// and GDB can not unwind callstack properly (it implies not windowed call0)
|
||||||
|
if (regs->ps & PS_UM) {
|
||||||
|
regs->ps &= ~PS_EXCM;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
regs->pc = stack_arr[XT_SOL_PC];
|
||||||
|
regs->ps = stack_arr[XT_SOL_PS];
|
||||||
|
for (int i = 0; i < XT_SOL_AR_NUM; i++) {
|
||||||
|
regs->ar[i] = stack_arr[XT_SOL_AR_START + i];
|
||||||
|
}
|
||||||
|
regs->pc = (regs->pc & 0x3fffffff);
|
||||||
|
if (regs->pc & 0x80000000) {
|
||||||
|
regs->pc = (regs->pc & 0x3fffffff);
|
||||||
|
}
|
||||||
|
if (regs->ar[0] & 0x80000000) {
|
||||||
|
regs->ar[0] = (regs->ar[0] & 0x3fffffff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t esp_core_dump_get_task_regs_dump(core_dump_task_header_t *task, void **reg_dump)
|
||||||
|
{
|
||||||
|
uint32_t stack_vaddr, stack_paddr, stack_len;
|
||||||
|
static xtensa_elf_reg_dump_t s_reg_dump = { 0 };
|
||||||
|
|
||||||
|
stack_paddr = esp_core_dump_get_stack(task, &stack_vaddr, &stack_len);
|
||||||
|
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Add regs for task 0x%x", task->tcb_addr);
|
||||||
|
|
||||||
|
// initialize program status for the task
|
||||||
|
s_reg_dump.pr_status.pr_cursig = 0;
|
||||||
|
s_reg_dump.pr_status.pr_pid = (uint32_t)task->tcb_addr;
|
||||||
|
|
||||||
|
// fill the gdb registers structure from stack
|
||||||
|
esp_err_t err = esp_core_dump_get_regs_from_stack((void*)stack_paddr,
|
||||||
|
stack_len,
|
||||||
|
&s_reg_dump.regs);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_COREDUMP_LOGE("Error while registers processing.");
|
||||||
|
}
|
||||||
|
*reg_dump = &s_reg_dump;
|
||||||
|
return sizeof(s_reg_dump);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void* esp_core_dump_get_current_task_handle()
|
||||||
|
{
|
||||||
|
return (void*)xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool esp_core_dump_check_task(void *frame,
|
||||||
|
core_dump_task_header_t *task,
|
||||||
|
bool* is_current,
|
||||||
|
bool* stack_is_valid)
|
||||||
|
{
|
||||||
|
XtExcFrame *exc_frame = frame;
|
||||||
|
bool is_curr_task = false;
|
||||||
|
bool stack_is_sane = false;
|
||||||
|
uint32_t stk_size = 0;
|
||||||
|
|
||||||
|
if (!esp_core_dump_tcb_addr_is_sane((uint32_t)task->tcb_addr)) {
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Bad TCB addr=%x!", task->tcb_addr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (task_snaphort->tcb_addr == xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID())) {
|
|
||||||
// Set correct stack top for current task
|
is_curr_task = task->tcb_addr == esp_core_dump_get_current_task_handle();
|
||||||
task_snaphort->stack_start = (uint32_t)exc_frame;
|
if (is_curr_task) {
|
||||||
// This field is not initialized for crashed task, but stack frame has the structure of interrupt one,
|
// Set correct stack top for current task; only modify if we came from the task,
|
||||||
// so make workaround to allow espcoredump to parse it properly.
|
// and not an ISR that crashed.
|
||||||
if (exc_frame->exit == 0)
|
if (!xPortInterruptedFromISRContext()) {
|
||||||
exc_frame->exit = -1;
|
task->stack_start = (uint32_t)exc_frame;
|
||||||
ESP_COREDUMP_LOG_PROCESS("Current task %x EXIT/PC/PS/A0/SP %x %x %x %x %x",
|
|
||||||
task_snaphort->tcb_addr, exc_frame->exit, exc_frame->pc, exc_frame->ps, exc_frame->a0, exc_frame->a1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
XtSolFrame *task_frame = (XtSolFrame *)task_snaphort->stack_start;
|
|
||||||
if (task_frame->exit == 0) {
|
|
||||||
ESP_COREDUMP_LOG_PROCESS("Task %x EXIT/PC/PS/A0/SP %x %x %x %x %x",
|
|
||||||
task_snaphort->tcb_addr, task_frame->exit, task_frame->pc, task_frame->ps, task_frame->a0, task_frame->a1);
|
|
||||||
}
|
}
|
||||||
else {
|
exc_frame->exit = COREDUMP_CURR_TASK_MARKER;
|
||||||
|
s_extra_info.crashed_task_tcb = (uint32_t)task->tcb_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_is_sane = esp_core_dump_check_stack(task->stack_start, task->stack_end);
|
||||||
|
if (!stack_is_sane) {
|
||||||
|
// Skip saving of invalid task if stack corrupted
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Task (TCB:%x), stack is corrupted (%x, %x)",
|
||||||
|
task->tcb_addr,
|
||||||
|
task->stack_start,
|
||||||
|
task->stack_end);
|
||||||
|
task->stack_start = (uint32_t)esp_core_dump_get_fake_stack(&stk_size);
|
||||||
|
task->stack_end = (uint32_t)(task->stack_start + stk_size);
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Task (TCB:%x), use start, end (%x, %x)",
|
||||||
|
task->tcb_addr,
|
||||||
|
task->stack_start,
|
||||||
|
task->stack_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_curr_task) {
|
||||||
|
if (!stack_is_sane)
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Current task 0x%x is broken!", task->tcb_addr);
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Current task (TCB:%x), EXIT/PC/PS/A0/SP %x %x %x %x %x",
|
||||||
|
task->tcb_addr,
|
||||||
|
exc_frame->exit,
|
||||||
|
exc_frame->pc,
|
||||||
|
exc_frame->ps,
|
||||||
|
exc_frame->a0,
|
||||||
|
exc_frame->a1);
|
||||||
|
} else {
|
||||||
|
XtSolFrame *task_frame = (XtSolFrame *)task->stack_start;
|
||||||
|
if (stack_is_sane) {
|
||||||
|
if (task_frame->exit == 0) {
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Task (TCB:%x), EXIT/PC/PS/A0/SP %x %x %x %x %x",
|
||||||
|
task->tcb_addr,
|
||||||
|
task_frame->exit,
|
||||||
|
task_frame->pc,
|
||||||
|
task_frame->ps,
|
||||||
|
task_frame->a0,
|
||||||
|
task_frame->a1);
|
||||||
|
} else {
|
||||||
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
|
#if CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH
|
||||||
XtExcFrame *task_frame2 = (XtExcFrame *)task_snaphort->stack_start;
|
XtExcFrame *task_frame2 = (XtExcFrame *)task->stack_start;
|
||||||
ESP_COREDUMP_LOG_PROCESS("Task %x EXIT/PC/PS/A0/SP %x %x %x %x %x",
|
task_frame2->exccause = COREDUMP_INVALID_CAUSE_VALUE;
|
||||||
task_snaphort->tcb_addr, task_frame2->exit, task_frame2->pc, task_frame2->ps, task_frame2->a0, task_frame2->a1);
|
ESP_COREDUMP_LOG_PROCESS("Task (TCB:%x) EXIT/PC/PS/A0/SP %x %x %x %x %x",
|
||||||
|
task->tcb_addr,
|
||||||
|
task_frame2->exit,
|
||||||
|
task_frame2->pc,
|
||||||
|
task_frame2->ps,
|
||||||
|
task_frame2->a0,
|
||||||
|
task_frame2->a1);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_COREDUMP_LOG_PROCESS("Task (TCB:%x), stack_start=%x is incorrect, skip registers printing.",
|
||||||
|
task->tcb_addr, task->stack_start);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_current) {
|
||||||
|
*is_current = is_curr_task;
|
||||||
|
}
|
||||||
|
if (stack_is_valid) {
|
||||||
|
*stack_is_valid = stack_is_sane;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool esp_core_dump_process_stack(core_dump_task_header_t* task_snaphort, uint32_t *length)
|
bool esp_core_dump_check_stack(uint32_t stack_start, uint32_t stack_end)
|
||||||
{
|
{
|
||||||
uint32_t len = 0;
|
uint32_t len = stack_end - stack_start;
|
||||||
bool task_is_valid = false;
|
bool task_is_valid = false;
|
||||||
len = (uint32_t)task_snaphort->stack_end - (uint32_t)task_snaphort->stack_start;
|
|
||||||
// Check task's stack
|
// Check task's stack
|
||||||
if (!esp_stack_ptr_is_sane(task_snaphort->stack_start) ||
|
if (!esp_stack_ptr_is_sane(stack_start) || !esp_core_dump_task_stack_end_is_sane(stack_end) ||
|
||||||
!esp_task_stack_start_is_sane((uint32_t)task_snaphort->stack_end) ||
|
|
||||||
(len > COREDUMP_MAX_TASK_STACK_SIZE)) {
|
(len > COREDUMP_MAX_TASK_STACK_SIZE)) {
|
||||||
// Check if current task stack corrupted
|
// Check if current task stack is corrupted
|
||||||
if (task_snaphort->tcb_addr == xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID())) {
|
|
||||||
ESP_COREDUMP_LOG_PROCESS("Crashed task will be skipped!");
|
|
||||||
}
|
|
||||||
ESP_COREDUMP_LOG_PROCESS("Corrupted TCB %x: stack len %lu, top %x, end %x!",
|
|
||||||
task_snaphort->tcb_addr, len, task_snaphort->stack_start, task_snaphort->stack_end);
|
|
||||||
task_snaphort->tcb_addr = 0; // make TCB addr invalid to skip it in dump
|
|
||||||
task_is_valid = false;
|
task_is_valid = false;
|
||||||
} else {
|
} else {
|
||||||
ESP_COREDUMP_LOG_PROCESS("Stack len = %lu (%x %x)", len,
|
ESP_COREDUMP_LOG_PROCESS("Stack len = %lu (%x %x)", len, stack_start, stack_end);
|
||||||
task_snaphort->stack_start, task_snaphort->stack_end);
|
|
||||||
// Take stack padding into account
|
|
||||||
if (length) {
|
|
||||||
*length = (len + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1);
|
|
||||||
}
|
|
||||||
task_is_valid = true;
|
task_is_valid = true;
|
||||||
}
|
}
|
||||||
return task_is_valid;
|
return task_is_valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void esp_core_dump_init_extra_info()
|
||||||
|
{
|
||||||
|
s_extra_info.crashed_task_tcb = COREDUMP_CURR_TASK_MARKER;
|
||||||
|
// Initialize exccause register to default value (required if current task corrupted)
|
||||||
|
s_extra_info.exccause.reg_val = COREDUMP_INVALID_CAUSE_VALUE;
|
||||||
|
s_extra_info.exccause.reg_index = EXCCAUSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t esp_core_dump_get_extra_info(void **info)
|
||||||
|
{
|
||||||
|
*info = &s_extra_info;
|
||||||
|
return sizeof(s_extra_info);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -16,9 +16,12 @@
|
||||||
#include "soc/gpio_periph.h"
|
#include "soc/gpio_periph.h"
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
#include "esp_core_dump_priv.h"
|
#include "esp_core_dump_priv.h"
|
||||||
|
// TODO: move chip dependent part to portable code
|
||||||
#if CONFIG_IDF_TARGET_ESP32
|
#if CONFIG_IDF_TARGET_ESP32
|
||||||
|
#include "esp32/rom/crc.h"
|
||||||
#include "esp32/clk.h"
|
#include "esp32/clk.h"
|
||||||
#elif CONFIG_IDF_TARGET_ESP32S2BETA
|
#elif CONFIG_IDF_TARGET_ESP32S2BETA
|
||||||
|
#include "esp32s2beta/rom/crc.h"
|
||||||
#include "esp32s2beta/clk.h"
|
#include "esp32s2beta/clk.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -54,22 +57,49 @@ static void esp_core_dump_b64_encode(const uint8_t *src, uint32_t src_len, uint8
|
||||||
static esp_err_t esp_core_dump_uart_write_start(void *priv)
|
static esp_err_t esp_core_dump_uart_write_start(void *priv)
|
||||||
{
|
{
|
||||||
esp_err_t err = ESP_OK;
|
esp_err_t err = ESP_OK;
|
||||||
|
core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv;
|
||||||
|
esp_core_dump_checksum_init(wr_data);
|
||||||
ets_printf(DRAM_STR("================= CORE DUMP START =================\r\n"));
|
ets_printf(DRAM_STR("================= CORE DUMP START =================\r\n"));
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static esp_err_t esp_core_dump_uart_write_prepare(void *priv, uint32_t *data_len)
|
||||||
|
{
|
||||||
|
core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv;
|
||||||
|
uint32_t cs_len;
|
||||||
|
cs_len = esp_core_dump_checksum_finish(wr_data, NULL);
|
||||||
|
*data_len += cs_len;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static esp_err_t esp_core_dump_uart_write_end(void *priv)
|
static esp_err_t esp_core_dump_uart_write_end(void *priv)
|
||||||
{
|
{
|
||||||
esp_err_t err = ESP_OK;
|
esp_err_t err = ESP_OK;
|
||||||
|
char buf[64 + 4];
|
||||||
|
void* cs_addr = NULL;
|
||||||
|
core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv;
|
||||||
|
if (wr_data) {
|
||||||
|
size_t cs_len = esp_core_dump_checksum_finish(wr_data, &cs_addr);
|
||||||
|
wr_data->off += cs_len;
|
||||||
|
esp_core_dump_b64_encode((const uint8_t *)cs_addr, cs_len, (uint8_t*)&buf[0]);
|
||||||
|
ets_printf(DRAM_STR("%s\r\n"), buf);
|
||||||
|
}
|
||||||
ets_printf(DRAM_STR("================= CORE DUMP END =================\r\n"));
|
ets_printf(DRAM_STR("================= CORE DUMP END =================\r\n"));
|
||||||
|
#if CONFIG_ESP32_COREDUMP_CHECKSUM_SHA256
|
||||||
|
if (cs_addr) {
|
||||||
|
esp_core_dump_print_sha256(DRAM_STR("Coredump SHA256"), (uint8_t*)(cs_addr));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t data_len)
|
static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t data_len)
|
||||||
{
|
{
|
||||||
esp_err_t err = ESP_OK;
|
esp_err_t err = ESP_OK;
|
||||||
char buf[64 + 4], *addr = data;
|
char buf[64 + 4];
|
||||||
|
char *addr = data;
|
||||||
char *end = addr + data_len;
|
char *end = addr + data_len;
|
||||||
|
core_dump_write_data_t *wr_data = (core_dump_write_data_t *)priv;
|
||||||
|
|
||||||
while (addr < end) {
|
while (addr < end) {
|
||||||
size_t len = end - addr;
|
size_t len = end - addr;
|
||||||
|
@ -82,6 +112,10 @@ static esp_err_t esp_core_dump_uart_write_data(void *priv, void * data, uint32_t
|
||||||
ets_printf(DRAM_STR("%s\r\n"), buf);
|
ets_printf(DRAM_STR("%s\r\n"), buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wr_data) {
|
||||||
|
wr_data->off += data_len;
|
||||||
|
esp_core_dump_checksum_update(wr_data, data, data_len);
|
||||||
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,16 +133,18 @@ static int esp_core_dump_uart_get_char(void) {
|
||||||
void esp_core_dump_to_uart(XtExcFrame *frame)
|
void esp_core_dump_to_uart(XtExcFrame *frame)
|
||||||
{
|
{
|
||||||
core_dump_write_config_t wr_cfg;
|
core_dump_write_config_t wr_cfg;
|
||||||
|
core_dump_write_data_t wr_data;
|
||||||
uint32_t tm_end, tm_cur;
|
uint32_t tm_end, tm_cur;
|
||||||
int ch;
|
int ch;
|
||||||
|
|
||||||
memset(&wr_cfg, 0, sizeof(wr_cfg));
|
memset(&wr_cfg, 0, sizeof(wr_cfg));
|
||||||
wr_cfg.prepare = NULL;
|
wr_cfg.prepare = esp_core_dump_uart_write_prepare;
|
||||||
wr_cfg.start = esp_core_dump_uart_write_start;
|
wr_cfg.start = esp_core_dump_uart_write_start;
|
||||||
wr_cfg.end = esp_core_dump_uart_write_end;
|
wr_cfg.end = esp_core_dump_uart_write_end;
|
||||||
wr_cfg.write = esp_core_dump_uart_write_data;
|
wr_cfg.write = esp_core_dump_uart_write_data;
|
||||||
wr_cfg.priv = NULL;
|
wr_cfg.priv = (void*)&wr_data;
|
||||||
|
|
||||||
|
// TODO: move chip dependent code to portable part
|
||||||
//Make sure txd/rxd are enabled
|
//Make sure txd/rxd are enabled
|
||||||
// use direct reg access instead of gpio_pullup_dis which can cause exception when flash cache is disabled
|
// use direct reg access instead of gpio_pullup_dis which can cause exception when flash cache is disabled
|
||||||
REG_CLR_BIT(GPIO_PIN_REG_1, FUN_PU);
|
REG_CLR_BIT(GPIO_PIN_REG_1, FUN_PU);
|
||||||
|
|
|
@ -1,192 +1,369 @@
|
||||||
YCEAAAEAAAAKAAAAfAEAAA==
|
UD0AAAIAAAAKAAAAfAEAAAAAAAA=
|
||||||
dFT7PwCd+z/0nvs/
|
f0VMRgEBAQAAAAAAAAAAAAQAXgABAAAAAAAAADQAAAAAAAAAAAAAADQAIAAWACgA
|
||||||
cJ37P5Ce+z/cHQAAeC/7P3gv+z90VPs/cC/7PxIAAADOzs7Ozs7OznRU+z8AAAAA
|
AAAAAA==
|
||||||
BwAAAPiW+z91bmFsaWduZWRfcHRyX3QAAQAAAPSe+z8AAAAAIAAGAA8AAADOzs7O
|
BAAAAPQCAAAAAAAAAAAAAMAXAADAFwAABgAAAAAAAAA=
|
||||||
BwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/Oj6P2Tp+j/M6fo/
|
AQAAALQaAACgavs/oGr7P3wBAAB8AQAABgAAAAAAAAA=
|
||||||
AAAAAAAAAAABAAAAAAAAAGg6QD8AAAAASB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AQAAADAcAAAgtPs/ILT7P/gBAAD4AQAABgAAAAAAAAA=
|
||||||
|
AQAAACgeAACQrPs/kKz7P3wBAAB8AQAABgAAAAAAAAA=
|
||||||
|
AQAAAKQfAABwqfs/cKn7PwwDAAAMAwAABgAAAAAAAAA=
|
||||||
|
AQAAALAiAABMgPs/TID7P3wBAAB8AQAABgAAAAAAAAA=
|
||||||
|
AQAAACwkAACQfvs/kH77P6gBAACoAQAABgAAAAAAAAA=
|
||||||
|
AQAAANQlAACwePs/sHj7P3wBAAB8AQAABgAAAAAAAAA=
|
||||||
|
AQAAAFAnAADwdvs/8Hb7P6wBAACsAQAABgAAAAAAAAA=
|
||||||
|
AQAAAPwoAAAUafs/FGn7P3wBAAB8AQAABgAAAAAAAAA=
|
||||||
|
AQAAAHgqAABgZ/s/YGf7P6ABAACgAQAABgAAAAAAAAA=
|
||||||
|
AQAAABgsAACYbPs/mGz7P3wBAAB8AQAABgAAAAAAAAA=
|
||||||
|
AQAAAJQtAACAvPs/gLz7P6gBAACoAQAABgAAAAAAAAA=
|
||||||
|
AQAAADwvAAD0ivs/9Ir7P3wBAAB8AQAABgAAAAAAAAA=
|
||||||
|
AQAAALgwAAAgifs/IIn7P8ABAADAAQAABgAAAAAAAAA=
|
||||||
|
AQAAAHgyAAA0+/o/NPv6P3wBAAB8AQAABgAAAAAAAAA=
|
||||||
|
AQAAAPQzAABg+fo/YPn6P8ABAADAAQAABgAAAAAAAAA=
|
||||||
|
AQAAALQ1AABwWPs/cFj7P3wBAAB8AQAABgAAAAAAAAA=
|
||||||
|
AQAAADA3AACwVvs/sFb7P6wBAACsAQAABgAAAAAAAAA=
|
||||||
|
AQAAANw4AACQUfs/kFH7P3wBAAB8AQAABgAAAAAAAAA=
|
||||||
|
AQAAAFg6AACwT/s/sE/7P8wBAADMAQAABgAAAAAAAAA=
|
||||||
|
BAAAACQ8AAAAAAAAAAAAABQBAAAUAQAABgAAAAAAAAA=
|
||||||
|
CAAAAEwCAAABAAAA
|
||||||
|
Q09SRQAAAAA=
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoGr7PwAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfQ4NQCAIBgD9FABADRUAQP////8AAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFgODYDgtPs/
|
||||||
|
AgAAACy1+z8gtfs/cOn6PwAAAAAAAAAABQAAAK3///8gAAAAIGv7PwEAAACAAAAA
|
||||||
|
AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAA
|
||||||
|
CAAAAEwCAAABAAAA
|
||||||
|
Q09SRQAAAAA=
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkKz7PwAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1pIAQCAFBgD9FABADRUAQP////8XAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+TAIAwqvs/
|
||||||
|
rKr7PwAAAAAAAAAAwBsEAFcAAAA3AAAAnAH+PwAA9D8AAAAAAAAAAAAAAADDGwQA
|
||||||
|
AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAA
|
||||||
|
CAAAAEwCAAABAAAA
|
||||||
|
Q09SRQAAAAA=
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATID7PwAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABogOQCAABgBsxABAd8QAQP////8AAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM44DYBQf/s/
|
||||||
|
AAAAAAAAAAABAAAAAQAAgAMAAAAjAAYA1JcIgEB/+z8DAAAAIwgGACAIBgABAAAA
|
||||||
|
IAgGAOCO+z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAA
|
||||||
|
CAAAAEwCAAABAAAA
|
||||||
|
Q09SRQAAAAA=
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsHj7PwAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABogOQCAFBgBsxABAd8QAQP////8AAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM44DYCwd/s/
|
||||||
|
AAAAAAMAAAABAAAAAQAAgAMAAAAjAAYAepEIgJB3+z8Ucfs/SB0AQCAEBgABAAAA
|
||||||
|
IAQGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAA
|
||||||
|
CAAAAEwCAAABAAAA
|
||||||
|
Q09SRQAAAAA=
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFGn7PwAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASBQIQCAFBgD9FABADRUAQPn///8AAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALqbCIAgaPs/
|
||||||
|
AAAAAFojBADUlwiAAFf7PwMAAAAjCAYASBQIgABo+z/cAPA/AQAAADgA+z8BAAAA
|
||||||
|
IAUGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAA
|
||||||
|
CAAAAEwCAAABAAAA
|
||||||
|
Q09SRQAAAAA=
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmGz7PwAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASBQIQCADBgD9FABADRUAQPj///8AAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALqbCIBAvfs/
|
||||||
|
AAAAAFojBADtWA2A4Kn7PwAIAAAEAPs/SBQIgCC9+z/cAPA/AQAAADgA+z8BAAAA
|
||||||
|
IAMGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAA
|
||||||
|
CAAAAEwCAAABAAAA
|
||||||
|
Q09SRQAAAAA=
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9Ir7PwAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASBQIQCAABgAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF2pCIDgifs/
|
||||||
|
AAAAAAAAAAAsgvs/AAAAAAAAAABgXPs/SBQIgMCJ+z/cAPA/AQAAADgA+z9wXPs/
|
||||||
|
iCsNQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAA
|
||||||
|
CAAAAEwCAAABAAAA
|
||||||
|
Q09SRQAAAAA=
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANPv6PwAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASBQIQCAGBgAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCNCIAg+vo/
|
||||||
|
AAAAAAAAAADoQfs/HQAAAFUAAADgUPs/SBQIgAD6+j/cAPA/AQAAADgA+z8BAAAA
|
||||||
|
IAYGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAA
|
||||||
|
CAAAAEwCAAABAAAA
|
||||||
|
Q09SRQAAAAA=
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcFj7PwAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkI0IQCAIBgAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEMdCIBwV/s/
|
||||||
|
9FP7PwAAAABIVPs/AAAAAAEAAAAAAAAAkI0IgFBX+z8BAAAABAAAAOxB+z8KAAAA
|
||||||
|
AACAABwA9D8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAA
|
||||||
|
CAAAAEwCAAABAAAA
|
||||||
|
Q09SRQAAAAA=
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkFH7PwAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASBQIQCAOBgAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCNCIBwUPs/
|
||||||
|
AAAAAAAAAADoQfs/zc0AAAEAAAAAAAAASBQIgFBQ+z/cAPA/AQAAADgA+z8BAAAA
|
||||||
|
IAAGAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAA
|
||||||
|
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
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
bJX7P1CS+z9Ylfs/
|
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
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADWxBmW
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADc5+vE
|
||||||
jJP7PyAAAIAhAAYA4En7PwAAAADQlPs/NAMOQAAAAAAjAAYAbJX7PwAAAAAAAAAA
|
rKr7PwAAAIAhAAYAIGH7PwAAAADwq/s/mGENQAAAAAAjAAYAYED7P5Cs+z8AAAAA
|
||||||
AAAAAPCU+z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AAAAABCs+z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAD8lPs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AAAAAAAAAAAcrPs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAA=
|
AAAAAAAAAAAAAAAA
|
||||||
DGn7P1Bn+z/4aPs/
|
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
|
||||||
cGH7P7Bf+z9cYfs/
|
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
|
||||||
6FL7P1BR+z/UUvs/
|
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
|
|
||||||
AAAAAA==
|
|
||||||
bFb7P4Cl+z8Ep/s/
|
|
||||||
gKX7P6Cm+z/EIQAA8FL7P9gu+z9sVvs/0C77Pw8AAADOzs7Ozs7OzmxW+z8AAAAA
|
|
||||||
CgAAAAif+z9mYWlsZWRfYXNzZXJ0X3QAAAAAAASn+z8AAAAAIQAGABAAAADOzs7O
|
|
||||||
CgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/Oj6P2Tp+j/M6fo/
|
|
||||||
AAAAAAAAAAABAAAAAAAAAGg6QD8AAAAASB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADOzs4=
|
|
||||||
xCAIQIJ3CEAwCQYAayEOgECm+z/EIQAAAAAAAEAE+z8gAACAIQAGAAAAAACCdwiA
|
|
||||||
IKb7PwAAAADEIQAAeAYOgMCS+z8ACAAAQBb7PwAAAABYJw2AAKb7P/0UAEANFQBA
|
|
||||||
+P///8QiCEDAkvs/HI4IQGgT+z8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAHgGDoDAkvs/AAgAAEAW+z+8gQiAYKb7PwAAAAAAAAAA
|
|
||||||
QAT7PyAAAIAhAAYAAAAAAAAAAACApvs/XCEOQAAAAAAjAAYAbFb7PwAAAAAAAAAA
|
|
||||||
AAAAAKCm+z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAACspvs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAA==
|
|
||||||
tHP7PwBy+z+gc/s/
|
|
||||||
AHL7P0Bz+z8AAAAAxC77P8Qu+z+0c/s/vC77PxgAAADEavs/xGr7P7Rz+z+8avs/
|
|
||||||
AQAAAKRr+z9UbXIgU3ZjAM7Ozs7Ozs4AAAAAAKBz+z8AAAAAIQAGAAgAAADOzs7O
|
|
||||||
AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/Oj6P2Tp+j/M6fo/
|
|
||||||
AAAAAAAAAAABAAAAAAAAAGg6QD8AAAAASB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADOzs4=
|
|
||||||
xCAIQPKTCEAwCgYAJ5UIgMBy+z8wMfs/AAAAAAEAAAAgAACAIQAGAAAAAADykwiA
|
|
||||||
oHL7PwAAAAA8Lvs/7Gr7PwAAAAAAAAAAIwAGAAAAAAClpaWlpaWlpQAAAAAAAAAA
|
|
||||||
AAAAAMQiCEAAAAAAHI4IQAjg+j8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAMlQhAAAAAAAAAAAC8gQiA8HL7PwAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAA1sQZlgAAAAAAAAAAAAAAAAAAAAAAAAAAIHP7PwyVCEAAAAAA
|
|
||||||
COD6PwAAAAABAAAA1sQZliMABgDUWfs/AAAAAAEAAAAAAAAAQHP7PwAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAExz+z8AAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
||||||
lPv6P+D5+j+A+/o/
|
gLz7P8C9+z9aIwQAHGn7P+g/+z+YbPs/4D/7Pw8AAAC8avs/WGz7P5hs+z8AAAAA
|
||||||
4Pn6PyD7+j/Ozs7OzED7P8Q6+z+U+/o/YC77PwMAAADY6vo/2Or6P5T7+j/Q6vo/
|
CgAAACy2+z9mYWlsZWRfYXNzZXJ0X3QAAAAAACi++z8AAAAAIQAGABAAAADOzs7O
|
||||||
FgAAAITr+j9lc3BfdGltZXIAzs7Ozs4AAAAAAID7+j8AAAAAIQAGAAEAAADOzs7O
|
CgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACOn6P3Dp+j/Y6fo/
|
||||||
FgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/Oj6P2Tp+j/M6fo/
|
AAAAAAAAAAABAAAAAAAAAAAAAAAAAAAASB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAABAAAAAAAAAGg6QD8AAAAASB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADOzs4=
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADOzs4=
|
||||||
xCAIQLSLCEAwAAYAmw8NgKD6+j+s6vo/AAAAAADr+j8AAAAAAQAAAAAAAAC0iwiA
|
nCQIQEgUCEAwAwYAupsIgEC9+z8AAAAAWiMEAO1YDYDgqfs/AAgAAAQA+z9IFAiA
|
||||||
gPr6PwAAAADYMPs/2DD7P1A5+z8DAAAAIw4GAAAAAAClpaWlpaWlpQAAAAAAAAAA
|
IL37P9wA8D8BAAAAOAD7PwEAAAAgAwYAAAAAAAAAAAD//wAAAAAAAP0UAEANFQBA
|
||||||
AAAAAMQiCEBQOfs/HI4IQOhn+j8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
|
+P///5wmCEABAAAA/IMIQGRP+z8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAACIDw1AAAAAAAAAAAC8gQiA4Pr6PwAAAAAAAAAA
|
AAAAAAAAAAAAAAAAAAAAAEQBQD8eAAAAgllAPwEAAABbDQ2AYL37P1ojBACUDPs/
|
||||||
AAAAAAAAAAAAAAAA/////wAAAAAAAAAAAAAAANbEGZYAAAAAAAAAAAAAAAAAAAAA
|
7VgNgOCp+z8ACAAABAD7P7yBCICAvfs/AAAAAAAAAAAgAAAAAAAAgCEABgAAAAAA
|
||||||
AAAAAAD7+j+IDw1AAAAAACMABgCU+/o/AAAAAAEAAAAAAAAAIPv6PwAAAAAAAAAA
|
AAAAAKC9+z9MDQ1AAAAAACMABgDEQPs/mGz7PwAAAAAAAAAAwL37PwAAAAAAAAAA
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACz7+j8AAAAA
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMy9+z8AAAAA
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||||
xED7P/A++z+wQPs/
|
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
|
||||||
AAAAAAAAAAAAAAAAAAAAAA==
|
AAAAAAAAAAAAAAAAAAAAAA==
|
||||||
vDr7PwA5+z+oOvs/
|
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
|
AAAAAAAAAAAAAAAAAAAAAA==
|
||||||
|
sFb7P/BX+z/Ozs7OeD/7Pzz7+j9wWPs/cD/7PwEAAAAgVPs/IFT7P3BY+z8YVPs/
|
||||||
|
GAAAAGBU+z9pcGMxAM7Ozs7Ozs7Ozs4AAQAAAFxY+z8AAAAAIQAGAAMAAADOzs7O
|
||||||
|
GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACOn6P3Dp+j/Y6fo/
|
||||||
|
AAAAAAAAAAABAAAAAAAAAAAAAAAAAAAASB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADOzs4=
|
||||||
|
nCQIQJCNCEAwCAYAQx0IgHBX+z/0U/s/AAAAAEhU+z8AAAAAAQAAAAAAAACQjQiA
|
||||||
|
UFf7PwEAAAAEAAAA7EH7PwoAAAAAAIAAHAD0PwAAAAD//wAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAJwmCEAKAAAA/IMIQJTp+j8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAJis+z8KAAAAAACAABwA9D+8gQiAsFf7PwEAAAAAAAAA
|
||||||
|
mKz7PwoAAAAAAIAA/////7yBCIAAAAAAiRsEANzn68RIVPs/AAAAAAEAAAAAAAAA
|
||||||
|
AAAAANBX+z8QHQhAAQAAAAEAAADcQfs/cFj7PwAAAAAAAAAA8Ff7PwAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAOARCICAff4/KAAAACgAAAAAAAAAAAAAAPxX+z8AAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
||||||
|
sE/7PxBR+z/Ozs7OPPv6P3g/+z+QUfs/cD/7PwEAAABATfs/QE37P5BR+z84Tfs/
|
||||||
|
GAAAAIBN+z9pcGMwAM7Ozs7Ozs7Ozs4AAAAAAHxR+z8AAAAAIQAGAAIAAADOzs7O
|
||||||
|
GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACOn6P3Dp+j/Y6fo/
|
||||||
|
AAAAAAAAAAABAAAAAAAAAAAAAAAAAAAASB0AQAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADOzs4=
|
||||||
|
nCQIQEgUCEAwDgYAkI0IgHBQ+z8AAAAAAAAAAOhB+z/NzQAAAQAAAAAAAABIFAiA
|
||||||
|
UFD7P9wA8D8BAAAAOAD7PwEAAAAgAAYAAQAAAAAAAAD//wAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAJwmCEABAAAA/IMIQLTi+j8AAAAAAAAAAAAAAAD//z+zAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAJwkCEC0gQhAMAAFAAAAAABDHQiAkFD7PxRN+z8AAAAA
|
||||||
|
AAAAABAdCEAAAAAAAAAAALyBCIDQUPs/AAAAAAAAAAAAAAAAAAAAAAAAAAD/////
|
||||||
|
AAAAAAAAAAAAAAAA3OfrxGhN+z8AAAAAAQAAAAIAAAAAAAAA8FD7PxAdCEAAAAAA
|
||||||
|
IwMGANxB+z+QUfs/AQAAAAAAAAAQUfs/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
WhEIgFA7/j9YP/s/3OfrxAAAAAAAAAAAHFH7PwAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||||
|
FAAAAEgAAABKIAAA
|
||||||
|
RVNQX0NPUkVfRFVNUF9JTkZPAAA=
|
||||||
|
AgAAADJhYTkyNjY1YTFiNzg5OWMyZjI4NjhmOGRhZWRmZDVhMWUzNWExYWVhMzY1
|
||||||
|
ZjkzNmRjODllZThjYzcxNzhhNTMAAAAA
|
||||||
|
DAAAAJQAAAClAgAA
|
||||||
|
RVhUUkFfSU5GTwAA
|
||||||
|
oGr7P+gAAAAdAAAA7gAAAAUAAADCAAAAAAAAAMMAAAAAAAAAxAAAAAAAAADFAAAA
|
||||||
|
AAAAAMYAAAAAAAAAxwAAAAAAAACxAAAAm4cOQLIAAAAAAAAAswAAAAAAAAC0AAAA
|
||||||
|
AAAAALUAAAAAAAAAtgAAAAAAAAC3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAA==
|
||||||
|
v1VmGg==
|
File diff suppressed because it is too large
Load diff
Binary file not shown.
|
@ -18,16 +18,31 @@ Configuration
|
||||||
|
|
||||||
There are a number of core dump related configuration options which user can choose in project configuration menu (`idf.py menuconfig`).
|
There are a number of core dump related configuration options which user can choose in project configuration menu (`idf.py menuconfig`).
|
||||||
|
|
||||||
1. Core dump data destination (`Components -> ESP32-specific config -> Core dump -> Data destination`):
|
1. Core dump data destination (`Components -> Core dump -> Data destination`):
|
||||||
|
|
||||||
* Disable core dump generation
|
* Save core dump to Flash (Flash)
|
||||||
* Save core dump to flash
|
* Print core dump to UART (UART)
|
||||||
* Print core dump to UART
|
* Disable core dump generation (None)
|
||||||
|
|
||||||
2. Maximum number of tasks snapshots in core dump (`Components -> ESP32-specific config -> Core dump -> Maximum number of tasks`).
|
2. Core dump data format (`Components -> Core dump -> Core dump data format`):
|
||||||
|
|
||||||
3. Delay before core dump is printed to UART (`Components -> ESP32-specific config -> Core dump -> Delay before print to UART`). Value is in ms.
|
* ELF format (Executable and Linkable Format file for core dump)
|
||||||
|
* Binary format (Basic binary format for core dump)
|
||||||
|
|
||||||
|
The ELF format contains extended features and allow to save more information about broken tasks and crashed software but it requires more space in the flash memory.
|
||||||
|
It also stores SHA256 of crashed application image. This format of core dump is recommended for new software designs and is flexible enough to extend saved information for future revisions.
|
||||||
|
The Binary format is kept for compatibility standpoint, it uses less space in the memory to keep data and provides better performance.
|
||||||
|
|
||||||
|
3. Maximum number of tasks snapshots in core dump (`Components -> Core dump -> Maximum number of tasks`).
|
||||||
|
|
||||||
|
4. Delay before core dump is printed to UART (`Components -> Core dump -> Delay before print to UART`). Value is in ms.
|
||||||
|
|
||||||
|
5. Type of data integrity check for core dump (`Components -> Core dump -> Core dump data integrity check`).
|
||||||
|
|
||||||
|
* Use CRC32 for core dump integrity verification
|
||||||
|
* Use SHA256 for core dump integrity verification
|
||||||
|
|
||||||
|
The SHA256 hash algorithm provides greater probability of detecting corruption than a CRC32 with multiple bit errors. The CRC32 option provides better calculation performance and consumes less memory for storage.
|
||||||
|
|
||||||
Save core dump to flash
|
Save core dump to flash
|
||||||
-----------------------
|
-----------------------
|
||||||
|
@ -75,7 +90,7 @@ To overcome this issue you can use ROM ELF provided by Espressif (https://dl.esp
|
||||||
|
|
||||||
|
|
||||||
Running 'espcoredump.py'
|
Running 'espcoredump.py'
|
||||||
------------------------------------
|
------------------------
|
||||||
|
|
||||||
Generic command syntax:
|
Generic command syntax:
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue