diff --git a/components/app_update/CMakeLists.txt b/components/app_update/CMakeLists.txt index 8beab3d5388..39ff14f0213 100644 --- a/components/app_update/CMakeLists.txt +++ b/components/app_update/CMakeLists.txt @@ -19,8 +19,8 @@ idf_component_optional_requires(PRIVATE mbedtls) if(CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP) # ota_auto_confirm.c has no other externally referenced symbol, so force the linker to pull - # it in from the archive to override the weak esp_ota_confirm_rollback_hook() in freertos. - target_link_libraries(${COMPONENT_LIB} INTERFACE "-u esp_ota_confirm_rollback_hook") + # its static PRE_APP_MAIN event registration from the archive. + target_link_libraries(${COMPONENT_LIB} INTERFACE "-u esp_ota_auto_confirm_include") endif() idf_define_esp_err_codes(HEADERS include/esp_ota_ops.h) diff --git a/components/app_update/ota_auto_confirm.c b/components/app_update/ota_auto_confirm.c index 14744a5cbd0..38dd2a9b741 100644 --- a/components/app_update/ota_auto_confirm.c +++ b/components/app_update/ota_auto_confirm.c @@ -10,17 +10,20 @@ #include "esp_ota_ops.h" #include "esp_log.h" +#include "esp_private/esp_sys_event_app_init.h" ESP_LOG_ATTR_TAG(TAG, "ota_auto_confirm"); -// Strong override of the weak hook declared in components/freertos/app_startup.c -void esp_ota_confirm_rollback_hook(void) +ESP_PRE_APP_MAIN_HANDLER_REGISTER(esp_ota_auto_confirm, 200) { + (void)user_arg; + (void)ctx; + esp_ota_img_states_t ota_state; const esp_partition_t *running = esp_ota_get_running_partition(); if (esp_ota_get_state_partition(running, &ota_state) != ESP_OK) { - return; + return ESP_OK; } if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) { @@ -33,13 +36,16 @@ void esp_ota_confirm_rollback_hook(void) ESP_LOGW(TAG, "Bootloader is not capable of rollback"); } else { - return; + return ESP_OK; } esp_err_t err = esp_ota_mark_app_valid_cancel_rollback(); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to mark app valid: %s", esp_err_to_name(err)); } + return ESP_OK; } +void esp_ota_auto_confirm_include(void) {} + #endif /* CONFIG_BOOTLOADER_APP_ROLLBACK_CONFIRM_ON_STARTUP */ diff --git a/components/esp_gdbstub/CMakeLists.txt b/components/esp_gdbstub/CMakeLists.txt index b0dbfba0885..78bc8b3b9db 100644 --- a/components/esp_gdbstub/CMakeLists.txt +++ b/components/esp_gdbstub/CMakeLists.txt @@ -34,3 +34,12 @@ if(CONFIG_ESP_SYSTEM_PANIC_GDBSTUB) # Make sure the panic event registration in gdbstub.c is considered by the linker target_link_libraries(${COMPONENT_LIB} INTERFACE "-u esp_gdbstub_panic_handler") endif() + +if(CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME) + # Pull in the PRE_SCHEDULER registration from gdbstub.c. + target_link_libraries(${COMPONENT_LIB} INTERFACE "-u esp_gdbstub_init") + if(CONFIG_IDF_TARGET_ARCH_XTENSA) + # Pull in the Xtensa debug exception handler from its separate object file. + target_link_libraries(${COMPONENT_LIB} INTERFACE "-u _xt_panic_gdbstub") + endif() +endif() diff --git a/components/esp_gdbstub/src/gdbstub.c b/components/esp_gdbstub/src/gdbstub.c index a46dd280ee5..afd9face188 100644 --- a/components/esp_gdbstub/src/gdbstub.c +++ b/components/esp_gdbstub/src/gdbstub.c @@ -25,6 +25,7 @@ #endif #include "esp_private/esp_sys_event_panic.h" +#include "esp_private/esp_sys_event_app_init.h" #include "esp_private/panic_internal.h" #include "esp_private/crosscore_int.h" @@ -451,6 +452,14 @@ void esp_gdbstub_init(void) esp_intr_alloc(ETS_UART0_INTR_SOURCE, 0, esp_gdbstub_int, NULL, NULL); esp_gdbstub_init_dports(); } + +ESP_PRE_SCHEDULER_HANDLER_REGISTER(init_gdbstub, 130) +{ + (void)user_arg; + (void)ctx; + esp_gdbstub_init(); + return ESP_OK; +} #endif /* CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME */ #ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS diff --git a/components/esp_psram/system_layer/esp_psram.c b/components/esp_psram/system_layer/esp_psram.c index bdef3d5e15f..aa62999dbdf 100644 --- a/components/esp_psram/system_layer/esp_psram.c +++ b/components/esp_psram/system_layer/esp_psram.c @@ -33,6 +33,7 @@ #include "esp_private/esp_psram_mspi.h" #include "esp_private/mspi_mem_barrier.h" #include "esp_private/startup_internal.h" +#include "esp_private/esp_sys_event_app_init.h" #if SOC_SPIRAM_XIP_SUPPORTED #include "esp_private/mmu_psram_flash.h" #endif @@ -675,6 +676,23 @@ esp_err_t esp_psram_extram_reserve_dma_pool(size_t size) return ESP_OK; } +#if CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL +ESP_PRE_APP_MAIN_HANDLER_REGISTER(reserve_dma_pool, 110) +{ + (void)user_arg; + (void)ctx; + if (!esp_psram_is_initialized()) { + return ESP_OK; + } + + esp_err_t err = esp_psram_extram_reserve_dma_pool(CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Could not reserve internal/DMA pool (error 0x%x)", err); + } + return err; +} +#endif + bool IRAM_ATTR __attribute__((pure)) esp_psram_is_initialized(void) { return s_psram_ctx.is_initialised; diff --git a/components/esp_system/CMakeLists.txt b/components/esp_system/CMakeLists.txt index 1626078a251..69f63936f45 100644 --- a/components/esp_system/CMakeLists.txt +++ b/components/esp_system/CMakeLists.txt @@ -183,6 +183,11 @@ endif() target_link_libraries(${COMPONENT_LIB} INTERFACE "-u esp_system_include_startup_funcs") +if(CONFIG_ESP_INT_WDT) + # Pull in the PRE_SCHEDULER registrations from int_wdt.c. + target_link_libraries(${COMPONENT_LIB} INTERFACE "-u esp_int_wdt_init") +endif() + # [refactor-todo] requirements due to init code, should be removable # once link-time registration of component init functions is used. if(NOT CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT) diff --git a/components/esp_system/check_system_init_priorities.py b/components/esp_system/check_system_init_priorities.py index 555f64d7e13..82fde48f2a8 100644 --- a/components/esp_system/check_system_init_priorities.py +++ b/components/esp_system/check_system_init_priorities.py @@ -3,7 +3,7 @@ # SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 # -# This file is used to check the order of execution of ESP_SYSTEM_INIT_FN functions. +# This file checks the order of execution of system startup handlers. # It compares the priorities found in .c source files to the contents of system_init_fn.txt # In case of an inconsistency, the script prints the differences found and returns with a # non-zero exit code. @@ -15,12 +15,47 @@ import re import sys COMMENT_REGEX = re.compile(r'//.*?$|/\*.*?\*/', re.DOTALL | re.MULTILINE) -ESP_SYSTEM_INIT_FN_REGEX_SIMPLE = re.compile(r'\bESP_SYSTEM_INIT_FN\s*\(') -ESP_SYSTEM_INIT_FN_REGEX = re.compile( - r'ESP_SYSTEM_INIT_FN\(([a-zA-Z0-9_]+)\s*,\s*([a-zA-Z\ _0-9\(\)|]+)\s*,\s*([a-zA-Z\ _0-9\(\)|]+)\s*,\s*([0-9]+)\)' +ESP_SYSTEM_INIT_FN_REGEX = ( + r'{macro}\((?P[a-zA-Z0-9_]+)\s*,\s*' + r'(?P[a-zA-Z\ _0-9\(\)|]+)\s*,\s*' + r'(?P[a-zA-Z\ _0-9\(\)|]+)\s*,\s*' + r'(?P[0-9]+)\)' +) +APP_INIT_FN_REGEX = r'{macro}\s*\(\s*(?P[a-zA-Z0-9_]+)\s*,\s*(?P[0-9]+)\s*\)' +STARTUP_REGISTRATIONS = ( + ( + 'ESP_SYSTEM_INIT_FN', + re.compile(ESP_SYSTEM_INIT_FN_REGEX.format(macro='ESP_SYSTEM_INIT_FN')), + '', + '', + ), + ( + 'ESP_PRE_SCHEDULER_HANDLER_REGISTER', + re.compile(APP_INIT_FN_REGEX.format(macro='ESP_PRE_SCHEDULER_HANDLER_REGISTER')), + 'PRE_SCHEDULER', + 'BIT(0)', + ), + ( + 'ESP_PRE_SCHEDULER_HANDLER_REGISTER_PER_CPU', + re.compile(APP_INIT_FN_REGEX.format(macro='ESP_PRE_SCHEDULER_HANDLER_REGISTER_PER_CPU')), + 'PRE_SCHEDULER', + 'ESP_SYSTEM_INIT_ALL_CORES', + ), + ( + 'ESP_PRE_APP_MAIN_HANDLER_REGISTER', + re.compile(APP_INIT_FN_REGEX.format(macro='ESP_PRE_APP_MAIN_HANDLER_REGISTER')), + 'PRE_APP_MAIN', + 'MAIN_TASK', + ), ) STARTUP_ENTRIES_FILE = 'components/esp_system/system_init_fn.txt' EXCLUDED_SOURCE_DIRS = {'test_apps', 'host_test', 'host_tests'} +STAGE_ORDER = { + 'CORE': 0, + 'SECONDARY': 1, + 'PRE_SCHEDULER': 2, + 'PRE_APP_MAIN': 3, +} class StartupEntry: @@ -69,36 +104,36 @@ def main() -> None: file_contents = f_obj.read() file_contents_no_comments = strip_comments(file_contents) - if not ESP_SYSTEM_INIT_FN_REGEX_SIMPLE.search(file_contents_no_comments): - continue + for macro, pattern, default_stage, default_affinity in STARTUP_REGISTRATIONS: + count_expected = len(re.findall(rf'\b{macro}\s*\(', file_contents_no_comments)) + found = list(pattern.finditer(file_contents_no_comments)) + if len(found) != count_expected: + print( + f'error: In {filename}, found {macro} {count_expected} time(s), ' + f'but regular expression matched {len(found)} time(s)', + file=sys.stderr, + ) + has_errors = True - count_expected = len(ESP_SYSTEM_INIT_FN_REGEX_SIMPLE.findall(file_contents_no_comments)) - found = ESP_SYSTEM_INIT_FN_REGEX.findall(file_contents_no_comments) - if len(found) != count_expected: - print( - ( - f'error: In {filename}, found ESP_SYSTEM_INIT_FN {count_expected} time(s), ' - f'but regular expression matched {len(found)} time(s)' - ), - file=sys.stderr, - ) - has_errors = True - - for match in found: - entry = StartupEntry( - filename=relpath, func=match[0], stage=match[1], affinity=match[2], priority=int(match[3]) - ) - startup_entries.append(entry) + for match in found: + startup_entries.append( + StartupEntry( + relpath, + match.group('func'), + match.groupdict().get('stage') or default_stage, + match.groupdict().get('affinity') or default_affinity, + int(match.group('priority')), + ) + ) # - # 2. Sort the ESP_SYSTEM_INIT_FN functions in C source files. + # 2. Sort the startup handlers in C source files. # In addition to the stage and priority, we also add filename to the sort key, # to have a stable sorting order in case when the same startup function is defined in multiple files, # for example for different targets. # - def sort_key(entry: StartupEntry) -> tuple[str, int, str]: - # luckily 'core' and 'secondary' are in alphabetical order, so we can return the string - return (entry.stage, entry.priority, entry.filename) + def sort_key(entry: StartupEntry) -> tuple[int, int, str]: + return (STAGE_ORDER[entry.stage], entry.priority, entry.filename) startup_entries = list(sorted(startup_entries, key=sort_key)) startup_entries_lines = [str(entry) for entry in startup_entries] diff --git a/components/esp_system/crosscore_int.c b/components/esp_system/crosscore_int.c index 860c8d8e6be..c6e49804411 100644 --- a/components/esp_system/crosscore_int.c +++ b/components/esp_system/crosscore_int.c @@ -11,6 +11,7 @@ #include "esp_intr_alloc.h" #include "esp_debug_helpers.h" #include "esp_private/crosscore_int.h" +#include "esp_private/esp_sys_event_app_init.h" #include "soc/periph_defs.h" #include "soc/system_intr.h" #include "hal/crosscore_int_ll.h" @@ -113,6 +114,14 @@ void esp_crosscore_int_init(void) ESP_ERROR_CHECK(err); } +ESP_PRE_SCHEDULER_HANDLER_REGISTER_PER_CPU(init_crosscore_interrupt, 120) +{ + (void)user_arg; + (void)ctx; + esp_crosscore_int_init(); + return ESP_OK; +} + static void ESP_SYSTEM_IRAM_ATTR esp_crosscore_int_send(int core_id, uint32_t reason_mask) { assert(core_id < CONFIG_FREERTOS_NUMBER_OF_CORES); diff --git a/components/esp_system/include/esp_private/esp_sys_event_app_init.h b/components/esp_system/include/esp_private/esp_sys_event_app_init.h new file mode 100644 index 00000000000..dff7c01c6b9 --- /dev/null +++ b/components/esp_system/include/esp_private/esp_sys_event_app_init.h @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_cpu.h" +#include "esp_private/esp_sys_event.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Register a handler to run on core 0 immediately before the scheduler starts + */ +#define ESP_PRE_SCHEDULER_HANDLER_REGISTER(fn, priority) \ + static esp_err_t fn(void *user_arg, void *ctx); \ + _ESP_SYS_EVENT_REGISTER(PRE_SCHEDULER, priority, __esp_pre_scheduler_##fn) \ + { \ + if (esp_cpu_get_core_id() != 0) { \ + return ESP_OK; \ + } \ + return fn(user_arg, ctx); \ + } \ + static esp_err_t fn(void *user_arg, void *ctx) + +/** + * @brief Register a handler to run on every core immediately before its scheduler starts + */ +#define ESP_PRE_SCHEDULER_HANDLER_REGISTER_PER_CPU(fn, priority) \ + _ESP_SYS_EVENT_REGISTER(PRE_SCHEDULER, priority, fn) + +/** + * @brief Register a handler to run in the main task immediately before app_main + * + * Handlers run in ascending priority order after all CPUs have completed + * FreeRTOS initialization. The scheduler and heap allocator are available. + */ +#define ESP_PRE_APP_MAIN_HANDLER_REGISTER(fn, priority) \ + _ESP_SYS_EVENT_REGISTER(PRE_APP_MAIN, priority, fn) + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_system/include/esp_sys_event_list.inc b/components/esp_system/include/esp_sys_event_list.inc index 8c3e34d3cd6..3e8892fd728 100644 --- a/components/esp_system/include/esp_sys_event_list.inc +++ b/components/esp_system/include/esp_sys_event_list.inc @@ -34,6 +34,8 @@ #define _ESP_SYS_EVENT_SECTION_SHUTDOWN esysev_shdn #define _ESP_SYS_EVENT_SECTION_SYSTEM_INIT_CORE esysev_initc #define _ESP_SYS_EVENT_SECTION_SYSTEM_INIT_SECONDARY esysev_inits +#define _ESP_SYS_EVENT_SECTION_PRE_SCHEDULER esysev_presc +#define _ESP_SYS_EVENT_SECTION_PRE_APP_MAIN esysev_pream #define _ESP_SYS_EVENT_SECTION_PANIC esysev_panic #define _ESP_SYS_EVENT_SECTION_PANIC_EARLY_BREAK esysev_paneb #endif @@ -42,5 +44,7 @@ ESP_SYS_EVENT_DEF(SHUTDOWN) ESP_SYS_EVENT_DEF(SYSTEM_INIT_CORE) ESP_SYS_EVENT_DEF(SYSTEM_INIT_SECONDARY) +ESP_SYS_EVENT_DEF(PRE_SCHEDULER) +ESP_SYS_EVENT_DEF(PRE_APP_MAIN) ESP_SYS_EVENT_DEF(PANIC) ESP_SYS_EVENT_DEF(PANIC_EARLY_BREAK) diff --git a/components/esp_system/int_wdt.c b/components/esp_system/int_wdt.c index b996be5a42e..5ca823f1c39 100644 --- a/components/esp_system/int_wdt.c +++ b/components/esp_system/int_wdt.c @@ -23,6 +23,7 @@ #include "esp_freertos_hooks.h" #include "esp_private/periph_ctrl.h" #include "esp_private/esp_int_wdt.h" +#include "esp_private/esp_sys_event_app_init.h" #include "mwdt_priv.h" #if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP && SOC_MWDT_SUPPORT_SLEEP_RETENTION @@ -194,6 +195,22 @@ void esp_int_wdt_cpu_init(void) esp_intr_enable_source(ETS_INT_WDT_INUM); } +ESP_PRE_SCHEDULER_HANDLER_REGISTER(init_int_wdt, 100) +{ + (void)user_arg; + (void)ctx; + esp_int_wdt_init(); + return ESP_OK; +} + +ESP_PRE_SCHEDULER_HANDLER_REGISTER_PER_CPU(init_int_wdt_cpu, 110) +{ + (void)user_arg; + (void)ctx; + esp_int_wdt_cpu_init(); + return ESP_OK; +} + void ESP_SYSTEM_IRAM_ATTR esp_int_wdt_pause(void) { wdt_hal_write_protect_disable(&iwdt_context); diff --git a/components/esp_system/ld/linux/sections.ld b/components/esp_system/ld/linux/sections.ld index 2fac1da58ca..3d411379049 100644 --- a/components/esp_system/ld/linux/sections.ld +++ b/components/esp_system/ld/linux/sections.ld @@ -22,6 +22,16 @@ SECTIONS KEEP(*(SORT_BY_INIT_PRIORITY(.esysev_inits.*))) PROVIDE(_esysev_inits_end = .); + . = ALIGN(4); + PROVIDE(_esysev_presc_start = .); + KEEP(*(SORT_BY_INIT_PRIORITY(.esysev_presc.*))) + PROVIDE(_esysev_presc_end = .); + + . = ALIGN(4); + PROVIDE(_esysev_pream_start = .); + KEEP(*(SORT_BY_INIT_PRIORITY(.esysev_pream.*))) + PROVIDE(_esysev_pream_end = .); + . = ALIGN(4); PROVIDE(_esysev_panic_start = .); KEEP(*(SORT_BY_INIT_PRIORITY(.esysev_panic.*))) diff --git a/components/esp_system/linker.lf b/components/esp_system/linker.lf index 52fc3d4133f..d9e9fc54c00 100644 --- a/components/esp_system/linker.lf +++ b/components/esp_system/linker.lf @@ -12,6 +12,14 @@ entries: entries: .esysev_inits+ +[sections:esysev_presc] +entries: + .esysev_presc+ + +[sections:esysev_pream] +entries: + .esysev_pream+ + [sections:esysev_panic] entries: .esysev_panic+ @@ -26,6 +34,8 @@ entries: esysev_shdn -> flash_rodata esysev_initc -> flash_rodata esysev_inits -> flash_rodata + esysev_presc -> flash_rodata + esysev_pream -> flash_rodata esysev_panic -> flash_rodata esysev_paneb -> flash_rodata @@ -36,6 +46,8 @@ entries: esysev_shdn -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_shdn), esysev_initc -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_initc), esysev_inits -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_inits), + esysev_presc -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_presc), + esysev_pream -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_pream), esysev_panic -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_panic), esysev_paneb -> flash_rodata ALIGN(4, pre) KEEP() SORT(init_priority) SURROUND(esysev_paneb) diff --git a/components/esp_system/startup_funcs.c b/components/esp_system/startup_funcs.c index 96c61a275bc..ccebf62e03c 100644 --- a/components/esp_system/startup_funcs.c +++ b/components/esp_system/startup_funcs.c @@ -6,6 +6,7 @@ #include #include +#include #include "sdkconfig.h" #include "esp_attr.h" @@ -13,8 +14,10 @@ #include "esp_check.h" #include "esp_system.h" #include "esp_log.h" +#include "esp_chip_info.h" #include "esp_xt_wdt.h" #include "esp_cpu.h" +#include "esp_private/esp_sys_event_app_init.h" #include "esp_private/startup_internal.h" #include "freertos/FreeRTOS.h" #include "soc/soc_caps.h" @@ -50,6 +53,16 @@ void esp_system_include_startup_funcs(void) // [refactor-todo] Most of these init functions should be moved to the respective components. +#if !CONFIG_ESP_INT_WDT && CONFIG_ESP32_ECO3_CACHE_LOCK_FIX +ESP_PRE_SCHEDULER_HANDLER_REGISTER(check_cache_lock_bug, 90) +{ + (void)user_arg; + (void)ctx; + assert(!soc_has_cache_lock_bug() && "ESP32 Rev 3 + Dual Core + PSRAM requires INT WDT enabled in project config!"); + return ESP_OK; +} +#endif + ESP_SYSTEM_INIT_FN(init_show_cpu_freq, CORE, BIT(0), 10) { ESP_EARLY_LOGI(TAG, "Pro cpu start user code"); diff --git a/components/esp_system/system_init_fn.txt b/components/esp_system/system_init_fn.txt index af5c98acb8d..60c105f25ab 100644 --- a/components/esp_system/system_init_fn.txt +++ b/components/esp_system/system_init_fn.txt @@ -1,7 +1,7 @@ -# This file documents the expected order of execution of ESP_SYSTEM_INIT_FN functions. +# This file documents the expected order of execution of system startup handlers. # -# When adding new ESP_SYSTEM_INIT_FN functions or changing init priorities of existing functions, -# keep this file up to date. This is checked in CI. +# When adding startup handlers or changing their priorities, keep this file up to date. +# This is checked in CI. # When adding new functions or changing the priorities, please read the comments and see if # they need to be updated to be consistent with the changes you are making. # @@ -9,7 +9,7 @@ # Each line has the following format: # stage: prio: function_name in path/to/source_file on affinity_expression # Where: -# stage: which startup stage the function is executed in (CORE or SECONDARY) +# stage: which startup stage the function is executed in (CORE, SECONDARY, PRE_SCHEDULER, or PRE_APP_MAIN) # prio: priority value (higher value means function is executed later) # affinity_expression: bit map of cores the function is executed on @@ -148,4 +148,35 @@ SECONDARY: 240: psram_adjust_timing_point_via_temperature in components/esp_hw_s # Now that the application is about to start, disable boot watchdog SECONDARY: 999: init_disable_rtc_wdt in components/esp_system/startup_funcs.c on BIT(0) -# DO NOT add new init functions here. Add them to the correct stage above. +# DO NOT add new init functions at the end of a stage. Add them at the correct priority. + + +########### PRE_SCHEDULER startup stage ########### + +# If the interrupt watchdog is disabled, validate the ESP32 ECO3 cache lock configuration instead. +PRE_SCHEDULER: 90: check_cache_lock_bug in components/esp_system/startup_funcs.c on BIT(0) + +# Initialize the interrupt watchdog globally, then enable it on each core. +PRE_SCHEDULER: 100: init_int_wdt in components/esp_system/int_wdt.c on BIT(0) +PRE_SCHEDULER: 110: init_int_wdt_cpu in components/esp_system/int_wdt.c on ESP_SYSTEM_INIT_ALL_CORES + +# Cross-core interrupts must be available before each core starts its scheduler. +PRE_SCHEDULER: 120: init_crosscore_interrupt in components/esp_system/crosscore_int.c on ESP_SYSTEM_INIT_ALL_CORES + +# Runtime GDB stub initialization requires cross-core interrupts. +PRE_SCHEDULER: 130: init_gdbstub in components/esp_gdbstub/src/gdbstub.c on BIT(0) + + +########### PRE_APP_MAIN startup stage ########### + +# Reclaim startup stacks only after every core has completed FreeRTOS initialization. +PRE_APP_MAIN: 100: enable_nonos_stack_heaps in components/heap/heap_caps_init.c on MAIN_TASK + +# Reserve the internal DMA pool after startup stack memory has been reclaimed. +PRE_APP_MAIN: 110: reserve_dma_pool in components/esp_psram/system_layer/esp_psram.c on MAIN_TASK + +# Initialize the task watchdog after the scheduler is available. +PRE_APP_MAIN: 120: init_task_wdt in components/esp_system/task_wdt/task_wdt.c on MAIN_TASK + +# Confirm the running OTA image immediately before calling app_main. +PRE_APP_MAIN: 200: esp_ota_auto_confirm in components/app_update/ota_auto_confirm.c on MAIN_TASK diff --git a/components/esp_system/task_wdt/task_wdt.c b/components/esp_system/task_wdt/task_wdt.c index f41eb0b1d44..6ffa5387b27 100644 --- a/components/esp_system/task_wdt/task_wdt.c +++ b/components/esp_system/task_wdt/task_wdt.c @@ -23,6 +23,7 @@ #include "esp_task_wdt.h" #include "esp_private/system_internal.h" #include "esp_private/crosscore_int.h" +#include "esp_private/esp_sys_event_app_init.h" #include "esp_private/esp_task_wdt.h" #include "esp_private/esp_task_wdt_impl.h" @@ -58,6 +59,28 @@ extern bool g_panic_abort; /* Global flag marking whether the current ISR is a Task Watchdog ISR. */ bool g_twdt_isr = false; +#if CONFIG_ESP_TASK_WDT_INIT +ESP_PRE_APP_MAIN_HANDLER_REGISTER(init_task_wdt, 120) +{ + (void)user_arg; + (void)ctx; + esp_task_wdt_config_t config = { + .timeout_ms = CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000, + .idle_core_mask = 0, +#if CONFIG_ESP_TASK_WDT_PANIC + .trigger_panic = true, +#endif + }; +#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 + config.idle_core_mask |= BIT(0); +#endif +#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1 + config.idle_core_mask |= BIT(1); +#endif + return esp_task_wdt_init(&config); +} +#endif + // --------------------------------------------------- Definitions ----------------------------------------------------- // ---------------------- Typedefs ------------------------- diff --git a/components/freertos/CMakeLists.txt b/components/freertos/CMakeLists.txt index d9017f3c15d..8a3ef25eb0e 100644 --- a/components/freertos/CMakeLists.txt +++ b/components/freertos/CMakeLists.txt @@ -223,22 +223,12 @@ else() idf_component_optional_requires(PUBLIC esp_trace) endif() - if(CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME) - # [refactor-todo]: app_startup.c esp_startup_start_app_other_cores() calls esp_gdbstub_init() (called on CPU0). - # This should be resolved when link-time registration of startup functions is added. - idf_component_optional_requires(PRIVATE esp_gdbstub) - endif() - if(CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER) # [refactor-todo]: esp_timer is required by FreeRTOS when we use esp_timer_get_time() to do profiling # Introduce a port wrapper function to avoid including esp_timer.h into the public header idf_component_optional_requires(PUBLIC esp_timer) endif() - if(CONFIG_SPIRAM) - idf_component_optional_requires(PRIVATE esp_psram) - endif() - if(CONFIG_PM_TRACE) # esp_pm is required by port_systick.c for tracing idf_component_optional_requires(PRIVATE esp_pm) diff --git a/components/freertos/app_startup.c b/components/freertos/app_startup.c index 1391734f451..27f75c59041 100644 --- a/components/freertos/app_startup.c +++ b/components/freertos/app_startup.c @@ -12,20 +12,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/portmacro.h" -#include "esp_private/esp_int_wdt.h" -#include "esp_private/crosscore_int.h" -#include "esp_task_wdt.h" -#include "esp_freertos_hooks.h" -#include "esp_heap_caps_init.h" -#include "esp_chip_info.h" -#if CONFIG_SPIRAM -/* Required by esp_psram_extram_reserve_dma_pool() */ -#include "esp_psram.h" -#include "esp_private/esp_psram_extram.h" -#endif -#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME -#include "esp_gdbstub.h" /* Required by esp_gdbstub_init() */ -#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME +#include "esp_private/esp_sys_event_internal.h" /* ------------------------------------------------- App/OS Startup ---------------------------------------------------- * - Functions related to application and FreeRTOS startup @@ -56,26 +43,18 @@ CONFIG_FREERTOS_UNICORE and CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE should be identic static void main_task(void* args); ESP_LOG_ATTR_TAG(APP_START_TAG, "app_start"); +static void run_app_startup_event(esp_sys_event_id_t id) +{ + ESP_SYS_EVENT_FOREACH(handler, id) { + ESP_ERROR_CHECK(handler->handler(NULL, NULL)); + } +} + // ------------------ CPU0 App Startup --------------------- void esp_startup_start_app(void) { -#if CONFIG_ESP_INT_WDT - esp_int_wdt_init(); - // Initialize the interrupt watch dog for CPU0. - esp_int_wdt_cpu_init(); -#elif CONFIG_ESP32_ECO3_CACHE_LOCK_FIX - // If the INT WDT isn't enabled on ESP32 ECO3, issue an error regarding the cache lock bug - assert(!soc_has_cache_lock_bug() && "ESP32 Rev 3 + Dual Core + PSRAM requires INT WDT enabled in project config!"); -#endif - - // Initialize the cross-core interrupt on CPU0 - esp_crosscore_int_init(); - -#if CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME - void esp_gdbstub_init(void); - esp_gdbstub_init(); -#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME + run_app_startup_event(ESP_SYS_EVENT_PRE_SCHEDULER); BaseType_t res = xTaskCreatePinnedToCore(main_task, "main", ESP_TASK_MAIN_STACK, NULL, @@ -87,11 +66,21 @@ void esp_startup_start_app(void) If a particular FreeRTOS port has port/arch specific OS startup behavior, they can implement a function of type "void port_start_app_hook(void)" in their `port.c` files. This function will be called below, thus allowing each FreeRTOS port to implement port specific app startup behavior. + + Deprecated: Register a PRE_SCHEDULER handler with priority 999 to preserve this ordering. */ - void __attribute__((weak)) port_start_app_hook(void); + // TODO: IDF-16135 + void __attribute__((weak, deprecated("port_start_app_hook is deprecated and will be removed in IDF 7.0. Use ESP_PRE_SCHEDULER_HANDLER_REGISTER instead."))) + port_start_app_hook(void); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" if (port_start_app_hook != NULL) { + ESP_EARLY_LOGW(APP_START_TAG, + "port_start_app_hook is deprecated and will be removed in IDF 7.0. " + "Use ESP_PRE_SCHEDULER_HANDLER_REGISTER instead."); port_start_app_hook(); } +#pragma GCC diagnostic pop ESP_EARLY_LOGD(APP_START_TAG, "Starting scheduler on CPU0"); vTaskStartScheduler(); @@ -113,13 +102,7 @@ void esp_startup_start_app_other_cores(void) ; } -#if CONFIG_ESP_INT_WDT - // Initialize the interrupt watch dog for CPU1. - esp_int_wdt_cpu_init(); -#endif - - // Initialize the cross-core interrupt on CPU1 - esp_crosscore_int_init(); + run_app_startup_event(ESP_SYS_EVENT_PRE_SCHEDULER); ESP_EARLY_LOGD(APP_START_TAG, "Starting scheduler on CPU%d", xPortGetCoreID()); xPortStartScheduler(); @@ -154,54 +137,13 @@ static void wait_for_all_cores_ready(void) #endif // !CONFIG_FREERTOS_UNICORE } -// Reinitialize the startup stack heaps, so it can be used for heap allocation. -static void reclaim_startup_stack_memory_for_heap(void) -{ - wait_for_all_cores_ready(); - // [refactor-todo] check if there is a way to move the following block to esp_system startup - heap_caps_enable_nonos_stack_heaps(); - - // Now we have startup stack RAM available for heap, enable any DMA pool memory -#if CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL - if (esp_psram_is_initialized()) { - esp_err_t r = esp_psram_extram_reserve_dma_pool(CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL); - if (r != ESP_OK) { - ESP_LOGE(MAIN_TAG, "Could not reserve internal/DMA pool (error 0x%x)", r); - abort(); - } - } -#endif -} - static void main_task(void* args) { ESP_LOGI(MAIN_TAG, "Started on CPU%d", (int)xPortGetCoreID()); - reclaim_startup_stack_memory_for_heap(); + wait_for_all_cores_ready(); - // Initialize TWDT if configured to do so -#if CONFIG_ESP_TASK_WDT_INIT - esp_task_wdt_config_t twdt_config = { - .timeout_ms = CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000, - .idle_core_mask = 0, -#if CONFIG_ESP_TASK_WDT_PANIC - .trigger_panic = true, -#endif - }; -#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 - twdt_config.idle_core_mask |= (1 << 0); -#endif -#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1 - twdt_config.idle_core_mask |= (1 << 1); -#endif - ESP_ERROR_CHECK(esp_task_wdt_init(&twdt_config)); -#endif // CONFIG_ESP_TASK_WDT - - // app_update overrides it to auto-confirm an OTA rollback right before app_main. - void __attribute__((weak)) esp_ota_confirm_rollback_hook(void); - if (esp_ota_confirm_rollback_hook != NULL) { - esp_ota_confirm_rollback_hook(); - } + run_app_startup_event(ESP_SYS_EVENT_PRE_APP_MAIN); /* Note: Be careful when changing the "Calling app_main()" log below as multiple pytest scripts expect this log as a diff --git a/components/heap/heap_caps_init.c b/components/heap/heap_caps_init.c index f2d0f90eac1..74927293443 100644 --- a/components/heap/heap_caps_init.c +++ b/components/heap/heap_caps_init.c @@ -15,6 +15,7 @@ #include "esp_heap_task_info_internal.h" #include "heap_memory_layout.h" +#include "esp_private/esp_sys_event_app_init.h" #include "esp_private/startup_internal.h" static const char *TAG = "heap_init"; @@ -98,6 +99,14 @@ void heap_caps_enable_nonos_stack_heaps(void) s_in_startup = false; } +ESP_PRE_APP_MAIN_HANDLER_REGISTER(enable_nonos_stack_heaps, 100) +{ + (void)user_arg; + (void)ctx; + heap_caps_enable_nonos_stack_heaps(); + return ESP_OK; +} + /* Initialize the heap allocator to use all of the memory not used by static data or reserved for other purposes */ diff --git a/docs/en/migration-guides/release-6.x/6.2/system.rst b/docs/en/migration-guides/release-6.x/6.2/system.rst index 5d67420f0fb..9525e49d18f 100644 --- a/docs/en/migration-guides/release-6.x/6.2/system.rst +++ b/docs/en/migration-guides/release-6.x/6.2/system.rst @@ -3,6 +3,21 @@ System :link_to_translation:`zh_CN:[中文]` +FreeRTOS +--------- + +The legacy ``port_start_app_hook`` is deprecated and will be removed in ESP-IDF v7.0. To preserve the previous execution order, replace it with an ``ESP_PRE_SCHEDULER_HANDLER_REGISTER`` handler at priority ``999``. + +.. code-block:: c + + #include "esp_private/esp_sys_event_app_init.h" + + ESP_PRE_SCHEDULER_HANDLER_REGISTER(port_start_app_hook, 999) + { + // Port-specific startup behavior + return ESP_OK; + } + OTA Updates ----------- diff --git a/docs/zh_CN/migration-guides/release-6.x/6.2/system.rst b/docs/zh_CN/migration-guides/release-6.x/6.2/system.rst index 3f3527affab..2a088df33a9 100644 --- a/docs/zh_CN/migration-guides/release-6.x/6.2/system.rst +++ b/docs/zh_CN/migration-guides/release-6.x/6.2/system.rst @@ -3,6 +3,21 @@ :link_to_translation:`en:[English]` +FreeRTOS +-------- + +旧版 ``port_start_app_hook`` 已弃用,并将在 ESP-IDF v7.0 中移除。为保持原有执行顺序,请使用优先级为 ``999`` 的 ``ESP_PRE_SCHEDULER_HANDLER_REGISTER`` 处理函数替代。 + +.. code-block:: c + + #include "esp_private/esp_sys_event_app_init.h" + + ESP_PRE_SCHEDULER_HANDLER_REGISTER(port_start_app_hook, 999) + { + // 特定于端口的启动行为 + return ESP_OK; + } + OTA 更新 --------