Merge branch 'feat/cache_error_c6_h2' into 'master'

fix(panic): fixed cache error being reported as illegal instruction

Closes IDF-6398, IDF-5657, IDF-7015, and IDF-6733

See merge request espressif/esp-idf!27430
This commit is contained in:
Marius Vikhammer
2023-12-21 10:32:06 +08:00
14 changed files with 349 additions and 122 deletions

View File

@@ -75,6 +75,8 @@ void panic_arch_fill_info(void *frame, panic_info_t *info);
void panic_soc_fill_info(void *frame, panic_info_t *info);
bool panic_soc_check_pseudo_cause(void *f, panic_info_t *info);
void panic_print_registers(const void *frame, int core);
void panic_print_backtrace(const void *frame, int core);

View File

@@ -5,7 +5,7 @@ entries:
panic (noflash)
panic_handler (noflash)
panic_arch (noflash)
cache_err_int:esp_cache_err_get_cpuid (noflash)
cache_err_int (noflash)
reset_reason:esp_reset_reason_get_hint (noflash)
if ESP_SYSTEM_HW_STACK_GUARD = y:
hw_stack_guard:esp_hw_stack_guard_get_bounds (noflash)

View File

@@ -41,124 +41,20 @@
#define DIM(array) (sizeof(array)/sizeof(*array))
/**
* Structure used to define a flag/bit to test in case of cache error.
* The message describes the cause of the error when the bit is set in
* a given status register.
*/
typedef struct {
const uint32_t bit;
const char *msg;
} register_bit_t;
/**
* Function to check each bits defined in the array reg_bits in the given
* status register. The first bit from the array to be set in the status
* register will have its associated message printed. This function returns
* true. If not bit was set in the register, it returns false.
* The order of the bits in the array is important as only the first bit to
* be set in the register will have its associated message printed.
*/
static inline bool test_and_print_register_bits(const uint32_t status,
const register_bit_t *reg_bits,
const uint32_t size)
{
/* Browse the flag/bit array and test each one with the given status
* register. */
for (int i = 0; i < size; i++) {
const uint32_t bit = reg_bits[i].bit;
if ((status & bit) == bit) {
/* Reason of the panic found, print the reason. */
panic_print_str(reg_bits[i].msg);
panic_print_str("\r\n");
return true;
}
}
/* Panic cause not found, no message was printed. */
return false;
}
/**
* 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_ESP32C6 && !CONFIG_IDF_TARGET_ESP32H2 && !CONFIG_IDF_TARGET_ESP32P4 // ESP32P4-TODO, ESP32C6-TODO, ESP32H2-TODO: IDF-5657
/* Define the array that contains the status (bits) to test on the register
* EXTMEM_CORE0_ACS_CACHE_INT_ST_REG. each bit is accompanied by a small
* message.
* The messages have been pulled from the header file where the status bit
* are defined. */
const register_bit_t core0_acs_bits[] = {
{
.bit = EXTMEM_CORE0_DBUS_WR_ICACHE_ST,
.msg = "dbus tried to write cache"
},
{
.bit = EXTMEM_CORE0_DBUS_REJECT_ST,
.msg = "dbus authentication failed"
},
{
.bit = EXTMEM_CORE0_DBUS_ACS_MSK_ICACHE_ST,
.msg = "access to cache while dbus or cache is disabled"
},
{
.bit = EXTMEM_CORE0_IBUS_REJECT_ST,
.msg = "ibus authentication failed"
},
{
.bit = EXTMEM_CORE0_IBUS_WR_ICACHE_ST,
.msg = "ibus tried to write cache"
},
{
.bit = EXTMEM_CORE0_IBUS_ACS_MSK_ICACHE_ST,
.msg = "access to cache while ibus or cache is disabled"
},
};
/* Same goes for the register EXTMEM_CACHE_ILG_INT_ST_REG and its bits. */
const register_bit_t cache_ilg_bits[] = {
{
.bit = EXTMEM_MMU_ENTRY_FAULT_ST,
.msg = "MMU entry fault"
},
{
.bit = EXTMEM_ICACHE_PRELOAD_OP_FAULT_ST,
.msg = "preload configurations fault"
},
{
.bit = EXTMEM_ICACHE_SYNC_OP_FAULT_ST,
.msg = "sync configurations fault"
},
};
/* Read the status register EXTMEM_CORE0_ACS_CACHE_INT_ST_REG. This status
* register is not equal to 0 when a cache access error occured. */
const uint32_t core0_status = REG_READ(EXTMEM_CORE0_ACS_CACHE_INT_ST_REG);
/* If the panic is due to a cache access error, one of the bit of the
* register is set. Thus, this function will return true. */
bool handled = test_and_print_register_bits(core0_status, core0_acs_bits, DIM(core0_acs_bits));
/* If the panic was due to a cache illegal error, the previous call returned false and this
* EXTMEM_CACHE_ILG_INT_ST_REG register should not me equal to 0.
* Check each bit of it and print the message associated if found. */
if (!handled) {
const uint32_t cache_ilg_status = REG_READ(EXTMEM_CACHE_ILG_INT_ST_REG);
handled = test_and_print_register_bits(cache_ilg_status, cache_ilg_bits, DIM(cache_ilg_bits));
/* If the error was not found, print the both registers value */
if (!handled) {
panic_print_str("EXTMEM_CORE0_ACS_CACHE_INT_ST_REG = 0x");
panic_print_hex(core0_status);
panic_print_str("\r\nEXTMEM_CACHE_ILG_INT_ST_REG = 0x");
panic_print_hex(cache_ilg_status);
panic_print_str("\r\n");
}
#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
}
@@ -294,6 +190,27 @@ void panic_print_registers(const void *f, int core)
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.

