diff --git a/tools/test_apps/.build-test-rules.yml b/tools/test_apps/.build-test-rules.yml index 70dfa01002..58c9378d86 100644 --- a/tools/test_apps/.build-test-rules.yml +++ b/tools/test_apps/.build-test-rules.yml @@ -208,10 +208,8 @@ tools/test_apps/system/no_embedded_paths: reason: the other targets are not tested yet tools/test_apps/system/panic: - disable: - - if: IDF_TARGET in ["esp32p4"] - temporary: true - reason: target(s) not supported yet # TODO: IDF-7511 + enable: + - if: INCLUDE_DEFAULT == 1 or IDF_TARGET in ["esp32p4"] # preview targets tools/test_apps/system/ram_loadable_app: disable_test: diff --git a/tools/test_apps/system/build_test/sdkconfig.ci.no_hwsg b/tools/test_apps/system/build_test/sdkconfig.ci.no_hwsg new file mode 100644 index 0000000000..eb3631ffbe --- /dev/null +++ b/tools/test_apps/system/build_test/sdkconfig.ci.no_hwsg @@ -0,0 +1 @@ +CONFIG_ESP_SYSTEM_HW_STACK_GUARD=n diff --git a/tools/test_apps/system/panic/README.md b/tools/test_apps/system/panic/README.md index 87ef04a8a6..fae6b93717 100644 --- a/tools/test_apps/system/panic/README.md +++ b/tools/test_apps/system/panic/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | # Introduction 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 68715152c9..09ba97fc88 100644 --- a/tools/test_apps/system/panic/main/include/test_panic.h +++ b/tools/test_apps/system/panic/main/include/test_panic.h @@ -24,7 +24,12 @@ void test_int_wdt(void); void test_task_wdt_cpu0(void); +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD void test_hw_stack_guard_cpu0(void); +#if !CONFIG_FREERTOS_UNICORE +void test_hw_stack_guard_cpu1(void); +#endif // CONFIG_FREERTOS_UNICORE +#endif // CONFIG_ESP_SYSTEM_HW_STACK_GUARD #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY void test_panic_extram_stack(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 aecd9f0836..98be5a7d70 100644 --- a/tools/test_apps/system/panic/main/test_app_main.c +++ b/tools/test_apps/system/panic/main/test_app_main.c @@ -31,9 +31,7 @@ static const char* get_test_name(void) { static char test_name_str[BOOT_CMD_MAX_LEN] = {0}; - - printf("Enter test name: "); - fflush(stdout); + bool print_prompt = true; /* Not using blocking fgets(stdin) here, as QEMU doesn't yet implement RX timeout interrupt, * which is required for the UART driver and blocking stdio to work. @@ -42,16 +40,24 @@ static const char* get_test_name(void) char *p = test_name_str; const char *end = test_name_str + sizeof(test_name_str) - 1; while (p < end) { + if (print_prompt) { + printf("Enter test name: "); + fflush(stdout); + print_prompt = false; + } c = getchar(); if (c == EOF) { vTaskDelay(pdMS_TO_TICKS(10)); - } else if ((c == '\r' || c == '\n') && p != test_name_str) { + } else if (c == '\r' || c == '\n') { /* terminate the line */ puts("\n\r"); fflush(stdout); - *p = '\0'; - break; - } else { + print_prompt = true; + if (p != test_name_str) { + *p = '\0'; + break; + } + } else if (c >= '0' && c <= 'z') { /* echo the received character */ putchar(c); fflush(stdout); @@ -83,7 +89,12 @@ void app_main(void) HANDLE_TEST(test_name, test_abort_cache_disabled); HANDLE_TEST(test_name, test_int_wdt); HANDLE_TEST(test_name, test_task_wdt_cpu0); +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD HANDLE_TEST(test_name, test_hw_stack_guard_cpu0); +#if !CONFIG_FREERTOS_UNICORE + HANDLE_TEST(test_name, test_hw_stack_guard_cpu1); +#endif // CONFIG_FREERTOS_UNICORE +#endif // CONFIG_ESP_SYSTEM_HW_STACK_GUARD #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY HANDLE_TEST(test_name, test_panic_extram_stack); #endif diff --git a/tools/test_apps/system/panic/main/test_panic.c b/tools/test_apps/system/panic/main/test_panic.c index adb9b3eff3..158bc3cf58 100644 --- a/tools/test_apps/system/panic/main/test_panic.c +++ b/tools/test_apps/system/panic/main/test_panic.c @@ -63,6 +63,8 @@ void test_task_wdt_cpu0(void) } } +#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD + __attribute__((optimize("-O0"))) static void test_hw_stack_guard_cpu(void* arg) { @@ -78,6 +80,18 @@ void test_hw_stack_guard_cpu0(void) } } +#if !CONFIG_FREERTOS_UNICORE +void test_hw_stack_guard_cpu1(void) +{ + xTaskCreatePinnedToCore(test_hw_stack_guard_cpu, "HWSG1", 512, NULL, 1, NULL, 1); + while (true) { + vTaskDelay(100); + } +} + +#endif // CONFIG_FREERTOS_UNICORE +#endif // CONFIG_ESP_SYSTEM_HW_STACK_GUARD + #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY static void stack_in_extram(void* arg) { diff --git a/tools/test_apps/system/panic/pytest_panic.py b/tools/test_apps/system/panic/pytest_panic.py index 08da655338..5017640194 100644 --- a/tools/test_apps/system/panic/pytest_panic.py +++ b/tools/test_apps/system/panic/pytest_panic.py @@ -17,7 +17,8 @@ TARGETS_TESTED = [ pytest.mark.esp32s3, pytest.mark.esp32c2, pytest.mark.esp32c6, - pytest.mark.esp32h2 + pytest.mark.esp32h2, + pytest.mark.esp32p4, ] # Most tests run on all targets and with all configs. @@ -34,7 +35,11 @@ CONFIGS = [ ] # Some tests only run on dual-core targets, they use the config below. -TARGETS_DUAL_CORE = [pytest.mark.esp32, pytest.mark.esp32s3] +TARGETS_DUAL_CORE = [ + pytest.mark.esp32, + pytest.mark.esp32s3, + pytest.mark.esp32p4, +] CONFIGS_DUAL_CORE = [ pytest.param('coredump_flash_bin_crc', marks=TARGETS_DUAL_CORE), pytest.param('coredump_flash_elf_sha', marks=TARGETS_DUAL_CORE), @@ -58,6 +63,7 @@ TARGETS_HW_STACK_GUARD = [ pytest.mark.esp32c3, pytest.mark.esp32c6, pytest.mark.esp32h2, + pytest.mark.esp32p4, ] CONFIGS_HW_STACK_GUARD = [ @@ -68,6 +74,14 @@ CONFIGS_HW_STACK_GUARD = [ pytest.param('panic', marks=TARGETS_HW_STACK_GUARD), ] +CONFIGS_HW_STACK_GUARD_DUAL_CORE = [ + pytest.param('coredump_flash_bin_crc', marks=[pytest.mark.esp32p4]), + pytest.param('coredump_uart_bin_crc', marks=[pytest.mark.esp32p4]), + pytest.param('coredump_uart_elf_crc', marks=[pytest.mark.esp32p4]), + pytest.param('gdbstub', marks=[pytest.mark.esp32p4]), + pytest.param('panic', marks=[pytest.mark.esp32p4]), +] + # Panic abort information will start with this string. PANIC_ABORT_PREFIX = 'Panic reason: ' @@ -148,15 +162,16 @@ def test_task_wdt_cpu1(dut: PanicTestDut, config: str, test_func_name: str) -> N 'Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:' ) dut.expect_exact('CPU 1: Infinite loop') + expected_backtrace = ['infinite_loop', 'vPortTaskWrapper'] if dut.is_xtensa: # see comment in test_task_wdt_cpu0 dut.expect_none('register dump:') dut.expect_exact('Print CPU 1 backtrace') dut.expect_backtrace() - # On Xtensa, we get incorrect backtrace from GDB in this test - expected_backtrace = ['infinite_loop', 'vPortTaskWrapper'] else: - assert False, 'No dual-core RISC-V chips yet, check this test case later' + # on RISC-V, need to dump both registers and stack memory to reconstruct the backtrace + dut.expect_reg_dump(core=1) + dut.expect_stack_dump() dut.expect_elf_sha256() dut.expect_none('Guru Meditation') @@ -204,9 +219,9 @@ def test_int_wdt( dut.expect_stack_dump() if target in TARGETS_DUAL_CORE_NAMES: - assert dut.is_xtensa, 'No dual-core RISC-V chips yet, check the test case' dut.expect_reg_dump(1) - dut.expect_backtrace() + if dut.is_xtensa: + dut.expect_backtrace() dut.expect_elf_sha256() dut.expect_none('Guru Meditation') @@ -228,9 +243,9 @@ def test_int_wdt_cache_disabled( dut.expect_stack_dump() if target in TARGETS_DUAL_CORE_NAMES: - assert dut.is_xtensa, 'No dual-core RISC-V chips yet, check the test case' dut.expect_reg_dump(1) - dut.expect_backtrace() + if dut.is_xtensa: + dut.expect_backtrace() dut.expect_elf_sha256() dut.expect_none('Guru Meditation') @@ -251,6 +266,8 @@ def test_cache_error(dut: PanicTestDut, config: str, test_func_name: str) -> Non elif dut.target in ['esp32s2']: # Cache error interrupt is not enabled, IDF-1558 dut.expect_gme('IllegalInstruction') + elif dut.target in ['esp32p4']: # TODO IDF-7515 + dut.expect_gme('Instruction access fault') else: dut.expect_gme('Cache disabled but cached memory region accessed') dut.expect_reg_dump(0) @@ -832,15 +849,36 @@ def test_gdbstub_coredump(dut: PanicTestDut) -> None: return # don't expect "Rebooting" output below +def test_hw_stack_guard_cpu(dut: PanicTestDut, cpu: int) -> None: + dut.expect_exact(f'Guru Meditation Error: Core {cpu} panic\'ed (Stack protection fault).') + dut.expect_none('ASSIST_DEBUG is not triggered BUT interrupt occured!') + dut.expect_exact(f'Detected in task "HWSG{cpu}"') + addr = dut.expect('at 0x([0-9a-fA-F]{8})') + assert addr.group(1) != b'00000000' + addr = dut.expect('Stack pointer: 0x([0-9a-fA-F]{8})') + assert addr.group(1) != b'00000000' + addr = dut.expect(r'Stack bounds: 0x([0-9a-fA-F]{8})') + assert addr.group(1) != b'00000000' + start_addr = int(addr.group(1), 16) + addr = dut.expect(r' - 0x([0-9a-fA-F]{8})') + assert addr.group(1) != b'00000000' + end_addr = int(addr.group(1), 16) + assert end_addr > start_addr + + @pytest.mark.parametrize('config', CONFIGS_HW_STACK_GUARD, indirect=True) @pytest.mark.generic def test_hw_stack_guard_cpu0(dut: PanicTestDut, config: str, test_func_name: str) -> None: dut.run_test_func(test_func_name) - dut.expect_exact('Guru Meditation Error: Core 0 panic\'ed (Stack protection fault).') - dut.expect_none('ASSIST_DEBUG is not triggered BUT interrupt occured!') - dut.expect(r'Detected in task(.*)at 0x') - dut.expect_exact('Stack pointer: 0x') - dut.expect(r'Stack bounds: 0x(.*) - 0x') + test_hw_stack_guard_cpu(dut, 0) + common_test(dut, config) + + +@pytest.mark.parametrize('config', CONFIGS_HW_STACK_GUARD_DUAL_CORE, indirect=True) +@pytest.mark.generic +def test_hw_stack_guard_cpu1(dut: PanicTestDut, config: str, test_func_name: str) -> None: + dut.run_test_func(test_func_name) + test_hw_stack_guard_cpu(dut, 1) common_test(dut, config) diff --git a/tools/test_apps/system/panic/test_panic_util/panic_dut.py b/tools/test_apps/system/panic/test_panic_util/panic_dut.py index 61a5d134b0..8600754c67 100644 --- a/tools/test_apps/system/panic/test_panic_util/panic_dut.py +++ b/tools/test_apps/system/panic/test_panic_util/panic_dut.py @@ -1,14 +1,23 @@ -# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Unlicense OR CC0-1.0 import logging import os import re import subprocess import sys -from typing import Any, Dict, List, Optional, TextIO, Union +from typing import Any +from typing import Dict +from typing import List +from typing import Optional +from typing import TextIO +from typing import Union import pexpect -from panic_utils import NoGdbProcessError, attach_logger, quote_string, sha256, verify_valid_gdb_subprocess +from panic_utils import attach_logger +from panic_utils import NoGdbProcessError +from panic_utils import quote_string +from panic_utils import sha256 +from panic_utils import verify_valid_gdb_subprocess from pygdbmi.gdbcontroller import GdbController from pytest_embedded_idf.app import IdfApp from pytest_embedded_idf.dut import IdfDut @@ -53,7 +62,7 @@ class PanicTestDut(IdfDut): @property def is_multi_core(self) -> bool: - return self.target in ['esp32', 'esp32s3'] + return self.target in ['esp32', 'esp32s3', 'esp32p4'] def run_test_func(self, test_func_name: str) -> None: self.expect_exact('Enter test name:')