mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
0e3673a2bd
esp32p4: panic tests support Closes IDF-7565, IDF-7861, IDF-9035, and IDF-9075 See merge request espressif/esp-idf!28586
349 lines
11 KiB
C
349 lines
11 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "spi_flash_mmap.h"
|
|
|
|
#include "soc/soc_caps.h"
|
|
#include "esp_private/panic_internal.h"
|
|
#include "esp_private/panic_reason.h"
|
|
#include "riscv/rvruntime-frames.h"
|
|
#include "riscv/rv_utils.h"
|
|
#include "esp_private/cache_err_int.h"
|
|
#include "soc/timer_periph.h"
|
|
|
|
#if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
|
|
#include "esp_private/esp_memprot_internal.h"
|
|
#include "esp_memprot.h"
|
|
#endif
|
|
|
|
#if CONFIG_ESP_SYSTEM_USE_EH_FRAME
|
|
#include "esp_private/eh_frame_parser.h"
|
|
#include "esp_private/cache_utils.h"
|
|
#endif
|
|
|
|
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "esp_cpu.h"
|
|
#include "esp_private/hw_stack_guard.h"
|
|
#endif
|
|
|
|
#define DIM(array) (sizeof(array)/sizeof(*array))
|
|
|
|
/**
|
|
* Function called when a cache error occurs. It prints details such as the
|
|
* explanation of why the panic occured.
|
|
*/
|
|
static inline void print_cache_err_details(const void *frame)
|
|
{
|
|
#if !CONFIG_IDF_TARGET_ESP32P4
|
|
const char* cache_err_msg = esp_cache_err_panic_string();
|
|
if (cache_err_msg) {
|
|
panic_print_str(cache_err_msg);
|
|
} else {
|
|
panic_print_str("Cache error active, but failed to find a corresponding error message");
|
|
}
|
|
panic_print_str("\r\n");
|
|
#endif
|
|
}
|
|
|
|
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
|
static inline void print_assist_debug_details(const void *frame)
|
|
{
|
|
uint32_t core_id = esp_hw_stack_guard_get_fired_cpu();
|
|
if (core_id == ESP_HW_STACK_GUARD_NOT_FIRED) {
|
|
panic_print_str("ASSIST_DEBUG is not triggered BUT interrupt occured!\r\n\r\n");
|
|
core_id = 0;
|
|
}
|
|
uint32_t sp_min, sp_max;
|
|
const char *task_name = pcTaskGetName(xTaskGetCurrentTaskHandleForCore(core_id));
|
|
esp_hw_stack_guard_get_bounds(core_id, &sp_min, &sp_max);
|
|
|
|
panic_print_str("\r\n");
|
|
|
|
panic_print_str("Detected in task \"");
|
|
panic_print_str(task_name);
|
|
panic_print_str("\" at 0x");
|
|
panic_print_hex((int) esp_hw_stack_guard_get_pc(core_id));
|
|
panic_print_str("\r\n");
|
|
panic_print_str("Stack pointer: 0x");
|
|
panic_print_hex((int)((RvExcFrame *)frame)->sp);
|
|
panic_print_str("\r\n");
|
|
panic_print_str("Stack bounds: 0x");
|
|
panic_print_hex((int) sp_min);
|
|
panic_print_str(" - 0x");
|
|
panic_print_hex((int) sp_max);
|
|
panic_print_str("\r\n\r\n");
|
|
}
|
|
#endif // CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
|
|
|
/**
|
|
* Function called when a memory protection error occurs (PMS). It prints details such as the
|
|
* explanation of why the panic occured.
|
|
*/
|
|
#if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
|
|
|
|
static esp_memp_intr_source_t s_memp_intr = {MEMPROT_TYPE_INVALID, -1};
|
|
|
|
#define PRINT_MEMPROT_ERROR(err) \
|
|
do { \
|
|
panic_print_str("N/A (error "); \
|
|
panic_print_str(esp_err_to_name(err)); \
|
|
panic_print_str(")"); \
|
|
} while(0)
|
|
|
|
static inline void print_memprot_err_details(const void *frame __attribute__((unused)))
|
|
{
|
|
if (s_memp_intr.mem_type == MEMPROT_TYPE_INVALID && s_memp_intr.core == -1) {
|
|
panic_print_str(" - no details available -\r\n");
|
|
return;
|
|
}
|
|
|
|
//common memprot fault info
|
|
panic_print_str(" memory type: ");
|
|
panic_print_str(esp_mprot_mem_type_to_str(s_memp_intr.mem_type));
|
|
|
|
panic_print_str("\r\n faulting address: ");
|
|
void *faulting_addr;
|
|
esp_err_t res = esp_mprot_get_violate_addr(s_memp_intr.mem_type, &faulting_addr, s_memp_intr.core);
|
|
if (res == ESP_OK) {
|
|
panic_print_str("0x");
|
|
panic_print_hex((int)faulting_addr);
|
|
} else {
|
|
PRINT_MEMPROT_ERROR(res);
|
|
}
|
|
|
|
panic_print_str("\r\n world: ");
|
|
esp_mprot_pms_world_t world;
|
|
res = esp_mprot_get_violate_world(s_memp_intr.mem_type, &world, s_memp_intr.core);
|
|
if (res == ESP_OK) {
|
|
panic_print_str(esp_mprot_pms_world_to_str(world));
|
|
} else {
|
|
PRINT_MEMPROT_ERROR(res);
|
|
}
|
|
|
|
panic_print_str("\r\n operation type: ");
|
|
uint32_t operation;
|
|
res = esp_mprot_get_violate_operation(s_memp_intr.mem_type, &operation, s_memp_intr.core);
|
|
if (res == ESP_OK) {
|
|
panic_print_str(esp_mprot_oper_type_to_str(operation));
|
|
} else {
|
|
PRINT_MEMPROT_ERROR(res);
|
|
}
|
|
|
|
if (esp_mprot_has_byte_enables(s_memp_intr.mem_type)) {
|
|
panic_print_str("\r\n byte-enables: ");
|
|
uint32_t byte_enables;
|
|
res = esp_mprot_get_violate_byte_enables(s_memp_intr.mem_type, &byte_enables, s_memp_intr.core);
|
|
if (res == ESP_OK) {
|
|
panic_print_hex(byte_enables);
|
|
} else {
|
|
PRINT_MEMPROT_ERROR(res);
|
|
}
|
|
}
|
|
|
|
panic_print_str("\r\n");
|
|
}
|
|
#endif
|
|
|
|
static void panic_print_register_array(const char* names[], const uint32_t* regs, int size)
|
|
{
|
|
const int regs_per_line = 4;
|
|
for (int i = 0; i < size; i++) {
|
|
if (i % regs_per_line == 0) {
|
|
panic_print_str("\r\n");
|
|
}
|
|
panic_print_str(names[i]);
|
|
panic_print_str(": 0x");
|
|
panic_print_hex(regs[i]);
|
|
panic_print_str(" ");
|
|
}
|
|
}
|
|
|
|
void panic_print_registers(const void *f, int core)
|
|
{
|
|
/**
|
|
* General Purpose context, only print ABI name
|
|
*/
|
|
const char *desc[] = {
|
|
"MEPC ", "RA ", "SP ", "GP ", "TP ", "T0 ", "T1 ", "T2 ",
|
|
"S0/FP ", "S1 ", "A0 ", "A1 ", "A2 ", "A3 ", "A4 ", "A5 ",
|
|
"A6 ", "A7 ", "S2 ", "S3 ", "S4 ", "S5 ", "S6 ", "S7 ",
|
|
"S8 ", "S9 ", "S10 ", "S11 ", "T3 ", "T4 ", "T5 ", "T6 ",
|
|
"MSTATUS ", "MTVEC ", "MCAUSE ", "MTVAL ", "MHARTID "
|
|
};
|
|
|
|
panic_print_str("Core ");
|
|
panic_print_dec(core);
|
|
panic_print_str(" register dump:");
|
|
panic_print_register_array(desc, f, DIM(desc));
|
|
}
|
|
|
|
/**
|
|
* This function will be called before the SoC-level panic is handled,
|
|
* allowing us to check and override the exception cause for certain
|
|
* pseudo-causes that do not have their own trigger
|
|
*/
|
|
bool panic_soc_check_pseudo_cause(void *f, panic_info_t *info)
|
|
{
|
|
RvExcFrame *frame = (RvExcFrame *) f;
|
|
bool pseudo_cause = false;
|
|
|
|
/* Cache errors when reading instructions will result in an illegal instructions,
|
|
before any cache error interrupts trigger. We override the exception cause if
|
|
any cache errors are active to more accurately report the actual reason */
|
|
if (esp_cache_err_has_active_err() && (frame->mcause == MCAUSE_ILLEGAL_INSTRUCTION)) {
|
|
pseudo_cause = true;
|
|
frame->mcause = ETS_CACHEERR_INUM;
|
|
}
|
|
|
|
return pseudo_cause;
|
|
}
|
|
|
|
/**
|
|
* This function will be called when a SoC-level panic occurs.
|
|
* SoC-level panics include cache errors and watchdog interrupts.
|
|
*/
|
|
void panic_soc_fill_info(void *f, panic_info_t *info)
|
|
{
|
|
RvExcFrame *frame = (RvExcFrame *) f;
|
|
|
|
info->reason = "Unknown reason";
|
|
info->addr = (void *) frame->mepc;
|
|
|
|
/* The mcause has been set by the CPU when the panic occured.
|
|
* All SoC-level panic will call this function, thus, this register
|
|
* lets us know which error was triggered. */
|
|
if (frame->mcause == ETS_CACHEERR_INUM) {
|
|
/* Panic due to a cache error, multiple cache error are possible,
|
|
* assign function print_cache_err_details to our structure's
|
|
* details field. As its name states, it will give more details
|
|
* about why the error happened. */
|
|
|
|
info->core = esp_cache_err_get_cpuid();
|
|
info->reason = "Cache error";
|
|
info->details = print_cache_err_details;
|
|
|
|
} else if (frame->mcause == PANIC_RSN_INTWDT_CPU0) {
|
|
const int core = 0;
|
|
info->core = core;
|
|
info->exception = PANIC_EXCEPTION_IWDT;
|
|
info->reason = "Interrupt wdt timeout on CPU0";
|
|
}
|
|
#if SOC_CPU_CORES_NUM > 1
|
|
else if (frame->mcause == PANIC_RSN_INTWDT_CPU1) {
|
|
const int core = 1;
|
|
info->core = core;
|
|
info->exception = PANIC_EXCEPTION_IWDT;
|
|
info->reason = "Interrupt wdt timeout on CPU1";
|
|
}
|
|
#endif
|
|
|
|
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
|
else if (frame->mcause == ETS_ASSIST_DEBUG_INUM) {
|
|
info->core = esp_hw_stack_guard_get_fired_cpu();
|
|
if (info->core == ESP_HW_STACK_GUARD_NOT_FIRED) {
|
|
info->core = 0;
|
|
}
|
|
info->reason = "Stack protection fault";
|
|
info->details = print_assist_debug_details;
|
|
}
|
|
#endif
|
|
#if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
|
|
else if (frame->mcause == ETS_MEMPROT_ERR_INUM) {
|
|
info->reason = "Memory protection fault";
|
|
info->details = print_memprot_err_details;
|
|
info->core = esp_mprot_get_active_intr(&s_memp_intr) == ESP_OK ? s_memp_intr.core : -1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void panic_arch_fill_info(void *frame, panic_info_t *info)
|
|
{
|
|
RvExcFrame *regs = (RvExcFrame *) frame;
|
|
info->core = rv_utils_get_core_id();
|
|
info->exception = PANIC_EXCEPTION_FAULT;
|
|
|
|
static const char *reason[] = {
|
|
"Instruction address misaligned",
|
|
"Instruction access fault",
|
|
"Illegal instruction",
|
|
"Breakpoint",
|
|
"Load address misaligned",
|
|
"Load access fault",
|
|
"Store address misaligned",
|
|
"Store access fault",
|
|
"Environment call from U-mode",
|
|
"Environment call from S-mode",
|
|
NULL,
|
|
"Environment call from M-mode",
|
|
"Instruction page fault",
|
|
"Load page fault",
|
|
NULL,
|
|
"Store page fault",
|
|
};
|
|
|
|
if (regs->mcause < (sizeof(reason) / sizeof(reason[0]))) {
|
|
if (reason[regs->mcause] != NULL) {
|
|
info->reason = (reason[regs->mcause]);
|
|
}
|
|
}
|
|
|
|
info->description = "Exception was unhandled.";
|
|
|
|
info->addr = (void *) regs->mepc;
|
|
}
|
|
|
|
static void panic_print_basic_backtrace(const void *frame, int core)
|
|
{
|
|
// Basic backtrace
|
|
panic_print_str("\r\nStack memory:\r\n");
|
|
uint32_t sp = (uint32_t)((RvExcFrame *)frame)->sp;
|
|
const int per_line = 8;
|
|
for (int x = 0; x < 1024; x += per_line * sizeof(uint32_t)) {
|
|
uint32_t *spp = (uint32_t *)(sp + x);
|
|
panic_print_hex(sp + x);
|
|
panic_print_str(": ");
|
|
for (int y = 0; y < per_line; y++) {
|
|
panic_print_str("0x");
|
|
panic_print_hex(spp[y]);
|
|
panic_print_str(y == per_line - 1 ? "\r\n" : " ");
|
|
}
|
|
}
|
|
}
|
|
|
|
void panic_print_backtrace(const void *frame, int core)
|
|
{
|
|
#if CONFIG_ESP_SYSTEM_USE_EH_FRAME
|
|
if (!spi_flash_cache_enabled()) {
|
|
panic_print_str("\r\nWarning: SPI Flash cache is disabled, cannot process eh_frame parsing. "
|
|
"Falling back to basic backtrace.\r\n");
|
|
panic_print_basic_backtrace(frame, core);
|
|
} else {
|
|
esp_eh_frame_print_backtrace(frame);
|
|
}
|
|
#else
|
|
panic_print_basic_backtrace(frame, core);
|
|
#endif
|
|
}
|
|
|
|
uint32_t panic_get_address(const void *f)
|
|
{
|
|
return ((RvExcFrame *)f)->mepc;
|
|
}
|
|
|
|
uint32_t panic_get_cause(const void *f)
|
|
{
|
|
return ((RvExcFrame *)f)->mcause;
|
|
}
|
|
|
|
void panic_set_address(void *f, uint32_t addr)
|
|
{
|
|
((RvExcFrame *)f)->mepc = addr;
|
|
}
|