View File

@@ -392,6 +392,17 @@ void panic_arch_fill_info(void *f, panic_info_t *info)
info->addr = ((void *) ((XtExcFrame *) frame)->pc);
}
/**
* 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)
{
// Currently only needed on riscv targets
return false;
}
void panic_soc_fill_info(void *f, panic_info_t *info)
{
// [refactor-todo] this should be in the common port panic_handler.c, once

View File

@@ -1,15 +1,18 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief initialize cache invalid access interrupt
*
@@ -32,6 +35,22 @@ void esp_cache_err_int_init(void);
*/
int esp_cache_err_get_cpuid(void);
/**
* @brief Returns a pointer to the cache error message
*
* @return const char* Pointer to the error message
*/
const char *esp_cache_err_panic_string(void);
/**
* @brief Checks if any cache errors are active
*
* @return true
* @return false
*/
bool esp_cache_err_has_active_err(void);
#ifdef __cplusplus
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -104,9 +104,10 @@ static void frame_to_panic_info(void *frame, panic_info_t *info, bool pseudo_exc
info->exception = PANIC_EXCEPTION_FAULT;
info->details = NULL;
info->reason = "Unknown";
info->pseudo_excause = pseudo_excause;
if (pseudo_excause) {
info->pseudo_excause = panic_soc_check_pseudo_cause(frame, info) | pseudo_excause;
if (info->pseudo_excause) {
panic_soc_fill_info(frame, info);
} else {
panic_arch_fill_info(frame, info);

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -20,6 +20,119 @@
static const char *TAG = "CACHE_ERR";
#define DIM(array) (sizeof(array)/sizeof(*array))
/**
* Structure used to define a flag/bit to test in case of cache error.
* The message describes the cause of the error when the bit is set in
* a given status register.
*/
typedef struct {
const uint32_t bit;
const char *msg;
} register_bit_t;
/* Define the array that contains the status (bits) to test on the register
* EXTMEM_CORE0_ACS_CACHE_INT_ST_REG. each bit is accompanied by a small
* message.
* The messages have been pulled from the header file where the status bit
* are defined. */
const register_bit_t core0_acs_bits[] = {
{
.bit = EXTMEM_CORE0_DBUS_WR_ICACHE_ST,
.msg = "Dbus tried to write cache"
},
{
.bit = EXTMEM_CORE0_DBUS_REJECT_ST,
.msg = "Dbus authentication failed"
},
{
.bit = EXTMEM_CORE0_DBUS_ACS_MSK_ICACHE_ST,
.msg = "Cached memory region accessed while dbus or cache is disabled"
},
{
.bit = EXTMEM_CORE0_IBUS_REJECT_ST,
.msg = "Ibus authentication failed"
},
{
.bit = EXTMEM_CORE0_IBUS_WR_ICACHE_ST,
.msg = "Ibus tried to write cache"
},
{
.bit = EXTMEM_CORE0_IBUS_ACS_MSK_ICACHE_ST,
.msg = "Cached memory region accessed while ibus or cache is disabled"
},
};
/* Same goes for the register EXTMEM_CACHE_ILG_INT_ST_REG and its bits. */
const register_bit_t cache_ilg_bits[] = {
{
.bit = EXTMEM_MMU_ENTRY_FAULT_ST,
.msg = "MMU entry fault"
},
{
.bit = EXTMEM_ICACHE_PRELOAD_OP_FAULT_ST,
.msg = "Preload configurations fault"
},
{
.bit = EXTMEM_ICACHE_SYNC_OP_FAULT_ST,
.msg = "Sync configurations fault"
},
};
/**
* Function to check each bits defined in the array reg_bits in the given
* status register. The first bit from the array to be set in the status
* register will have its associated message printed. This function returns
* true. If not bit was set in the register, it returns false.
* The order of the bits in the array is important as only the first bit to
* be set in the register will have its associated message printed.
*/
static inline const char* test_and_print_register_bits(const uint32_t status,
const register_bit_t *reg_bits,
const uint32_t size)
{
/* Browse the flag/bit array and test each one with the given status
* register. */
for (int i = 0; i < size; i++) {
const uint32_t bit = reg_bits[i].bit;
if ((status & bit) == bit) {
/* Reason of the panic found, print the reason. */
return reg_bits[i].msg;
}
}
/* Panic cause not found, no message was printed. */
return NULL;
}
const char *esp_cache_err_panic_string(void)
{
/* Read the status register EXTMEM_CORE0_ACS_CACHE_INT_ST_REG. This status
* register is not equal to 0 when a cache access error occured. */
const uint32_t access_err_status = cache_ll_l1_get_access_error_intr_status(0, CACHE_LL_L1_ACCESS_EVENT_MASK);
/* If the panic is due to a cache access error, one of the bit of the
* register is set. Thus, this function will return an error string. */
const char* err_str = test_and_print_register_bits(access_err_status, core0_acs_bits, DIM(core0_acs_bits));
/* If the panic was due to a cache illegal error, the previous call returned NULL and this
* EXTMEM_CACHE_ILG_INT_ST_REG register should not be equal to 0.
* Check each bit of it and print the message associated if found. */
if (err_str == NULL) {
const uint32_t cache_ilg_status = cache_ll_l1_get_illegal_error_intr_status(0, CACHE_LL_L1_ILG_EVENT_MASK);
err_str = test_and_print_register_bits(cache_ilg_status, cache_ilg_bits, DIM(cache_ilg_bits));
}
return err_str;
}
bool esp_cache_err_has_active_err(void)
{
return cache_ll_l1_get_access_error_intr_status(0, CACHE_LL_L1_ACCESS_EVENT_MASK) || cache_ll_l1_get_illegal_error_intr_status(0, CACHE_LL_L1_ILG_EVENT_MASK);
}
void esp_cache_err_int_init(void)
{
const uint32_t core_id = 0;

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -20,6 +20,118 @@
static const char *TAG = "CACHE_ERR";
#define DIM(array) (sizeof(array)/sizeof(*array))
/**
* Structure used to define a flag/bit to test in case of cache error.
* The message describes the cause of the error when the bit is set in
* a given status register.
*/
typedef struct {
const uint32_t bit;
const char *msg;
} register_bit_t;
/* Define the array that contains the status (bits) to test on the register
* EXTMEM_CORE0_ACS_CACHE_INT_ST_REG. each bit is accompanied by a small
* message.
* The messages have been pulled from the header file where the status bit
* are defined. */
const register_bit_t core0_acs_bits[] = {
{
.bit = EXTMEM_CORE0_DBUS_WR_ICACHE_ST,
.msg = "Dbus tried to write cache"
},
{
.bit = EXTMEM_CORE0_DBUS_REJECT_ST,
.msg = "Dbus authentication failed"
},
{
.bit = EXTMEM_CORE0_DBUS_ACS_MSK_ICACHE_ST,
.msg = "Cached memory region accessed while dbus or cache is disabled"
},
{
.bit = EXTMEM_CORE0_IBUS_REJECT_ST,
.msg = "Ibus authentication failed"
},
{
.bit = EXTMEM_CORE0_IBUS_WR_ICACHE_ST,
.msg = "Ibus tried to write cache"
},
{
.bit = EXTMEM_CORE0_IBUS_ACS_MSK_ICACHE_ST,
.msg = "Cached memory region accessed while ibus or cache is disabled"
},
};
/* Same goes for the register EXTMEM_CACHE_ILG_INT_ST_REG and its bits. */
const register_bit_t cache_ilg_bits[] = {
{
.bit = EXTMEM_MMU_ENTRY_FAULT_ST,
.msg = "MMU entry fault"
},
{
.bit = EXTMEM_ICACHE_PRELOAD_OP_FAULT_ST,
.msg = "Preload configurations fault"
},
{
.bit = EXTMEM_ICACHE_SYNC_OP_FAULT_ST,
.msg = "Sync configurations fault"
},
};
/**
* Function to check each bits defined in the array reg_bits in the given
* status register. The first bit from the array to be set in the status
* register will have its associated message printed. This function returns
* true. If not bit was set in the register, it returns false.
* The order of the bits in the array is important as only the first bit to
* be set in the register will have its associated message printed.
*/
static inline const char* test_and_print_register_bits(const uint32_t status,
const register_bit_t *reg_bits,
const uint32_t size)
{
/* Browse the flag/bit array and test each one with the given status
* register. */
for (int i = 0; i < size; i++) {
const uint32_t bit = reg_bits[i].bit;
if ((status & bit) == bit) {
/* Reason of the panic found, print the reason. */
return reg_bits[i].msg;
}
}
/* Panic cause not found, no message was printed. */
return NULL;
}
const char *esp_cache_err_panic_string(void)
{
/* Read the status register EXTMEM_CORE0_ACS_CACHE_INT_ST_REG. This status
* register is not equal to 0 when a cache access error occured. */
const uint32_t access_err_status = cache_ll_l1_get_access_error_intr_status(0, CACHE_LL_L1_ACCESS_EVENT_MASK);
/* If the panic is due to a cache access error, one of the bit of the
* register is set. Thus, this function will return an error string. */
const char* err_str = test_and_print_register_bits(access_err_status, core0_acs_bits, DIM(core0_acs_bits));
/* If the panic was due to a cache illegal error, the previous call returned NULL and this
* EXTMEM_CACHE_ILG_INT_ST_REG register should not be equal to 0.
* Check each bit of it and return the message associated if found. */
if (err_str == NULL) {
const uint32_t cache_ilg_status = cache_ll_l1_get_illegal_error_intr_status(0, CACHE_LL_L1_ILG_EVENT_MASK);
err_str = test_and_print_register_bits(cache_ilg_status, cache_ilg_bits, DIM(cache_ilg_bits));
}
return err_str;
}
bool esp_cache_err_has_active_err(void)
{
return cache_ll_l1_get_access_error_intr_status(0, CACHE_LL_L1_ACCESS_EVENT_MASK) || cache_ll_l1_get_illegal_error_intr_status(0, CACHE_LL_L1_ILG_EVENT_MASK);
}
void esp_cache_err_int_init(void)
{
const uint32_t core_id = 0;

View File

@@ -20,6 +20,24 @@
static const char *TAG = "CACHE_ERR";
const char cache_error_msg[] = "Cache access error";
const char *esp_cache_err_panic_string(void)
{
const uint32_t access_err_status = cache_ll_l1_get_access_error_intr_status(0, CACHE_LL_L1_ACCESS_EVENT_MASK);
/* Return the error string if a cache error is active */
const char* err_str = access_err_status ? cache_error_msg : NULL;
return err_str;
}
bool esp_cache_err_has_active_err(void)
{
return cache_ll_l1_get_access_error_intr_status(0, CACHE_LL_L1_ACCESS_EVENT_MASK);
}
void esp_cache_err_int_init(void)
{
const uint32_t core_id = 0;

View File

@@ -20,6 +20,23 @@
static const char *TAG = "CACHE_ERR";
const char cache_error_msg[] = "Cache access error";
const char *esp_cache_err_panic_string(void)
{
const uint32_t access_err_status = cache_ll_l1_get_access_error_intr_status(0, CACHE_LL_L1_ACCESS_EVENT_MASK);
/* Return the error string if a cache error is active */
const char* err_str = access_err_status ? cache_error_msg : NULL;
return err_str;
}
bool esp_cache_err_has_active_err(void)
{
return cache_ll_l1_get_access_error_intr_status(0, CACHE_LL_L1_ACCESS_EVENT_MASK);
}
void esp_cache_err_int_init(void)
{
const uint32_t core_id = 0;

View File

@@ -20,6 +20,18 @@
static const char *TAG = "CACHE_ERR";
//TODO: IDF-7515
const char *esp_cache_err_panic_string(void)
{
return NULL;
}
//TODO: IDF-7515
bool esp_cache_err_has_active_err(void)
{
return false;
}
//TODO: IDF-7515
void esp_cache_err_int_init(void)
{

View File

@@ -18,3 +18,5 @@
#endif
#define PANIC_RSN_CACHEERR 3
#define MCAUSE_ILLEGAL_INSTRUCTION 2

View File

@@ -6,8 +6,8 @@
#include "riscv/rvruntime-frames.h"
#include "esp_private/panic_internal.h"
#include "esp_private/panic_reason.h"
#define MCAUSE_ILLEGAL_INSTRUCTION 2
extern void esp_panic_handler(panic_info_t *info);
volatile bool g_override_illegal_instruction = false;

View File

@@ -277,9 +277,12 @@ def test_int_wdt_cache_disabled(
@pytest.mark.generic
def test_cache_error(dut: PanicTestDut, config: str, test_func_name: str) -> None:
dut.run_test_func(test_func_name)
if dut.target in ['esp32c3', 'esp32c2', 'esp32c6', 'esp32h2']:
# Cache error interrupt is not raised, IDF-6398
dut.expect_gme('Illegal instruction')
if dut.target in ['esp32c3', 'esp32c2']:
dut.expect_gme('Cache error')
dut.expect_exact('Cached memory region accessed while ibus or cache is disabled')
elif dut.target in ['esp32c6', 'esp32h2']:
dut.expect_gme('Cache error')
dut.expect_exact('Cache access error')
elif dut.target in ['esp32s2']:
# Cache error interrupt is not enabled, IDF-1558
dut.expect_gme('IllegalInstruction')