diff --git a/components/esp_system/port/arch/riscv/panic_arch.c b/components/esp_system/port/arch/riscv/panic_arch.c index e3b46aa395..718a01116b 100644 --- a/components/esp_system/port/arch/riscv/panic_arch.c +++ b/components/esp_system/port/arch/riscv/panic_arch.c @@ -294,6 +294,14 @@ void panic_arch_fill_info(void *frame, panic_info_t *info) info->description = "Exception was unhandled."; +#if SOC_ASYNCHRONOUS_BUS_ERROR_MODE + uintptr_t bus_error_pc = rv_utils_asynchronous_bus_get_error_pc(); + if (bus_error_pc) { + /* Change mepc with the fault pc address */ + regs->mepc = bus_error_pc; + } +#endif // SOC_ASYNCHRONOUS_BUS_ERROR_MODE + info->addr = (void *) regs->mepc; } diff --git a/components/riscv/include/riscv/csr.h b/components/riscv/include/riscv/csr.h index 580aa46ccc..f393f635ac 100644 --- a/components/riscv/include/riscv/csr.h +++ b/components/riscv/include/riscv/csr.h @@ -155,6 +155,20 @@ extern "C" { #define TDATA1_HIT_S (20) +/******************************************************** + Espressif's bus error exceptions registers and fields + ********************************************************/ + +#define MEXSTATUS 0x7E1 +#define MHINT 0x7C5 + +#define LDPC0 0xBE0 +#define LDPC1 0xBE1 + +#define STPC0 0xBF0 +#define STPC1 0xBF1 +#define STPC2 0xBF2 + /* RISC-V CSR macros * Adapted from https://github.com/michaeljclark/riscv-probe/blob/master/libfemto/include/arch/riscv/machine.h */ diff --git a/components/riscv/include/riscv/rv_utils.h b/components/riscv/include/riscv/rv_utils.h index 930ef14070..716fee6e80 100644 --- a/components/riscv/include/riscv/rv_utils.h +++ b/components/riscv/include/riscv/rv_utils.h @@ -138,12 +138,12 @@ FORCE_INLINE_ATTR void rv_utils_intr_global_disable(void) * and `interrupt_intc.h`. */ -#if SOC_CPU_HAS_FPU - /* ------------------------------------------------- FPU Related ---------------------------------------------------- * * ------------------------------------------------------------------------------------------------------------------ */ +#if SOC_CPU_HAS_FPU + FORCE_INLINE_ATTR bool rv_utils_enable_fpu(void) { /* Set mstatus[14:13] to 0b01 to start the floating-point unit initialization */ @@ -172,6 +172,48 @@ FORCE_INLINE_ATTR void rv_utils_disable_fpu(void) * * ------------------------------------------------------------------------------------------------------------------ */ +#if SOC_ASYNCHRONOUS_BUS_ERROR_MODE + +FORCE_INLINE_ATTR uintptr_t rv_utils_asynchronous_bus_get_error_pc(void) +{ + uint32_t error_pc; + uint32_t mcause, mexstatus; + + mexstatus = RV_READ_CSR(MEXSTATUS); + /* MEXSTATUS: Bit 8: Indicates that a load/store access fault (MCAUSE=5/7) + * is due to bus-error exception. If this bit is not cleared before exiting + * the exception handler, it will trigger a bus error again. + * Since we have not mechanisms to recover a normal program execution after + * load/store error appears, do nothing. */ + if ((mexstatus & BIT(8)) == 0) { + return 0; + } + mcause = RV_READ_CSR(mcause) & 0xFF; + if (mcause == 5) { /* Load access fault */ + /* Get the oldest PC at which the load instruction failed */ + error_pc = RV_READ_CSR(LDPC1); + if (error_pc == 0) { + error_pc = RV_READ_CSR(LDPC0); + } + } else if (mcause == 7) { /* Store access fault */ + /* Get the oldest PC at which the store instruction failed */ + error_pc = RV_READ_CSR(STPC2); + if (error_pc == 0) { + error_pc = RV_READ_CSR(STPC1); + if (error_pc == 0) { + error_pc = RV_READ_CSR(STPC0); + } + } + } else { + return 0; + } + /* Bit 0: Valid bit indicating that this CSR holds the PC (program counter). + * Clear this bit */ + return error_pc & ~(1); +} + +#endif // SOC_ASYNCHRONOUS_BUS_ERROR_MODE + /* ---------------------------------------------------- Debugging ------------------------------------------------------ * * ------------------------------------------------------------------------------------------------------------------ */ diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 631a0a708f..e7694d453d 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -1551,6 +1551,10 @@ config SOC_MEM_NON_CONTIGUOUS_SRAM bool default y +config SOC_ASYNCHRONOUS_BUS_ERROR_MODE + bool + default y + config SOC_EMAC_USE_IO_MUX bool default y diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 538e43066c..ffe827f739 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -629,6 +629,7 @@ /*-------------------------- Memory CAPS --------------------------*/ #define SOC_MEM_TCM_SUPPORTED (1) #define SOC_MEM_NON_CONTIGUOUS_SRAM (1) +#define SOC_ASYNCHRONOUS_BUS_ERROR_MODE (1) /*--------------------------- EMAC --------------------------------*/ #define SOC_EMAC_USE_IO_MUX (1) /*!< GPIO matrix is used to select GPIO pads */ diff --git a/tools/test_apps/system/panic/main/include/test_panic.h b/tools/test_apps/system/panic/main/include/test_panic.h index 09ba97fc88..5e96b2000c 100644 --- a/tools/test_apps/system/panic/main/include/test_panic.h +++ b/tools/test_apps/system/panic/main/include/test_panic.h @@ -39,6 +39,8 @@ void test_panic_extram_stack(void); void test_task_wdt_cpu1(void); #endif +void test_loadprohibited(void); + void test_storeprohibited(void); void test_cache_error(void); diff --git a/tools/test_apps/system/panic/main/test_app_main.c b/tools/test_apps/system/panic/main/test_app_main.c index 98be5a7d70..1a387f0018 100644 --- a/tools/test_apps/system/panic/main/test_app_main.c +++ b/tools/test_apps/system/panic/main/test_app_main.c @@ -101,6 +101,7 @@ void app_main(void) #if !CONFIG_FREERTOS_UNICORE HANDLE_TEST(test_name, test_task_wdt_cpu1); #endif + HANDLE_TEST(test_name, test_loadprohibited); HANDLE_TEST(test_name, test_storeprohibited); HANDLE_TEST(test_name, test_cache_error); HANDLE_TEST(test_name, test_int_wdt_cache_disabled); diff --git a/tools/test_apps/system/panic/main/test_panic.c b/tools/test_apps/system/panic/main/test_panic.c index 158bc3cf58..eab2c082ee 100644 --- a/tools/test_apps/system/panic/main/test_panic.c +++ b/tools/test_apps/system/panic/main/test_panic.c @@ -140,7 +140,15 @@ void test_task_wdt_cpu1(void) void __attribute__((no_sanitize_undefined)) test_storeprohibited(void) { - *(int*) 0x1 = 0; + *(int*) 0x4 = 0; + test_task_wdt_cpu0(); /* Trap for unhandled asynchronous bus errors */ +} + +void __attribute__((no_sanitize_undefined)) test_loadprohibited(void) +{ + static int __attribute__((used)) var; + var = *(int*) 0x4; + test_task_wdt_cpu0(); /* Trap for unhandled asynchronous bus errors */ } void IRAM_ATTR test_cache_error(void) @@ -222,7 +230,7 @@ void test_ub(void) * used for memory protection. * * However, this test is not valid for S2 and S3, because they have PMS - * enabled on top of this, giving unpredicatable results. + * enabled on top of this, giving unpredictable results. */ #if CONFIG_IDF_TARGET_ESP32 void test_illegal_access(void) diff --git a/tools/test_apps/system/panic/pytest_panic.py b/tools/test_apps/system/panic/pytest_panic.py index b0b48da20c..90154a40d2 100644 --- a/tools/test_apps/system/panic/pytest_panic.py +++ b/tools/test_apps/system/panic/pytest_panic.py @@ -365,14 +365,12 @@ def test_illegal_instruction( common_test(dut, config, expected_backtrace=get_default_backtrace(test_func_name)) -@pytest.mark.parametrize('config', CONFIGS, indirect=True) -@pytest.mark.generic -def test_storeprohibited(dut: PanicTestDut, config: str, test_func_name: str) -> None: +def check_x_prohibited(dut: PanicTestDut, config: str, test_func_name: str, operation: str) -> None: dut.run_test_func(test_func_name) if dut.is_xtensa: - dut.expect_gme('StoreProhibited') + dut.expect_gme(f'{operation}Prohibited') else: - dut.expect_gme('Store access fault') + dut.expect_gme(f'{operation} access fault') dut.expect_reg_dump(0) if dut.is_xtensa: dut.expect_backtrace() @@ -384,6 +382,18 @@ def test_storeprohibited(dut: PanicTestDut, config: str, test_func_name: str) -> common_test(dut, config, expected_backtrace=get_default_backtrace(test_func_name)) +@pytest.mark.parametrize('config', CONFIGS, indirect=True) +@pytest.mark.generic +def test_storeprohibited(dut: PanicTestDut, config: str, test_func_name: str) -> None: + check_x_prohibited(dut, config, test_func_name, 'Store') + + +@pytest.mark.parametrize('config', CONFIGS, indirect=True) +@pytest.mark.generic +def test_loadprohibited(dut: PanicTestDut, config: str, test_func_name: str) -> None: + check_x_prohibited(dut, config, test_func_name, 'Load') + + @pytest.mark.parametrize('config', CONFIGS, indirect=True) @pytest.mark.generic def test_abort(dut: PanicTestDut, config: str, test_func_name: str) -> None: